CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cirq

A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

protocols.mddocs/

Protocols and Extensibility

The protocols module provides Cirq's extensible protocol system based on structural subtyping. Protocols define interfaces that allow custom classes to integrate seamlessly with Cirq's quantum operations and enable polymorphic behavior without inheritance.

Protocol System Overview

Cirq uses protocols (similar to type classes or traits) to provide extensible behavior for quantum objects. Any class can implement protocol methods to participate in Cirq's ecosystem.

Core Protocol Functions

Unitary Protocols

Protocols for working with unitary matrices and operations.

def has_unitary(val: Any) -> bool:
    """Check if object has a unitary matrix representation."""
    
def unitary(val: Any) -> np.ndarray:
    """Get the unitary matrix of a quantum operation.
    
    Args:
        val: Object that may have a unitary representation
        
    Returns:
        Unitary matrix as numpy array
        
    Raises:
        TypeError: If val doesn't have a unitary representation
    """
    
def apply_unitary(val: Any, args: 'ApplyUnitaryArgs') -> Union[np.ndarray, None]:
    """Apply unitary effect to a state vector or density matrix.
    
    Args:
        val: Object with unitary effect
        args: Arguments specifying target state and workspace
        
    Returns:
        Modified state array, or None if not implemented
    """
    
def apply_unitaries(vals: Iterable[Any], qubits: Sequence['cirq.Qid'], 
                   args: 'ApplyUnitaryArgs') -> Union[np.ndarray, None]:
    """Apply multiple unitaries in sequence."""

Channel Protocols

Protocols for quantum channels and noise operations.

def has_kraus(val: Any) -> bool:
    """Check if object has Kraus representation."""
    
def kraus(val: Any) -> Tuple[np.ndarray, ...]:
    """Get Kraus operators of a quantum channel.
    
    Returns:
        Tuple of Kraus operator matrices
    """
    
def apply_channel(val: Any, args: 'ApplyChannelArgs') -> Union[np.ndarray, None]:
    """Apply quantum channel to a state.
    
    Args:
        val: Object representing quantum channel
        args: Arguments specifying target state and workspace
        
    Returns:
        Modified state array, or None if not implemented
    """
    
def has_mixture(val: Any) -> bool:
    """Check if object has mixture representation."""
    
def mixture(val: Any) -> Tuple[Tuple[float, Any], ...]:
    """Get mixture representation as probability-unitary pairs."""
    
def apply_mixture(val: Any, args: 'ApplyMixtureArgs') -> Union[np.ndarray, None]:
    """Apply mixture representation to a state."""

Decomposition Protocols

Protocols for decomposing operations into simpler components.

def decompose(val: Any, *, intercepting_decomposer: Callable = None,
             fallback_decomposer: Callable = None,
             keep: Callable[[Any], bool] = None,
             on_stuck_raise: Union[Exception, Callable[[], Exception]] = None) -> List[Any]:
    """Recursively decompose a value into simpler operations.
    
    Args:
        val: Object to decompose
        intercepting_decomposer: Function called before default decomposition
        fallback_decomposer: Function called if default decomposition fails
        keep: Predicate for operations that should not be decomposed further
        on_stuck_raise: Exception to raise if decomposition gets stuck
        
    Returns:
        List of decomposed operations
    """
    
def decompose_once(val: Any, *, default: Any = None) -> Union[List[Any], Any]:
    """Decompose value one level.
    
    Args:
        val: Object to decompose
        default: Value to return if decomposition fails
        
    Returns:
        List of operations from one level of decomposition, or default
    """
    
def decompose_once_with_qubits(val: Any, qubits: Sequence['cirq.Qid'], 
                              *, default: Any = None) -> Union[List[Any], Any]:
    """Decompose with explicit qubit specification."""

Comparison and Equality Protocols

def approx_eq(val: Any, other: Any, *, atol: Union[int, float] = 1e-8) -> bool:
    """Check approximate equality with tolerance.
    
    Args:
        val: First value to compare
        other: Second value to compare  
        atol: Absolute tolerance for comparison
        
    Returns:
        True if values are approximately equal
    """
    
def equal_up_to_global_phase(val: Any, other: Any, *, atol: float = 1e-8) -> bool:
    """Check equality up to global phase factor."""
    
def commutes(val1: Any, val2: Any, *, atol: float = 1e-8) -> Union[bool, None]:
    """Check if two operations commute.
    
    Returns:
        True if they commute, False if they don't, None if unknown
    """
    
def definitely_commutes(val1: Any, val2: Any, *, atol: float = 1e-8) -> bool:
    """Check if operations definitely commute (conservative check)."""

Measurement Protocols

Protocols for measurement operations and keys.

def is_measurement(val: Any) -> bool:
    """Check if object represents a measurement operation."""
    
def measurement_key_name(val: Any) -> Optional[str]:
    """Get measurement key name, if any."""
    
def measurement_key_obj(val: Any) -> Optional['cirq.MeasurementKey']:
    """Get measurement key object, if any."""
    
def measurement_key_names(val: Any) -> AbstractSet[str]:
    """Get all measurement key names used by an object."""
    
def measurement_key_objs(val: Any) -> AbstractSet['cirq.MeasurementKey']:
    """Get all measurement key objects used by an object."""
    
def measurement_keys_touched(val: Any) -> FrozenSet['cirq.MeasurementKey']:
    """Get measurement keys read or written by an operation."""

Parameterization Protocols

Protocols for parameterized operations.

def is_parameterized(val: Any) -> bool:
    """Check if object contains symbolic parameters."""
    
def parameter_names(val: Any) -> AbstractSet[str]:
    """Get names of symbolic parameters."""
    
def parameter_symbols(val: Any) -> AbstractSet[sympy.Symbol]:
    """Get symbolic parameter symbols."""
    
def resolve_parameters(val: Any, resolver: 'cirq.ParamResolverOrSimilarType', 
                      recursive: bool = True) -> Any:
    """Resolve symbolic parameters to concrete values.
    
    Args:
        val: Object with parameters to resolve
        resolver: Maps parameter names/symbols to values
        recursive: Whether to resolve parameters recursively
        
    Returns:
        Object with parameters resolved
    """
    
def resolve_parameters_once(val: Any, resolver: 'cirq.ParamResolverOrSimilarType') -> Any:
    """Resolve parameters one level (non-recursive)."""

Shape and Size Protocols

def num_qubits(val: Any) -> int:
    """Get number of qubits operated on by a gate or operation."""
    
def qid_shape(val: Any) -> Tuple[int, ...]:
    """Get shape tuple describing qid dimensions.
    
    Returns:
        Tuple where each element is the dimension of the corresponding qid
    """

Inverse and Power Protocols

def inverse(val: Any, default: Any = None) -> Any:
    """Get inverse of an operation.
    
    Args:
        val: Operation to invert
        default: Value to return if inversion not possible
        
    Returns:
        Inverse operation, or default if not invertible
    """
    
def pow(val: Any, exponent: Any, default: Any = None) -> Any:
    """Raise operation to a power.
    
    Args:
        val: Operation to exponentiate
        exponent: Power to raise to
        default: Value to return if exponentiation not supported
        
    Returns:
        Operation raised to given power
    """

Phase and Multiplication Protocols

def phase_by(val: Any, phase_turns: float, qubit_index: int) -> Any:
    """Phase operation by rotating a qubit's eigenspace.
    
    Args:
        val: Operation to phase
        phase_turns: Phase in units of full turns (2π radians)
        qubit_index: Index of qubit to phase
        
    Returns:
        Phased operation
    """
    
def mul(lhs: Any, rhs: Any, default: Any = None) -> Any:
    """Multiply/compose two operations."""

Circuit Diagram Protocol

def circuit_diagram_info(val: Any, *, args: 'CircuitDiagramInfoArgs' = None) -> 'CircuitDiagramInfo':
    """Get information for drawing operation in circuit diagrams.
    
    Args:
        val: Operation to get diagram info for
        args: Arguments affecting diagram generation
        
    Returns:
        Circuit diagram information
    """

QASM Generation Protocol

def qasm(val: Any, *, args: 'QasmArgs' = None, default: Any = None) -> Union[str, Any]:
    """Generate QASM code for an operation.
    
    Args:
        val: Operation to convert to QASM
        args: QASM generation arguments
        default: Value to return if QASM generation fails
        
    Returns:
        QASM string representation
    """

Other Protocols

def act_on(val: Any, args: 'cirq.ActOnArgs', allow_decompose: bool = True) -> bool:
    """Apply operation effect to a simulation state.
    
    Returns:
        True if operation was applied successfully, False otherwise
    """
    
def pauli_expansion(val: Any) -> Dict['cirq.PauliString', complex]:
    """Get Pauli expansion of an operation."""
    
def has_stabilizer_effect(val: Any) -> Optional[bool]:
    """Check if operation has stabilizer effect (preserves stabilizer states)."""
    
def control_keys(val: Any) -> FrozenSet['cirq.MeasurementKey']:
    """Get measurement keys that control this operation."""
    
def trace_distance_bound(val: Any) -> Optional[float]:
    """Upper bound on trace distance from perfect operation."""

Protocol Support Classes

Apply Unitary Support

class ApplyUnitaryArgs:
    """Arguments for apply_unitary protocol method."""
    
    def __init__(self, target_tensor: np.ndarray, 
                 available_buffer: np.ndarray,
                 axes: Sequence[int],
                 default: Any = None) -> None:
        """Initialize apply unitary arguments.
        
        Args:
            target_tensor: State tensor to modify
            available_buffer: Workspace tensor of same shape
            axes: Tensor axes corresponding to qubits
            default: Default value to return if application fails
        """
        
    @property
    def target_tensor(self) -> np.ndarray:
        """Target tensor to apply unitary to."""
        
    @property
    def available_buffer(self) -> np.ndarray:
        """Workspace buffer tensor."""
        
    @property
    def axes(self) -> Tuple[int, ...]:
        """Axes in tensor corresponding to qubits."""
        
    def subspace_view(self, little_endian_bits_int: int = 0) -> np.ndarray:
        """Get view of a computational basis subspace."""
        
    def swap_target_tensor_for(self, new_target_tensor: np.ndarray) -> 'ApplyUnitaryArgs':
        """Return args with different target tensor."""

class SupportsConsistentApplyUnitary:
    """Marker interface for consistent unitary application."""

class SupportsExplicitHasUnitary:
    """Marker interface for explicit unitary support."""

Apply Channel Support

class ApplyChannelArgs:
    """Arguments for apply_channel protocol method."""
    
    def __init__(self, target_tensor: np.ndarray,
                 out_buffer: np.ndarray, 
                 auxiliary_buffer0: np.ndarray,
                 auxiliary_buffer1: np.ndarray,
                 left_axes: Sequence[int],
                 right_axes: Sequence[int]) -> None:
        """Initialize apply channel arguments."""

class ApplyMixtureArgs:
    """Arguments for apply_mixture protocol method."""
    
    def __init__(self, target_tensor: np.ndarray,
                 available_buffer: np.ndarray,
                 axes: Sequence[int]) -> None:
        """Initialize apply mixture arguments."""

Circuit Diagram Support

class CircuitDiagramInfo:
    """Information for drawing operations in circuit diagrams."""
    
    def __init__(self, wire_symbols: Tuple[str, ...],
                 exponent: Any = 1,
                 connected: bool = True,
                 exponent_qubit_index: Optional[int] = None,
                 auto_exponent_parens: bool = True) -> None:
        """Initialize circuit diagram info.
        
        Args:
            wire_symbols: Symbols to show on each wire
            exponent: Exponent to display (for gates like X^0.5)
            connected: Whether to connect symbols with lines
            exponent_qubit_index: Which wire to show exponent on
            auto_exponent_parens: Whether to add parentheses around exponent
        """
        
    @property
    def wire_symbols(self) -> Tuple[str, ...]:
        """Symbols for each wire."""
        
    def with_wire_symbols(self, wire_symbols: Sequence[str]) -> 'CircuitDiagramInfo':
        """Return info with different wire symbols."""

class CircuitDiagramInfoArgs:
    """Arguments for circuit diagram info generation."""
    
    def __init__(self, known_qubits: Optional[Iterable['cirq.Qid']] = None,
                 known_qubit_count: Optional[int] = None,
                 use_unicode_characters: bool = True,
                 precision: Optional[int] = None,
                 label_map: Optional[Dict[str, str]] = None) -> None:
        """Initialize circuit diagram args."""

class LabelEntity:
    """Entity for labeling in circuit diagrams."""
    
    def __init__(self, label: str) -> None:
        """Initialize label entity."""

Decomposition Context

class DecompositionContext:
    """Context for operation decomposition."""
    
    def __init__(self, qubit_manager: Optional['cirq.QubitManager'] = None) -> None:
        """Initialize decomposition context."""
        
    @property
    def qubit_manager(self) -> Optional['cirq.QubitManager']:
        """Qubit manager for allocating ancillas during decomposition."""

QASM Support

class QasmArgs:
    """Arguments for QASM generation."""
    
    def __init__(self, precision: int = 10,
                 version: str = '2.0',
                 qubit_id_map: Optional[Dict['cirq.Qid', str]] = None,
                 meas_key_id_map: Optional[Dict[str, str]] = None) -> None:
        """Initialize QASM arguments."""
        
    def format_field(self, value: Any) -> str:
        """Format a field for QASM output."""
        
    def validate_version(self, supported_versions: Sequence[str]) -> str:
        """Validate QASM version."""

JSON Serialization Support

JSON Protocol Classes

class HasJSONNamespace:
    """Interface for objects with JSON namespace."""
    
    @property
    @abc.abstractmethod
    def _json_namespace_(self) -> str:
        """JSON namespace for serialization."""

class JsonResolver:
    """Resolver for JSON object deserialization."""
    
    def __init__(self, cirq_type_to_resolver: Dict[str, Callable] = None) -> None:
        """Initialize JSON resolver."""
        
    def resolve(self, cirq_type: str, obj_dict: Dict[str, Any]) -> Any:
        """Resolve JSON object to Python object."""

class SerializableByKey:
    """Interface for objects serializable by key."""
    
    @property
    @abc.abstractmethod  
    def _serialization_key_(self) -> str:
        """Key used for serialization."""

DEFAULT_RESOLVERS: List[JsonResolver]
"""Default JSON resolvers for Cirq objects."""

JSON Utility Functions

def to_json(obj: Any) -> str:
    """Convert object to JSON string."""
    
def read_json(file_or_fn: Union[str, pathlib.Path, IO], 
             resolvers: Sequence[JsonResolver] = None) -> Any:
    """Read object from JSON file."""
    
def to_json_gzip(obj: Any) -> bytes:
    """Convert object to gzip-compressed JSON."""
    
def read_json_gzip(file_or_fn: Union[str, pathlib.Path, IO],
                  resolvers: Sequence[JsonResolver] = None) -> Any:
    """Read object from gzip-compressed JSON file."""
    
def cirq_type_from_json(json_dict: Dict[str, Any], 
                       resolvers: Sequence[JsonResolver] = None) -> Any:
    """Convert JSON dictionary to Cirq object."""
    
def obj_to_dict_helper(obj: Any, attribute_names: Iterable[str], 
                      namespace: Optional[str] = None) -> Dict[str, Any]:
    """Helper for converting objects to dictionaries."""
    
def dataclass_json_dict(obj: Any) -> Dict[str, Any]:
    """Convert dataclass to JSON dictionary."""

def json_cirq_type(cls: type) -> type:
    """Decorator to register class for JSON serialization."""
    
def json_namespace(namespace: str) -> Callable[[type], type]:
    """Decorator to set JSON namespace for a class."""

Protocol Support Interfaces

All protocol support interfaces follow the naming pattern Supports*:

class SupportsActOn:
    """Supports acting on simulation states."""

class SupportsActOnQubits:
    """Supports acting on qubits in simulation."""
    
class SupportsApplyChannel:
    """Supports applying quantum channels."""
    
class SupportsApplyMixture: 
    """Supports applying mixture representations."""
    
class SupportsApproximateEquality:
    """Supports approximate equality comparison."""
    
class SupportsCircuitDiagramInfo:
    """Supports circuit diagram generation."""
    
class SupportsCommutes:
    """Supports commutativity checking."""
    
class SupportsControlKey:
    """Supports control keys for conditional operations."""
    
class SupportsDecompose:
    """Supports operation decomposition."""
    
class SupportsDecomposeWithQubits:
    """Supports decomposition with explicit qubits."""
    
class SupportsEqualUpToGlobalPhase:
    """Supports equality up to global phase."""
    
class SupportsExplicitQidShape:
    """Supports explicit qid shape specification."""
    
class SupportsExplicitNumQubits:
    """Supports explicit qubit count specification."""
    
class SupportsJSON:
    """Supports JSON serialization."""
    
class SupportsKraus:
    """Supports Kraus operator representation."""
    
class SupportsMeasurementKey:
    """Supports measurement keys."""
    
class SupportsMixture:
    """Supports mixture representation."""
    
class SupportsParameterization:
    """Supports symbolic parameterization."""
    
class SupportsPauliExpansion:
    """Supports Pauli operator expansion."""
    
class SupportsPhase:
    """Supports phase operations."""
    
class SupportsQasm:
    """Supports QASM generation."""
    
class SupportsQasmWithArgs:
    """Supports QASM generation with arguments."""
    
class SupportsQasmWithArgsAndQubits:
    """Supports QASM generation with arguments and qubits."""
    
class SupportsTraceDistanceBound:
    """Supports trace distance bounds."""
    
class SupportsUnitary:
    """Supports unitary matrix representation."""

Measurement Key Utilities

def with_key_path(operation: 'cirq.Operation', path: Tuple[str, ...]) -> 'cirq.Operation':
    """Add key path to measurement operation."""
    
def with_key_path_prefix(operation: 'cirq.Operation', prefix: Tuple[str, ...]) -> 'cirq.Operation':
    """Add key path prefix to measurement operation."""
    
def with_measurement_key_mapping(operation: 'cirq.Operation', 
                                key_map: Dict[str, str]) -> 'cirq.Operation':
    """Apply measurement key mapping to operation."""
    
def with_rescoped_keys(operation: 'cirq.Operation', 
                      path: Tuple[str, ...]) -> 'cirq.Operation':
    """Rescope measurement keys with path."""

Validation Functions

def validate_mixture(mixture: Sequence[Tuple[float, Any]]) -> None:
    """Validate mixture representation (probabilities sum to 1)."""
    
def trace_distance_from_angle_list(angles: Sequence[float]) -> float:
    """Calculate trace distance bound from rotation angles."""

Usage Examples

Implementing Protocol Methods

import cirq
import numpy as np

class MyGate(cirq.Gate):
    """Custom gate implementing multiple protocols."""
    
    def __init__(self, theta: float):
        self.theta = theta
        
    def _num_qubits_(self) -> int:
        return 1
        
    # Unitary protocol
    def _unitary_(self) -> np.ndarray:
        """Return unitary matrix."""
        c, s = np.cos(self.theta/2), np.sin(self.theta/2)
        return np.array([[c, -1j*s], [-1j*s, c]])
        
    # Decomposition protocol  
    def _decompose_(self, qubits):
        """Decompose into basic gates."""
        return [cirq.rx(self.theta)(qubits[0])]
        
    # Circuit diagram protocol
    def _circuit_diagram_info_(self, args):
        """Circuit diagram representation."""
        return cirq.CircuitDiagramInfo(
            wire_symbols=(f"MyGate({self.theta:.2f})",)
        )
        
    # Parameterization protocol
    def _is_parameterized_(self) -> bool:
        """Check if gate has symbolic parameters."""
        return isinstance(self.theta, sympy.Symbol)
        
    # Approximate equality protocol
    def _approx_eq_(self, other, atol):
        """Check approximate equality."""
        return (isinstance(other, MyGate) and 
                abs(self.theta - other.theta) <= atol)

# Use the custom gate
gate = MyGate(np.pi/4)
q = cirq.LineQubit(0)

# Protocol functions work automatically
print(f"Has unitary: {cirq.has_unitary(gate)}")
print(f"Unitary matrix:\n{cirq.unitary(gate)}")
print(f"Decomposition: {cirq.decompose(gate(q))}")

Using Protocol Functions

import cirq
import numpy as np

# Create various operations
h_gate = cirq.H
x_gate = cirq.X  
noise = cirq.depolarize(0.1)
measurement = cirq.measure(cirq.LineQubit(0), key='result')

# Check what protocols they support
operations = [h_gate, x_gate, noise, measurement]

for op in operations:
    print(f"\nOperation: {op}")
    print(f"  Has unitary: {cirq.has_unitary(op)}")
    print(f"  Has Kraus: {cirq.has_kraus(op)}")
    print(f"  Is measurement: {cirq.is_measurement(op)}")
    print(f"  Is parameterized: {cirq.is_parameterized(op)}")
    
    # Get protocol information when available
    if cirq.has_unitary(op):
        print(f"  Unitary shape: {cirq.unitary(op).shape}")
    if cirq.is_measurement(op):
        print(f"  Measurement key: {cirq.measurement_key_name(op)}")

Parameter Resolution

import cirq
import sympy

# Create parameterized circuit
theta, phi = sympy.symbols('theta phi')
qubits = cirq.LineQubit.range(2)

circuit = cirq.Circuit([
    cirq.rx(theta)(qubits[0]),
    cirq.ry(phi)(qubits[1]), 
    cirq.CNOT(qubits[0], qubits[1])
])

print(f"Is parameterized: {cirq.is_parameterized(circuit)}")
print(f"Parameters: {cirq.parameter_names(circuit)}")

# Resolve parameters
resolver = cirq.ParamResolver({'theta': np.pi/4, 'phi': np.pi/6})
resolved_circuit = cirq.resolve_parameters(circuit, resolver)

print(f"After resolution: {cirq.is_parameterized(resolved_circuit)}")

This comprehensive protocol system enables Cirq's extensible architecture and allows custom quantum objects to integrate seamlessly with the framework.

Install with Tessl CLI

npx tessl i tessl/pypi-cirq

docs

circuits.md

devices.md

index.md

linalg.md

ops.md

protocols.md

qis.md

remaining-modules.md

sim.md

study.md

transformers.md

tile.json