Circuits

ParityOS provides a framework for representing quantum circuits as ordered sequences of Operator instances. Circuits serve as the primary container for composing quantum algorithms, supporting both mutable construction and immutable finalization.

Architecture Hierarchy

The following is a coarse overview of the inheritance hierarchy of classes describing circuits:

CircuitLike (Interface for all circuit-like operations)
├── Circuit (Mutable circuit, built up by successively adding operators)
│   └── operator_sequence: list[Operator]
└── FrozenCircuit (Immutable, hashable circuit)
    └── operator_sequence: tuple[Operator]

Both Circuit and FrozenCircuit inherit from CircuitLike, implementing the Sequence protocol to allow indexing and iteration over their operators.

Circuit Properties

Certain circuit properties can be checked via methods or by inspecting the aggregated operators.

Sequence and Ordering

Circuits are strictly ordered sequences. Two circuits are considered equal only if they contain the exact same operators in the exact same order. Equivalent circuits (e.g. circuits that only differ by permutations of commuting operators) are not considered equal.

from parityos.bits import Qubit
from parityos.operators.circuit import Circuit
from parityos.operators.controlled_operator import CNOT
from parityos.operators.elementary_operator import X

q0, q1 = Qubit(0), Qubit(1)

c1 = Circuit([X(q1), X(q0), CNOT(q0, q1)])
c2 = Circuit([X(q0), X(q1), CNOT(q0, q1)])

assert c1 != c2  # Order matters, even if operators commute

Hermiticity

A circuit’s Hermiticity is determined by its constituent operators. You can check if a circuit is Hermitian by accessing the is_hermitian property or verifying if it is an instance of a Hermitian-compatible interface (though the primary check is the boolean property).

from parityos.bits import Qubit
from parityos.operators.circuit import Circuit
from parityos.operators.elementary_operator import S, X

qubit = Qubit(0)

# X is Hermitian
c_x = Circuit([X(qubit)])
assert c_x.is_hermitian

# S is not Hermitian
c_s = Circuit([S(qubit)])
assert not c_s.is_hermitian

# Mixed circuit
c_mixed = Circuit([X(qubit), S(qubit)])
assert not c_mixed.is_hermitian

Qubits and Classical Bits

Circuits automatically aggregate the qubits and classical bits (cbits) used by their operators and implement the HasFrozenQubits and HasFrozenCBits interfaces:

  • qubits: Returns a frozenset of all unique qubits.

  • ordered_qubits: Returns a sorted tuple of the above unique qubit set.

  • cbits: Returns a frozenset of all classical bits (only for operators that support them, e.g., measurements).

  • ordered_cbits: Returns a sorted tuple of the above unique classical bit set.

Circuit Classes

Mutable Circuits

Circuit is a mutable class designed for building quantum algorithms incrementally. It maintains an internal list of operators that can be modified after instantiation.

Construction:

from parityos.bits import Qubit
from parityos.operators.circuit import Circuit
from parityos.operators.elementary_operator import H, X

q0, q1 = Qubit(0), Qubit(1)

# Initialize with a list
c = Circuit([X(q0), H(q1)])

Modification: As a mutable sequence, Circuit supports standard list operations:

Mutable only methods

  • measure_all: Appends MZ (Z-measurement) operators for all qubits in the circuit. It automatically generates new classical bits with unique IDs, skipping any IDs already in use.

  • freeze: Converts the mutable Circuit into an immutable FrozenCircuit, e.g.

frozen_c = c.freeze()

Immutable Circuits

FrozenCircuit is an immutable, hashable representation of a circuit. Once created, its operator sequence cannot be changed.

Construction: Can be created by using freeze <Circuit.freeze> on a Circuit or instantiated directly. Likewise, a Circuit can be created from a FrozenCircuit using unfreeze

Circuit Addition

Appends the operators of one circuit to another using the + operator.

from parityos.bits import Cbit, Qubit
from parityos.operators import Circuit
from parityos.operators.conditional_operator import ConditionalOperator
from parityos.operators.controlled_operator import CX
from parityos.operators.elementary_operator import H, X, Z
from parityos.operators.measurement import MZ

q0, q1, qpsi = Qubit(0), Qubit(1), Qubit("psi")
c0, c1 = Cbit(0), Cbit(1)
# bell state
circuit1 = Circuit([H(q0), CX(q0, q1)])
# teleportation
circuit2 = Circuit([CX(qpsi, q0), H(qpsi), MZ(qpsi, c1), MZ(q0, c0)])
# classical correction
circuit3 = Circuit([ConditionalOperator(c0, X(q1)), ConditionalOperator(c1, Z(q1))])

circuit = circuit1 + circuit2 + circuit3
assert circuit.qubits == {q0, q1, qpsi}
assert circuit.cbits == {c0, c1}

If at least one of the circuits is frozen, the resulting circuit will be a FrozenCircuit, otherwise it will be a Circuit.

Scalar Circuit Multiplication

The multiplication operator * facilitates the concatenation of a circuit sequence to itself n times. This is similar in spirit to the scalar multiplication of lists.

from parityos.bits import Qubit
from parityos.operators import Circuit
from parityos.operators.controlled_operator import CX
from parityos.operators.elementary_operator import H
from parityos.operators.rotation_operator import RX

q0, q1 = Qubit(0), Qubit(1)

circuit = Circuit([H(q0), CX(q0, q1), RX(q1)])

repeated_circuit = circuit * 3
assert repeated_circuit == 3 * circuit

Warning

Only multiplication of Circuits with scalars are supported, multiplications between circuits are not.