Skip to content

circuit_builder

CircuitBuilder

A class using the builder pattern to make construction of circuits easy from Python. Adds corresponding instruction when a method is called. Checks that instructions are known and called with the right arguments. Mainly here to allow for Qiskit-style circuit construction:

Parameters:

Name Type Description Default
qubit_register_size int

Size of the qubit register

required
bit_register_size int

Size of the bit register

0
Example

CircuitBuilder(qubit_register_size=3, bit_register_size=3). H(0).CNOT(0, 1).CNOT(0, 2). to_circuit() version 3.0 qubit[3] q h q[0] cnot q[0], q[1] cnot q[0], q[2]

Source code in opensquirrel/circuit_builder.py
class CircuitBuilder:
    """
    A class using the builder pattern to make construction of circuits easy from Python.
    Adds corresponding instruction when a method is called. Checks that instructions are known and called with the right
    arguments.
    Mainly here to allow for Qiskit-style circuit construction:

    Args:
        qubit_register_size (int): Size of the qubit register
        bit_register_size (int): Size of the bit register

    Example:
        >>> CircuitBuilder(qubit_register_size=3, bit_register_size=3).\
        H(0).CNOT(0, 1).CNOT(0, 2).\
        to_circuit()
        version 3.0
        <BLANKLINE>
        qubit[3] q
        <BLANKLINE>
        h q[0]
        cnot q[0], q[1]
        cnot q[0], q[2]
        <BLANKLINE>
    """

    def __init__(self, qubit_register_size: int, bit_register_size: int = 0) -> None:
        self.register_manager = RegisterManager(QubitRegister(qubit_register_size), BitRegister(bit_register_size))
        self.ir = IR()

    def __getattr__(self, attr: Any) -> Callable[..., Self]:
        return partial(self._add_statement, attr)

    def _check_qubit_out_of_bounds_access(self, qubit: QubitLike) -> None:
        """Throw error if qubit index is outside the qubit register range.

        Args:
            qubit: qubit to check.
        """
        index = Qubit(qubit).index
        if index >= self.register_manager.get_qubit_register_size():
            msg = "qubit index is out of bounds"
            raise IndexError(msg)

    def _check_bit_out_of_bounds_access(self, bit: BitLike) -> None:
        """Throw error if bit index is outside the bit register range.

        Args:
            bit: bit to check.
        """
        index = Bit(bit).index
        if index >= self.register_manager.get_bit_register_size():
            msg = "bit index is out of bounds"
            raise IndexError(msg)

    def _check_out_of_bounds_access(self, instruction: Instruction) -> None:
        for qubit in instruction.get_qubit_operands():
            self._check_qubit_out_of_bounds_access(qubit)
        for bit in instruction.get_bit_operands():
            self._check_bit_out_of_bounds_access(bit)

    def _add_statement(self, attr: str, *args: Any) -> Self:
        if attr == "asm":
            try:
                asm_declaration = AsmDeclaration(*args)
                self.ir.add_asm_declaration(asm_declaration)
            except TypeError:
                msg = f"trying to build '{attr}' with the wrong number or type of arguments: '{args}'"
                raise TypeError(msg) from None
            return self

        if attr not in default_instruction_set:
            msg = f"unknown instruction '{attr}'"
            raise ValueError(msg)
        try:
            instruction = default_instruction_set[attr](*args)
        except TypeError as e:
            msg = f"trying to build {attr!r} with the wrong number or type of arguments: {args!r}: {e}"
            raise TypeError(msg) from e

        self._check_out_of_bounds_access(instruction)

        self.ir.add_statement(instruction)
        return self

    def to_circuit(self) -> Circuit:
        return Circuit(deepcopy(self.register_manager), deepcopy(self.ir))