Skip to content

1. Backends

graphiq.backends.compiler_base.CompilerBase

Bases: ABC

Base class for compiler implementations. In general, compilers compile circuits using a specific representation(s) for the underlying quantum state

Source code in graphiq/backends/compiler_base.py
class CompilerBase(ABC):
    """
    Base class for compiler implementations.
    In general, compilers compile circuits using a specific representation(s) for the underlying quantum state
    """

    name = "base"
    ops = [ops.OperationBase]  # the accepted operations for a given compiler

    def __init__(self):
        """
        Initializes CompilerBase fields

        :return: function returns nothing
        :rtype: None
        """
        self._measurement_determinism = "probabilistic"
        self._noise_simulation = False
        self._monte_carlo = False

    @property
    def noise_simulation(self):
        """
        Flag to run circuit simulation using noise or not.

        :return: boolean flag to run noise
        :rtype: boolean
        """
        return self._noise_simulation

    @noise_simulation.setter
    def noise_simulation(self, value):
        """
        Set the flag for running noise.

        :param value: True or False
        :return:
        """
        if isinstance(value, bool):
            self._noise_simulation = value
        else:
            raise ValueError("Noise simulation should be True or False.")

    def compile(self, circuit: CircuitBase, initial_state=None):
        """
        Compiles (i.e. produces an output state) circuit, in the appropriate representation.
        This involves sequentially applying each operation of the circuit on the initial state

        :param circuit: the circuit to compile
        :type circuit: CircuitBase
        :raises ValueError: if there is a circuit Operation which is incompatible with this compiler
        :return: the state produced by the circuit
        :rtype: QuantumState
        """
        # TODO: make this more general, but for now we assume all registers are initialized to |0>

        if initial_state:
            assert isinstance(
                initial_state, QuantumState
            ), "the initial state must be a valid QuantumState object"
            assert (
                initial_state.n_qubits == circuit.n_quantum
            ), "the number of qubits in initial state must match the circuit"
            state_data = initial_state.rep_data.data
        else:
            state_data = circuit.n_quantum

        state = QuantumState(
            data=state_data,  # initialize to |0...0> state
            rep_type=self.__class__.name,
            mixed=True if (self._noise_simulation and not self._monte_carlo) else False,
        )

        classical_registers = np.zeros(circuit.n_classical)

        # TODO: support self-defined mapping functions later instead of using the default above
        # Get functions which will map from registers to a unique index
        q_index = CompilerBase.reg_to_index_func(circuit.n_photons)

        # the unwrapping allows us to support Wrapper operation types
        seq = circuit.sequence(unwrapped=True)

        for op in seq:
            if type(op) not in self.ops:
                raise RuntimeError(
                    f"The Operation class {op.__class__.__name__} is not valid with "
                    f"the {self.__class__.__name__} compiler"
                )

            no_noise = not self._noise_simulation
            is_controlled_op = isinstance(
                op, ops.ControlledPairOperationBase
            ) or isinstance(op, ops.ClassicalControlledPairOperationBase)

            if is_controlled_op:
                no_noise = (
                    isinstance(op.noise[0], nm.NoNoise)
                    and isinstance(op.noise[1], nm.NoNoise)
                ) or no_noise
            else:
                no_noise = no_noise or isinstance(op.noise, nm.NoNoise)

            if no_noise:
                self.compile_one_gate(
                    state, op, circuit.n_quantum, q_index, classical_registers
                )

            else:
                if is_controlled_op:
                    if isinstance(op.noise[0], nm.AdditionNoiseBase) and isinstance(
                        op.noise[1], nm.AdditionNoiseBase
                    ):
                        after_control = op.noise[0].noise_parameters["After gate"]
                        after_target = op.noise[1].noise_parameters["After gate"]
                        if after_control and after_target:
                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                        elif (not after_control) and (not after_target):
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )
                        elif after_control and (not after_target):
                            noise_copy = op.noise
                            tmp_noise = [nm.NoNoise, op.noise[1]]
                            op.noise = tmp_noise
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )

                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )
                            tmp_noise = [op.noise[0], nm.NoNoise]

                            op.noise = tmp_noise
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                            op.noise = noise_copy
                        else:
                            noise_copy = op.noise
                            tmp_noise = [op.noise[0], nm.NoNoise]
                            op.noise = tmp_noise
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )

                            tmp_noise = [nm.NoNoise, op.noise[1]]
                            op.noise = tmp_noise
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                            op.noise = noise_copy
                    else:
                        raise ValueError(
                            "We currently do not support different noise positions for one controlled gate."
                        )

                else:
                    if isinstance(op.noise, nm.AdditionNoiseBase):
                        if op.noise.noise_parameters["After gate"]:
                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )

                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                        else:
                            self._apply_additional_noise(
                                state, op, circuit.n_quantum, q_index
                            )
                            self.compile_one_gate(
                                state,
                                op,
                                circuit.n_quantum,
                                q_index,
                                classical_registers,
                            )
                    elif isinstance(op.noise, nm.ReplacementNoiseBase):
                        self.compile_one_noisy_gate(
                            state, op, circuit.n_quantum, q_index, classical_registers
                        )
                    else:
                        raise ValueError("Noise position is not acceptable.")

        return state

    def validate_ops(self, circuit):
        """
        Verifies that all operations of a circuit are valid for the selected compiler

        :param circuit: the circuit for which we are assessing validity of operations with this compiler
        :type circuit: CircuitBase (or some subclass of it)
        :return: True if all operations are valid, False otherwise
        :rtype: bool
        """
        seq = circuit.sequence()
        valid = True

        for i, op in enumerate(seq):
            if type(op) in self.ops:
                logging.info(
                    f"Operation {i} {type(op).__name__} is valid with {type(self).__name__}"
                )
            else:
                logging.error(
                    f"Error: Operation {i} {type(op).__name__} is not valid with {type(self).__name__}"
                )
                valid = False

        return valid

    @property
    def measurement_determinism(self):
        """
        Returns the measurement determinism (either it's probabilistic, defaults to 0, or defaults to 1)

        :return: determinism setting
        :rtype: str or int
        """
        return self._measurement_determinism

    @measurement_determinism.setter
    def measurement_determinism(self, measurement_setting):
        """
        Sets the measurement setting with which the compiler simulates a circuit
        (this can be set to "probabilistic", 1, 0)

        :param measurement_setting: if "probabilistic", measurement results are probabilistically selected
                                    if 1, measurement results default to 1 unless the probability of measuring p(1) = 0
                                    if 0, measurement results default to 0 unless the probability of measuring p(0) = 0
        :type measurement_setting: str or int
        :return: nothing
        :rtype: None
        """
        if measurement_setting in ["probabilistic", 1, 0]:
            self._measurement_determinism = measurement_setting
        else:
            raise ValueError(
                'Measurement determinism can only be set to "probabilistic", 0, or 1'
            )

    @staticmethod
    def reg_to_index_func(n_photon):
        """
        Given the number of photonic qubits in the circuit, this returns a function which will match a
        given register number and type (i.e. photonic or emitter) to a matrix index

        :param n_photon: the number of photonic qubits in the system being simulated
        :type n_photon: int
        :return: a function which will map register + register type to a matrix index (zero-indexed)
        :rtype: function
        """

        def reg_to_index(reg, reg_type):
            if reg_type == "p":
                return reg
            elif reg_type == "e":
                return reg + n_photon

        return reg_to_index

    @abstractmethod
    def compile_one_gate(self, state, op, n_quantum, q_index, classical_registers):
        raise NotImplementedError("Please select a valid compiler")

    @abstractmethod
    def compile_one_noisy_gate(
        self, state, op, n_quantum, q_index, classical_registers
    ):
        raise NotImplementedError("Please select a valid compiler")

    @abstractmethod
    def _apply_additional_noise(self, op, n_quantum, q_index):
        raise NotImplementedError("Please select a valid compiler")

measurement_determinism property writable

Returns the measurement determinism (either it's probabilistic, defaults to 0, or defaults to 1)

Returns:

Type Description
str | int

determinism setting

noise_simulation property writable

Flag to run circuit simulation using noise or not.

Returns:

Type Description
boolean

boolean flag to run noise

__init__()

Initializes CompilerBase fields

Returns:

Type Description
None

function returns nothing

Source code in graphiq/backends/compiler_base.py
def __init__(self):
    """
    Initializes CompilerBase fields

    :return: function returns nothing
    :rtype: None
    """
    self._measurement_determinism = "probabilistic"
    self._noise_simulation = False
    self._monte_carlo = False

compile(circuit, initial_state=None)

Compiles (i.e. produces an output state) circuit, in the appropriate representation. This involves sequentially applying each operation of the circuit on the initial state

Parameters:

Name Type Description Default
circuit CircuitBase

the circuit to compile

required

Returns:

Type Description
QuantumState

the state produced by the circuit

Raises:

Type Description
ValueError

if there is a circuit Operation which is incompatible with this compiler

Source code in graphiq/backends/compiler_base.py
def compile(self, circuit: CircuitBase, initial_state=None):
    """
    Compiles (i.e. produces an output state) circuit, in the appropriate representation.
    This involves sequentially applying each operation of the circuit on the initial state

    :param circuit: the circuit to compile
    :type circuit: CircuitBase
    :raises ValueError: if there is a circuit Operation which is incompatible with this compiler
    :return: the state produced by the circuit
    :rtype: QuantumState
    """
    # TODO: make this more general, but for now we assume all registers are initialized to |0>

    if initial_state:
        assert isinstance(
            initial_state, QuantumState
        ), "the initial state must be a valid QuantumState object"
        assert (
            initial_state.n_qubits == circuit.n_quantum
        ), "the number of qubits in initial state must match the circuit"
        state_data = initial_state.rep_data.data
    else:
        state_data = circuit.n_quantum

    state = QuantumState(
        data=state_data,  # initialize to |0...0> state
        rep_type=self.__class__.name,
        mixed=True if (self._noise_simulation and not self._monte_carlo) else False,
    )

    classical_registers = np.zeros(circuit.n_classical)

    # TODO: support self-defined mapping functions later instead of using the default above
    # Get functions which will map from registers to a unique index
    q_index = CompilerBase.reg_to_index_func(circuit.n_photons)

    # the unwrapping allows us to support Wrapper operation types
    seq = circuit.sequence(unwrapped=True)

    for op in seq:
        if type(op) not in self.ops:
            raise RuntimeError(
                f"The Operation class {op.__class__.__name__} is not valid with "
                f"the {self.__class__.__name__} compiler"
            )

        no_noise = not self._noise_simulation
        is_controlled_op = isinstance(
            op, ops.ControlledPairOperationBase
        ) or isinstance(op, ops.ClassicalControlledPairOperationBase)

        if is_controlled_op:
            no_noise = (
                isinstance(op.noise[0], nm.NoNoise)
                and isinstance(op.noise[1], nm.NoNoise)
            ) or no_noise
        else:
            no_noise = no_noise or isinstance(op.noise, nm.NoNoise)

        if no_noise:
            self.compile_one_gate(
                state, op, circuit.n_quantum, q_index, classical_registers
            )

        else:
            if is_controlled_op:
                if isinstance(op.noise[0], nm.AdditionNoiseBase) and isinstance(
                    op.noise[1], nm.AdditionNoiseBase
                ):
                    after_control = op.noise[0].noise_parameters["After gate"]
                    after_target = op.noise[1].noise_parameters["After gate"]
                    if after_control and after_target:
                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                    elif (not after_control) and (not after_target):
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )
                    elif after_control and (not after_target):
                        noise_copy = op.noise
                        tmp_noise = [nm.NoNoise, op.noise[1]]
                        op.noise = tmp_noise
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )

                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )
                        tmp_noise = [op.noise[0], nm.NoNoise]

                        op.noise = tmp_noise
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                        op.noise = noise_copy
                    else:
                        noise_copy = op.noise
                        tmp_noise = [op.noise[0], nm.NoNoise]
                        op.noise = tmp_noise
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )

                        tmp_noise = [nm.NoNoise, op.noise[1]]
                        op.noise = tmp_noise
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                        op.noise = noise_copy
                else:
                    raise ValueError(
                        "We currently do not support different noise positions for one controlled gate."
                    )

            else:
                if isinstance(op.noise, nm.AdditionNoiseBase):
                    if op.noise.noise_parameters["After gate"]:
                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )

                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                    else:
                        self._apply_additional_noise(
                            state, op, circuit.n_quantum, q_index
                        )
                        self.compile_one_gate(
                            state,
                            op,
                            circuit.n_quantum,
                            q_index,
                            classical_registers,
                        )
                elif isinstance(op.noise, nm.ReplacementNoiseBase):
                    self.compile_one_noisy_gate(
                        state, op, circuit.n_quantum, q_index, classical_registers
                    )
                else:
                    raise ValueError("Noise position is not acceptable.")

    return state

reg_to_index_func(n_photon) staticmethod

Given the number of photonic qubits in the circuit, this returns a function which will match a given register number and type (i.e. photonic or emitter) to a matrix index

Parameters:

Name Type Description Default
n_photon int

the number of photonic qubits in the system being simulated

required

Returns:

Type Description
function

a function which will map register + register type to a matrix index (zero-indexed)

Source code in graphiq/backends/compiler_base.py
@staticmethod
def reg_to_index_func(n_photon):
    """
    Given the number of photonic qubits in the circuit, this returns a function which will match a
    given register number and type (i.e. photonic or emitter) to a matrix index

    :param n_photon: the number of photonic qubits in the system being simulated
    :type n_photon: int
    :return: a function which will map register + register type to a matrix index (zero-indexed)
    :rtype: function
    """

    def reg_to_index(reg, reg_type):
        if reg_type == "p":
            return reg
        elif reg_type == "e":
            return reg + n_photon

    return reg_to_index

validate_ops(circuit)

Verifies that all operations of a circuit are valid for the selected compiler

Parameters:

Name Type Description Default
circuit CircuitBase (or some subclass of it)

the circuit for which we are assessing validity of operations with this compiler

required

Returns:

Type Description
bool

True if all operations are valid, False otherwise

Source code in graphiq/backends/compiler_base.py
def validate_ops(self, circuit):
    """
    Verifies that all operations of a circuit are valid for the selected compiler

    :param circuit: the circuit for which we are assessing validity of operations with this compiler
    :type circuit: CircuitBase (or some subclass of it)
    :return: True if all operations are valid, False otherwise
    :rtype: bool
    """
    seq = circuit.sequence()
    valid = True

    for i, op in enumerate(seq):
        if type(op) in self.ops:
            logging.info(
                f"Operation {i} {type(op).__name__} is valid with {type(self).__name__}"
            )
        else:
            logging.error(
                f"Error: Operation {i} {type(op).__name__} is not valid with {type(self).__name__}"
            )
            valid = False

    return valid

graphiq.backends.density_matrix.compiler.DensityMatrixCompiler

Bases: CompilerBase

Compiler which deals exclusively with the DensityMatrix state representation. Currently creates a DensityMatrix state object and applies the circuit Operations to it in order

Source code in graphiq/backends/density_matrix/compiler.py
class DensityMatrixCompiler(CompilerBase):
    """
    Compiler which deals exclusively with the DensityMatrix state representation.
    Currently creates a DensityMatrix state object and applies the circuit Operations to it in order

    """

    # TODO: [longer term] refactor to take a QuantumState object input instead of creating its own initial state?

    name = "dm"
    ops = {  # the accepted operations and the single-qubit action needed for each gate
        ops.Input: lambda: None,
        ops.Identity: lambda: None,
        ops.Phase: lambda: dm.phase(),
        ops.PhaseDagger: lambda: dm.phase_dag(),
        ops.Hadamard: lambda: dm.hadamard(),
        ops.SigmaX: lambda: dm.sigmax(),
        ops.SigmaY: lambda: dm.sigmay(),
        ops.SigmaZ: lambda: dm.sigmaz(),
        ops.ParameterizedOneQubitRotation: (
            lambda theta, phi, lam: dm.parameterized_one_qubit_unitary(theta, phi, lam)
        ),
        ops.ParameterizedControlledRotationQubit: (
            lambda theta, phi, lam: dm.parameterized_one_qubit_unitary(theta, phi, lam)
        ),
        ops.CNOT: lambda: dm.sigmax(),
        ops.CZ: lambda: dm.sigmaz(),
        ops.ClassicalCNOT: lambda: dm.sigmax(),
        ops.ClassicalCZ: lambda: dm.sigmaz(),
        ops.MeasurementZ: lambda: dm.sigmaz(),
        ops.MeasurementCNOTandReset: lambda: dm.sigmax(),
        ops.Output: lambda: None,
    }

    def __init__(self, *args, **kwargs):
        """
        Create a compiler which acts on a DensityMatrix state representation

        :return: nothing
        :rtype: None
        """
        super().__init__(*args, **kwargs)

    def compile_one_gate(self, state, op, n_quantum, q_index, classical_registers):
        """
        Compile one ideal gate

        :param state: the QuantumState representation of the state to be evolved, where a density matrix representation can be accessed
        :type state: QuantumState
        :param op: the operation to be applied
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :param classical_registers: a list of values for classical registers
        :type classical_registers: list
        :return: nothing
        :rtype: None
        """
        assert (
            op.__class__ in self.ops.keys()
        ), f"{op.__class__} is not a valid operation for this compiler"
        state = state.rep_data

        params = op.params

        if isinstance(op, ops.InputOutputOperationBase) or isinstance(op, ops.Identity):
            pass  # TODO: should think about best way to handle inputs/outputs

        elif isinstance(op, ops.OneQubitOperationBase):
            unitary = dm.get_one_qubit_gate(
                n_quantum,
                q_index(op.register, op.reg_type),
                self.ops[op.__class__](*params),
            )
            state.apply_unitary(unitary)

        elif isinstance(op, ops.ControlledPairOperationBase):
            unitary = dm.get_two_qubit_controlled_gate(
                n_quantum,
                q_index(op.control, op.control_type),
                q_index(op.target, op.target_type),
                self.ops[op.__class__](*params),
            )
            state.apply_unitary(unitary)

        elif isinstance(op, ops.ClassicalControlledPairOperationBase):
            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.control, op.control_type)
            )

            # apply an gate on the target qubit conditioned on the measurement outcome = 1
            unitary = dm.get_one_qubit_gate(
                n_quantum,
                q_index(op.target, op.target_type),
                self.ops[op.__class__](*params),
            )

            outcome = state.apply_measurement_controlled_gate(
                projectors,
                unitary,
                measurement_determinism=self.measurement_determinism,
            )

            classical_registers[op.c_register] = outcome

        elif isinstance(op, ops.MeasurementCNOTandReset):
            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.control, op.control_type)
            )

            # apply an X gate on the target qubit conditioned on the measurement outcome = 1
            unitary = dm.get_one_qubit_gate(
                n_quantum, q_index(op.target, op.target_type), dm.sigmax()
            )

            outcome = state.apply_measurement_controlled_gate(
                projectors,
                unitary,
                measurement_determinism=self.measurement_determinism,
            )

            # reset the control qubit
            reset_kraus_ops = dm.get_reset_qubit_kraus(
                n_quantum, q_index(op.control, op.control_type)
            )

            classical_registers[op.c_register] = outcome
            state.apply_channel(reset_kraus_ops)

        elif isinstance(op, ops.MeasurementZ):
            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.register, op.reg_type)
            )
            outcome = state.apply_measurement(
                projectors, measurement_determinism=self.measurement_determinism
            )
            classical_registers[op.c_register] = outcome

        else:
            raise ValueError(
                f"The compile function has an error. "
                f"{type(op)} is a valid operation of the class, but the op was not processed"
            )

    def compile_one_noisy_gate(
        self, state, op, n_quantum, q_index, classical_registers
    ):
        """
        Compile one noisy gate

        :param state: the density matrix representation of the state to be evolved
        :type state: DensityMatrix
        :param op: the operation to be applied
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :param classical_registers: a list of values for classical registers
        :type classical_registers: list
        :return: nothing
        :rtype: None
        """
        if isinstance(op, ops.InputOutputOperationBase):
            pass

        elif isinstance(op, ops.OneQubitOperationBase):
            op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])

        elif isinstance(op, ops.ControlledPairOperationBase):
            op.noise[0].apply(
                state,
                n_quantum,
                [q_index(op.control, op.control_type)],
            )
            op.noise[1].apply(
                state,
                n_quantum,
                [q_index(op.target, op.target_type)],
            )

        # TODO: Handle the following two-qubit noisy gates, currently no replacement or partial replacement
        else:
            if isinstance(op, ops.ClassicalControlledPairOperationBase):
                projectors = dm.projectors_zbasis(
                    n_quantum, q_index(op.control, op.control_type)
                )

                if isinstance(op.noise, nm.OneQubitGateReplacement):
                    # apply whatever unitary given by the noise model to the target qubit
                    unitary = op.noise.get_backend_dependent_noise(
                        state, n_quantum, [q_index(op.target, op.target_type)]
                    )
                else:
                    # apply an X gate on the target qubit conditioned on the measurement outcome = 1
                    unitary = dm.get_one_qubit_gate(
                        n_quantum,
                        q_index(op.target, op.target_type),
                        self.ops[op.__class__],
                    )

                outcome = state.dm.apply_measurement_controlled_gate(
                    projectors,
                    unitary,
                    measurement_determinism=self.measurement_determinism,
                )
                classical_registers[op.c_register] = outcome

            elif type(op) is ops.MeasurementCNOTandReset:
                projectors = dm.projectors_zbasis(
                    n_quantum, q_index(op.control, op.control_type)
                )

                if isinstance(op.noise, nm.OneQubitGateReplacement):
                    # apply whatever unitary given by the noise model to the target qubit
                    unitary = op.noise.get_backend_dependent_noise(
                        state, n_quantum, [q_index(op.target, op.target_type)]
                    )
                else:
                    # apply an X gate on the target qubit conditioned on the measurement outcome = 1
                    unitary = dm.get_one_qubit_gate(
                        n_quantum, q_index(op.target, op.target_type), dm.sigmax()
                    )

                outcome = state.dm.apply_measurement_controlled_gate(
                    projectors,
                    unitary,
                    measurement_determinism=self.measurement_determinism,
                )

                # reset the control qubit
                reset_kraus_ops = dm.get_reset_qubit_kraus(
                    n_quantum, q_index(op.control, op.control_type)
                )

                classical_registers[op.c_register] = outcome
                state.dm.apply_channel(reset_kraus_ops)

            elif type(op) is ops.MeasurementZ:
                # TODO: implement measurement-related error model

                projectors = dm.projectors_zbasis(
                    n_quantum, q_index(op.register, op.reg_type)
                )
                outcome = state.dm.apply_measurement(
                    projectors, measurement_determinism=self.measurement_determinism
                )
                classical_registers[op.c_register] = outcome

            else:
                raise ValueError(
                    f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
                )

    def _apply_additional_noise(self, state, op, n_quantum, q_index):
        """
        A helper function to apply additional noise before or after the operation

        :param state: the state representation
        :type state: QuantumState
        :param op: the operation associated with the noise
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :return: nothing
        :rtype: None
        """
        if isinstance(op, ops.OneQubitOperationBase):
            op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])
        elif isinstance(op, ops.ControlledPairOperationBase):
            if not isinstance(op.noise, list):
                op.noise = [op.noise] * 2
            control_noise = op.noise[0]
            target_noise = op.noise[1]
            control_noise.apply(
                state, n_quantum, [q_index(op.control, op.control_type)]
            )
            target_noise.apply(state, n_quantum, [q_index(op.target, op.target_type)])
        else:
            raise ValueError(
                f"Noise model not implemented for operation type {type(op)}"
            )

__init__(*args, **kwargs)

Create a compiler which acts on a DensityMatrix state representation

Returns:

Type Description
None

nothing

Source code in graphiq/backends/density_matrix/compiler.py
def __init__(self, *args, **kwargs):
    """
    Create a compiler which acts on a DensityMatrix state representation

    :return: nothing
    :rtype: None
    """
    super().__init__(*args, **kwargs)

compile_one_gate(state, op, n_quantum, q_index, classical_registers)

Compile one ideal gate

Parameters:

Name Type Description Default
state QuantumState

the QuantumState representation of the state to be evolved, where a density matrix representation can be accessed

required
op OperationBase

the operation to be applied

required
n_quantum int

the number of qubits

required
q_index function

a function that maps register + register type to a matrix index (zero-indexed)

required
classical_registers list

a list of values for classical registers

required

Returns:

Type Description
None

nothing

Source code in graphiq/backends/density_matrix/compiler.py
def compile_one_gate(self, state, op, n_quantum, q_index, classical_registers):
    """
    Compile one ideal gate

    :param state: the QuantumState representation of the state to be evolved, where a density matrix representation can be accessed
    :type state: QuantumState
    :param op: the operation to be applied
    :type op: OperationBase
    :param n_quantum: the number of qubits
    :type n_quantum: int
    :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
    :type q_index: function
    :param classical_registers: a list of values for classical registers
    :type classical_registers: list
    :return: nothing
    :rtype: None
    """
    assert (
        op.__class__ in self.ops.keys()
    ), f"{op.__class__} is not a valid operation for this compiler"
    state = state.rep_data

    params = op.params

    if isinstance(op, ops.InputOutputOperationBase) or isinstance(op, ops.Identity):
        pass  # TODO: should think about best way to handle inputs/outputs

    elif isinstance(op, ops.OneQubitOperationBase):
        unitary = dm.get_one_qubit_gate(
            n_quantum,
            q_index(op.register, op.reg_type),
            self.ops[op.__class__](*params),
        )
        state.apply_unitary(unitary)

    elif isinstance(op, ops.ControlledPairOperationBase):
        unitary = dm.get_two_qubit_controlled_gate(
            n_quantum,
            q_index(op.control, op.control_type),
            q_index(op.target, op.target_type),
            self.ops[op.__class__](*params),
        )
        state.apply_unitary(unitary)

    elif isinstance(op, ops.ClassicalControlledPairOperationBase):
        projectors = dm.projectors_zbasis(
            n_quantum, q_index(op.control, op.control_type)
        )

        # apply an gate on the target qubit conditioned on the measurement outcome = 1
        unitary = dm.get_one_qubit_gate(
            n_quantum,
            q_index(op.target, op.target_type),
            self.ops[op.__class__](*params),
        )

        outcome = state.apply_measurement_controlled_gate(
            projectors,
            unitary,
            measurement_determinism=self.measurement_determinism,
        )

        classical_registers[op.c_register] = outcome

    elif isinstance(op, ops.MeasurementCNOTandReset):
        projectors = dm.projectors_zbasis(
            n_quantum, q_index(op.control, op.control_type)
        )

        # apply an X gate on the target qubit conditioned on the measurement outcome = 1
        unitary = dm.get_one_qubit_gate(
            n_quantum, q_index(op.target, op.target_type), dm.sigmax()
        )

        outcome = state.apply_measurement_controlled_gate(
            projectors,
            unitary,
            measurement_determinism=self.measurement_determinism,
        )

        # reset the control qubit
        reset_kraus_ops = dm.get_reset_qubit_kraus(
            n_quantum, q_index(op.control, op.control_type)
        )

        classical_registers[op.c_register] = outcome
        state.apply_channel(reset_kraus_ops)

    elif isinstance(op, ops.MeasurementZ):
        projectors = dm.projectors_zbasis(
            n_quantum, q_index(op.register, op.reg_type)
        )
        outcome = state.apply_measurement(
            projectors, measurement_determinism=self.measurement_determinism
        )
        classical_registers[op.c_register] = outcome

    else:
        raise ValueError(
            f"The compile function has an error. "
            f"{type(op)} is a valid operation of the class, but the op was not processed"
        )

compile_one_noisy_gate(state, op, n_quantum, q_index, classical_registers)

Compile one noisy gate

Parameters:

Name Type Description Default
state DensityMatrix

the density matrix representation of the state to be evolved

required
op OperationBase

the operation to be applied

required
n_quantum int

the number of qubits

required
q_index function

a function that maps register + register type to a matrix index (zero-indexed)

required
classical_registers list

a list of values for classical registers

required

Returns:

Type Description
None

nothing

Source code in graphiq/backends/density_matrix/compiler.py
def compile_one_noisy_gate(
    self, state, op, n_quantum, q_index, classical_registers
):
    """
    Compile one noisy gate

    :param state: the density matrix representation of the state to be evolved
    :type state: DensityMatrix
    :param op: the operation to be applied
    :type op: OperationBase
    :param n_quantum: the number of qubits
    :type n_quantum: int
    :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
    :type q_index: function
    :param classical_registers: a list of values for classical registers
    :type classical_registers: list
    :return: nothing
    :rtype: None
    """
    if isinstance(op, ops.InputOutputOperationBase):
        pass

    elif isinstance(op, ops.OneQubitOperationBase):
        op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])

    elif isinstance(op, ops.ControlledPairOperationBase):
        op.noise[0].apply(
            state,
            n_quantum,
            [q_index(op.control, op.control_type)],
        )
        op.noise[1].apply(
            state,
            n_quantum,
            [q_index(op.target, op.target_type)],
        )

    # TODO: Handle the following two-qubit noisy gates, currently no replacement or partial replacement
    else:
        if isinstance(op, ops.ClassicalControlledPairOperationBase):
            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.control, op.control_type)
            )

            if isinstance(op.noise, nm.OneQubitGateReplacement):
                # apply whatever unitary given by the noise model to the target qubit
                unitary = op.noise.get_backend_dependent_noise(
                    state, n_quantum, [q_index(op.target, op.target_type)]
                )
            else:
                # apply an X gate on the target qubit conditioned on the measurement outcome = 1
                unitary = dm.get_one_qubit_gate(
                    n_quantum,
                    q_index(op.target, op.target_type),
                    self.ops[op.__class__],
                )

            outcome = state.dm.apply_measurement_controlled_gate(
                projectors,
                unitary,
                measurement_determinism=self.measurement_determinism,
            )
            classical_registers[op.c_register] = outcome

        elif type(op) is ops.MeasurementCNOTandReset:
            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.control, op.control_type)
            )

            if isinstance(op.noise, nm.OneQubitGateReplacement):
                # apply whatever unitary given by the noise model to the target qubit
                unitary = op.noise.get_backend_dependent_noise(
                    state, n_quantum, [q_index(op.target, op.target_type)]
                )
            else:
                # apply an X gate on the target qubit conditioned on the measurement outcome = 1
                unitary = dm.get_one_qubit_gate(
                    n_quantum, q_index(op.target, op.target_type), dm.sigmax()
                )

            outcome = state.dm.apply_measurement_controlled_gate(
                projectors,
                unitary,
                measurement_determinism=self.measurement_determinism,
            )

            # reset the control qubit
            reset_kraus_ops = dm.get_reset_qubit_kraus(
                n_quantum, q_index(op.control, op.control_type)
            )

            classical_registers[op.c_register] = outcome
            state.dm.apply_channel(reset_kraus_ops)

        elif type(op) is ops.MeasurementZ:
            # TODO: implement measurement-related error model

            projectors = dm.projectors_zbasis(
                n_quantum, q_index(op.register, op.reg_type)
            )
            outcome = state.dm.apply_measurement(
                projectors, measurement_determinism=self.measurement_determinism
            )
            classical_registers[op.c_register] = outcome

        else:
            raise ValueError(
                f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
            )

graphiq.backends.stabilizer.compiler.StabilizerCompiler

Bases: CompilerBase

Compiler which deals exclusively with the state representation of Stabilizer. Currently creates a Stabilizer object and applies the circuit Operations to it in order

Source code in graphiq/backends/stabilizer/compiler.py
class StabilizerCompiler(CompilerBase):
    """
    Compiler which deals exclusively with the state representation of Stabilizer.
    Currently creates a Stabilizer object and applies the circuit Operations to it in order

    """

    # TODO: [longer term] refactor to take a QuantumState object input instead of creating its own initial state?

    name = "stabilizer"
    ops = [  # the accepted operations for a given compiler
        ops.Input,
        ops.Identity,
        ops.Phase,
        ops.PhaseDagger,
        ops.Hadamard,
        ops.SigmaX,
        ops.SigmaY,
        ops.SigmaZ,
        ops.CNOT,
        ops.CZ,
        ops.ClassicalCNOT,
        ops.ClassicalCZ,
        ops.MeasurementZ,
        ops.MeasurementCNOTandReset,
        ops.Output,
    ]

    def __init__(self, *args, **kwargs):
        """
        Create a compiler which acts on a Stabilizer representation

        :return: nothing
        :rtype: None
        """
        super().__init__(*args, **kwargs)

    def compile_one_gate(self, state, op, n_quantum, q_index, classical_registers):
        """
        Compile one ideal gate

        :param state: the stabilizer representation of the state to be evolved
        :type state: QuantumState
        :param op: the operation to be applied
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :param classical_registers: a list of values for classical registers
        :type classical_registers: list
        :return: nothing
        :rtype: None
        """
        state = state.rep_data

        if type(op) is ops.Input:
            return

        elif type(op) is ops.Output:
            return

        elif type(op) is ops.Identity:
            return

        elif type(op) is ops.Hadamard:
            state.apply_hadamard(q_index(op.register, op.reg_type))

        elif type(op) is ops.Phase:
            state.apply_phase(q_index(op.register, op.reg_type))

        elif type(op) is ops.PhaseDagger:
            state.apply_phase_dagger(q_index(op.register, op.reg_type))

        elif type(op) is ops.SigmaX:
            state.apply_sigmax(q_index(op.register, op.reg_type))

        elif type(op) is ops.SigmaY:
            state.apply_sigmay(q_index(op.register, op.reg_type))

        elif type(op) is ops.SigmaZ:
            state.apply_sigmaz(q_index(op.register, op.reg_type))

        elif type(op) is ops.CNOT:
            state.apply_cnot(
                control=q_index(op.control, op.control_type),
                target=q_index(op.target, op.target_type),
            )

        elif type(op) is ops.CZ:
            state.apply_cz(
                control=q_index(op.control, op.control_type),
                target=q_index(op.target, op.target_type),
            )

        elif type(op) is ops.ClassicalCNOT:
            # apply an X gate on the target qubit conditioned on the measurement outcome = 1
            if isinstance(state, MixedStabilizer):
                outcome = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )
                print(outcome)
                state.apply_conditioned_gate(
                    q_index(op.target, op.target_type), outcome, gate="x"
                )

            else:
                outcome = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )
                if outcome == 1:
                    state.apply_sigmax(q_index(op.target, op.target_type))

            classical_registers[op.c_register] = outcome

        elif type(op) is ops.ClassicalCZ:
            # apply an Z gate on the target qubit conditioned on the measurement outcome = 1
            if isinstance(state, MixedStabilizer):
                outcome = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )
                state.apply_conditioned_gate(
                    q_index(op.target, op.target_type), outcome, gate="z"
                )
                classical_registers[op.c_register] = outcome[0]

            else:
                outcome = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )
                if outcome == 1:
                    state.apply_sigmaz(q_index(op.target, op.target_type))

            classical_registers[op.c_register] = outcome

        elif type(op) is ops.MeasurementCNOTandReset:
            if isinstance(state, MixedStabilizer):
                outcomes = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )
                state.apply_conditioned_gate(
                    q_index(op.target, op.target_type), outcomes, gate="x"
                )

            else:
                outcome = state.apply_measurement(
                    q_index(op.control, op.control_type),
                    measurement_determinism=self.measurement_determinism,
                )

                if outcome == 1:
                    state.apply_sigmax(q_index(op.target, op.target_type))

            # reset the control qubit
            state.reset_qubit(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )

        elif type(op) is ops.MeasurementZ:
            if isinstance(state, MixedStabilizer):
                outcomes = state.apply_measurement(
                    q_index(op.register, op.reg_type),
                    measurement_determinism=self.measurement_determinism,
                )
                classical_registers[op.c_register] = outcomes[0]

            else:
                outcome = state.apply_measurement(
                    q_index(op.register, op.reg_type),
                    measurement_determinism=self.measurement_determinism,
                )
                classical_registers[op.c_register] = outcome

        else:
            raise ValueError(
                f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
            )

    def compile_one_noisy_gate(
        self, state, op, n_quantum, q_index, classical_registers
    ):
        """
        Compile one noisy gate

        :param state: the QuantumState representation of the state to be evolved,
            where a stabilizer representation can be accessed
        :type state: QuantumState
        :param op: the operation to be applied
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :param classical_registers: a list of values for classical registers
        :type classical_registers: list
        :return: nothing
        :rtype: None
        """
        # TODO: consolidate compile_one_gate and compile_one_noisy_gate to one function
        if isinstance(op, ops.InputOutputOperationBase):
            pass

        elif isinstance(op, ops.OneQubitOperationBase):
            op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])

        elif isinstance(op, ops.ControlledPairOperationBase):
            op.noise.apply(
                state,
                n_quantum,
                [
                    q_index(op.control, op.control_type),
                    q_index(op.target, op.target_type),
                ],
            )

        # TODO: Handle the following two-qubit noisy gates
        else:
            if type(op) is ops.ClassicalCNOT:
                pass

            elif type(op) is ops.ClassicalCZ:
                pass

            elif type(op) is ops.MeasurementCNOTandReset:
                pass

            elif type(op) is ops.MeasurementZ:
                pass

            else:
                raise ValueError(
                    f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
                )

    def _apply_additional_noise(self, state, op, n_quantum, q_index):
        """
        A helper function to apply additional noise before or after the operation

        :param state: the state to be applied for the additional noise
        :type state: QuantumState
        :param op: the operation associated with the noise
        :type op: OperationBase
        :param n_quantum: the number of qubits
        :type n_quantum: int
        :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
        :type q_index: function
        :return: nothing
        :rtype: None
        """
        if isinstance(op, ops.OneQubitOperationBase):
            op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])
        elif isinstance(op, ops.ControlledPairOperationBase):
            if not isinstance(op.noise, list):
                op.noise = [op.noise] * 2
            control_noise = op.noise[0]
            target_noise = op.noise[1]
            control_noise.apply(
                state, n_quantum, [q_index(op.control, op.control_type)]
            )
            target_noise.apply(state, n_quantum, [q_index(op.target, op.target_type)])
        else:
            pass

__init__(*args, **kwargs)

Create a compiler which acts on a Stabilizer representation

Returns:

Type Description
None

nothing

Source code in graphiq/backends/stabilizer/compiler.py
def __init__(self, *args, **kwargs):
    """
    Create a compiler which acts on a Stabilizer representation

    :return: nothing
    :rtype: None
    """
    super().__init__(*args, **kwargs)

compile_one_gate(state, op, n_quantum, q_index, classical_registers)

Compile one ideal gate

Parameters:

Name Type Description Default
state QuantumState

the stabilizer representation of the state to be evolved

required
op OperationBase

the operation to be applied

required
n_quantum int

the number of qubits

required
q_index function

a function that maps register + register type to a matrix index (zero-indexed)

required
classical_registers list

a list of values for classical registers

required

Returns:

Type Description
None

nothing

Source code in graphiq/backends/stabilizer/compiler.py
def compile_one_gate(self, state, op, n_quantum, q_index, classical_registers):
    """
    Compile one ideal gate

    :param state: the stabilizer representation of the state to be evolved
    :type state: QuantumState
    :param op: the operation to be applied
    :type op: OperationBase
    :param n_quantum: the number of qubits
    :type n_quantum: int
    :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
    :type q_index: function
    :param classical_registers: a list of values for classical registers
    :type classical_registers: list
    :return: nothing
    :rtype: None
    """
    state = state.rep_data

    if type(op) is ops.Input:
        return

    elif type(op) is ops.Output:
        return

    elif type(op) is ops.Identity:
        return

    elif type(op) is ops.Hadamard:
        state.apply_hadamard(q_index(op.register, op.reg_type))

    elif type(op) is ops.Phase:
        state.apply_phase(q_index(op.register, op.reg_type))

    elif type(op) is ops.PhaseDagger:
        state.apply_phase_dagger(q_index(op.register, op.reg_type))

    elif type(op) is ops.SigmaX:
        state.apply_sigmax(q_index(op.register, op.reg_type))

    elif type(op) is ops.SigmaY:
        state.apply_sigmay(q_index(op.register, op.reg_type))

    elif type(op) is ops.SigmaZ:
        state.apply_sigmaz(q_index(op.register, op.reg_type))

    elif type(op) is ops.CNOT:
        state.apply_cnot(
            control=q_index(op.control, op.control_type),
            target=q_index(op.target, op.target_type),
        )

    elif type(op) is ops.CZ:
        state.apply_cz(
            control=q_index(op.control, op.control_type),
            target=q_index(op.target, op.target_type),
        )

    elif type(op) is ops.ClassicalCNOT:
        # apply an X gate on the target qubit conditioned on the measurement outcome = 1
        if isinstance(state, MixedStabilizer):
            outcome = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )
            print(outcome)
            state.apply_conditioned_gate(
                q_index(op.target, op.target_type), outcome, gate="x"
            )

        else:
            outcome = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )
            if outcome == 1:
                state.apply_sigmax(q_index(op.target, op.target_type))

        classical_registers[op.c_register] = outcome

    elif type(op) is ops.ClassicalCZ:
        # apply an Z gate on the target qubit conditioned on the measurement outcome = 1
        if isinstance(state, MixedStabilizer):
            outcome = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )
            state.apply_conditioned_gate(
                q_index(op.target, op.target_type), outcome, gate="z"
            )
            classical_registers[op.c_register] = outcome[0]

        else:
            outcome = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )
            if outcome == 1:
                state.apply_sigmaz(q_index(op.target, op.target_type))

        classical_registers[op.c_register] = outcome

    elif type(op) is ops.MeasurementCNOTandReset:
        if isinstance(state, MixedStabilizer):
            outcomes = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )
            state.apply_conditioned_gate(
                q_index(op.target, op.target_type), outcomes, gate="x"
            )

        else:
            outcome = state.apply_measurement(
                q_index(op.control, op.control_type),
                measurement_determinism=self.measurement_determinism,
            )

            if outcome == 1:
                state.apply_sigmax(q_index(op.target, op.target_type))

        # reset the control qubit
        state.reset_qubit(
            q_index(op.control, op.control_type),
            measurement_determinism=self.measurement_determinism,
        )

    elif type(op) is ops.MeasurementZ:
        if isinstance(state, MixedStabilizer):
            outcomes = state.apply_measurement(
                q_index(op.register, op.reg_type),
                measurement_determinism=self.measurement_determinism,
            )
            classical_registers[op.c_register] = outcomes[0]

        else:
            outcome = state.apply_measurement(
                q_index(op.register, op.reg_type),
                measurement_determinism=self.measurement_determinism,
            )
            classical_registers[op.c_register] = outcome

    else:
        raise ValueError(
            f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
        )

compile_one_noisy_gate(state, op, n_quantum, q_index, classical_registers)

Compile one noisy gate

Parameters:

Name Type Description Default
state QuantumState

the QuantumState representation of the state to be evolved, where a stabilizer representation can be accessed

required
op OperationBase

the operation to be applied

required
n_quantum int

the number of qubits

required
q_index function

a function that maps register + register type to a matrix index (zero-indexed)

required
classical_registers list

a list of values for classical registers

required

Returns:

Type Description
None

nothing

Source code in graphiq/backends/stabilizer/compiler.py
def compile_one_noisy_gate(
    self, state, op, n_quantum, q_index, classical_registers
):
    """
    Compile one noisy gate

    :param state: the QuantumState representation of the state to be evolved,
        where a stabilizer representation can be accessed
    :type state: QuantumState
    :param op: the operation to be applied
    :type op: OperationBase
    :param n_quantum: the number of qubits
    :type n_quantum: int
    :param q_index: a function that maps register + register type to a matrix index (zero-indexed)
    :type q_index: function
    :param classical_registers: a list of values for classical registers
    :type classical_registers: list
    :return: nothing
    :rtype: None
    """
    # TODO: consolidate compile_one_gate and compile_one_noisy_gate to one function
    if isinstance(op, ops.InputOutputOperationBase):
        pass

    elif isinstance(op, ops.OneQubitOperationBase):
        op.noise.apply(state, n_quantum, [q_index(op.register, op.reg_type)])

    elif isinstance(op, ops.ControlledPairOperationBase):
        op.noise.apply(
            state,
            n_quantum,
            [
                q_index(op.control, op.control_type),
                q_index(op.target, op.target_type),
            ],
        )

    # TODO: Handle the following two-qubit noisy gates
    else:
        if type(op) is ops.ClassicalCNOT:
            pass

        elif type(op) is ops.ClassicalCZ:
            pass

        elif type(op) is ops.MeasurementCNOTandReset:
            pass

        elif type(op) is ops.MeasurementZ:
            pass

        else:
            raise ValueError(
                f"{type(op)} is invalid or not implemented for {self.__class__.__name__}."
            )