A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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.
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."""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."""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."""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)."""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."""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)."""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
"""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
"""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."""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
"""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
"""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."""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."""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."""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."""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."""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."""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."""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."""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."""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."""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."""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))}")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)}")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