User Guide
Subpackages
QuantumCircuit
This module allows users to create and simulate quantum circuits with noise models based on quantum machine calibration data. It provides methods for adding gates, executing the circuit with Monte-Carlo simulations, and visualizing the circuit. It considers both single and two-qubit gate errors as well as measurement errors.
Example
>>> from NoisyCircuits.QuantumCircuit import QuantumCircuit
>>> circuit = QuantumCircuit(num_qubits=3, noise_model=my_noise_model, backend_qpu_type='Heron', use_fractional=True, sim_backend="custom", threshold=1e-8, verbose=False) # Using the default value for basis_gates --> basis gates of the Heron QPU.
>>> circuit.h(0)
>>> circuit.cx(0, 1)
>>> circuit.cx(1, 2)
>>> circuit.run_with_density_matrix(qubits=[0, 1, 2]) # Executes the circuit using the density matrix solver
[0.39841323, 0.00300163, 0.09303931, 0.00615167, 0.00616272, 0.09281154, 0.00300024, 0.39741967]
>>> circuit.execute(qubits=[0, 1, 2]) # Executes the circuit using the Monte-Carlo Wavefunction method
[0.39748485, 0.0037614 , 0.09168292, 0.00799886, 0.00746056, 0.09156762, 0.00367236, 0.39637143]
>>> circuit.run_pure_state(qubits=[0, 1, 2], num_cores=2, return_statevector=False) # Executes the circuit using the pure state solver to return probabilities
[0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5]
>>> circuit.run_pure_state(qubits=[0, 1, 2], num_cores=2, return_statevector=False) # Executes the circuit using the pure state solver to return amplitudes
[0.7071067811865475 + 0j, 0 + 0j, 0 + 0j, 0 + 0j, 0 + 0j, 0 + 0j, 0 + 0j, 0.7071067811865475 + 0j]
- class NoisyCircuits.QuantumCircuit.QuantumCircuit(num_qubits: int, noise_model: dict, backend_qpu_type: str = 'heron', basis_gates: list[list[str]] = [['rz', 'rx', 'sx', 'x'], ['cz', 'rzz']], use_fractional: bool = True, sim_backend: str = 'custom', threshold: float = 1e-12, verbose: bool = True)[source]
Bases:
objectThis class allows a user to create a quantum circuit with error model from IBM machines (or an available CSV data file with the machine calibration data) where selected gates (both parameterized and non-parameterized) are implemented as methods. The gate decomposition uses the basis gates of the IBM Eagle (\(\sqrt{X}\), \(X\), \(R_Z(\theta)\) and \(ECR\)) / Heron (\(\sqrt{X}\), \(X\), \(R_Z(\theta)\), \(R_X(\theta)\), \(CZ\) and \(RZZ(\theta)\)) QPUs. Users can use custom gate decompositions/basis gates by utilizing the OpenQasm 3.0 data format to read-in quantum circuits.
Currently, it is only possible to apply a limited selection of single and two-qubit gates to the circuit simulation (except when imported via OpenQasm). For a full list of supported gates, please refer to the Decomposition
NoisyCircuits.utils.Decomposition()class documentation.- Parameters:
num_qubits (int) – The number of qubits in the quantum circuit.
noise_model (dict) – A dictionary containing the raw noise model for the quantum circuit.
backend_qpu_type (str) – The QPU architecture to use. Supported options are eagle and heron. (Defaults to heron)
basis_gates (list[list[str]]) – A list of list of basis gates given in the format [[single qubit gates], [two qubit gates]]. (Defaults to the basis gates of the Heron QPU)
use_fractional (bool) – A flag to determine whether to use fractional gates or not. Applicable to QPUs which allow fractional gates (Defaults to True)
sim_backend (str) – The simulation backend to use for propogating the quantum circuit. Supported options are custom, pennylane, qiskit and qulacs. (Defaults to custom)
threshold (float) – The threshold for pruning errors in the noise model. (Defaults to 1e-12)
verbose (bool) – Whether to print verbose output during the noise model construction. (Defaults to True)
- Raises:
TypeError : –
num_qubits must be an integer. - noise_model must be a dictionary. - backend_qpu_type must be a string. - basis_gates must be a list of list of strings. - use_fractional must be a boolean. - sim_backend must be a string. - threshold must be a float. - verbose must be a boolean.
ValueError : –
num_qubits must be a positive integer. - backend_qpu_type must be one of the supported QPU architectures. - sim_backend must be one of the supported simulation backends. - threshold must be between 0 and 1 (exclusive).
Notes
For using a different backend QPU, write in the basis gate set of the QPU in the arguement and leave the backend_qpu_type empty to let it default to “heron” but the customized basis gate set is given priority.
- available_sim_backends = ['custom', 'pennylane', 'qiskit', 'qulacs']
- property basis_gates: list[list[str]]
Getter for the basis gates attribute.
- Returns:
The list of basis gates supported by the simulator in the format [[single_qubit_gates], [two_qubit_gates]].
- Return type:
list[list[str]]
- basis_gates_set = {'eagle': {'basis_gates': [['rz', 'sx', 'x'], ['ecr']], 'gate_decomposition': <class 'NoisyCircuits.utils.EagleDecomposition.EagleDecomposition'>}, 'heron': {'basis_gates': [['rz', 'rx', 'sx', 'x'], ['cz', 'rzz']], 'gate_decomposition': <class 'NoisyCircuits.utils.HeronDecomposition.HeronDecomposition'>}}
- draw_circuit(style: str = 'mpl') None[source]
Draws the quantum circuit.
- Parameters:
style (str, optional) – The style of the drawing, either ‘mpl’ for matplotlib or ‘text’ for text-based representation. Defaults to ‘mpl’.
- Raises:
TypeError: –
If style is not a string.
ValueError: –
If style is not one of [‘mpl’, ‘text’]. - If there are no instructions in the circuit to draw.
- execute(qubits: list[int] | None = None, num_trajectories: int = 100, num_cores: int = -1) ndarray[float64][source]
Executes the quantum circuit simulation using the Monte-Carlo Wavefunction method.
- Parameters:
qubits (list[int], optional) – List of qubits to be measured. If None, all qubits will be measured.
num_trajectories (int, optional) – The total number of trajectories to run. Defaults to 100.
num_cores (int, optional) – The number of CPU cores to use for parallel execution. If -1, half of all available cores will be used. Defaults to -1.
- Returns:
An array containing the probabilities of the executed quantum circuit.
- Return type:
np.ndarray[np.float64]
- Raises:
TypeError –
Raised when qubits is not a list of integers. - Raised when num_trajectories is not an integer. - Raised when num_cores is not an integer.
ValueError –
Raised when qubits contains invalid qubit indices. - Raised when there are no instructions in the circuit to execute. - Raised when num_trajectories is not a positive integer. - Raised when num_cores is not a positive integer or -1.
Notes
This method is designed for shared memory parallel execution. In case where the RAM requirements for running a single trajectory are very high or single threaded execution is too long, users may consider the execute_mpi method that is specifically designed for distributed memory systems (eg. HPC clusters) for faster execution.
- read_openqasm(file_path: str, append_to_circuit: bool = False) None[source]
Reads an OpenQASM 3.0 file and adds the required instructions to build the quantum circuit.
- Parameters:
file_path (str) – The path to the OpenQASM 3.0 file.
append_to_circuit (bool, optional) – A flag to determine whether to add the parser
- refresh()[source]
Resets the quantum circuit by clearing the instruction list and qubit-to-instruction mapping.
- run_pure_state(qubits: list[int], num_cores: int = 1, return_statevector: bool = False) ndarray[float64] | ndarray[complex128][source]
Runs the quantum circuit with the pure state solver.
- Parameters:
qubits (list[int]) – List of qubits to be measured.
num_cores (int) – The number of CPU cores to use for parallel execution. Defaults to 1.
return_statevector (bool) – Whether to return the final statevector instead of the probabilities. Defaults to False.
- Returns:
If return_statevector is False, returns the probabilities of measuring each qubit in the computational basis. If return_statevector is True, returns the final statevector of the quantum circuit.
- Return type:
np.ndarray[np.float64] | np.ndarray[np.complex128]
- Raises:
TypeError –
Raised when qubits is not a list of integers. - Raised when num_cores is not an integer. - Raised when return_statevector is not a boolean.
ValueError –
Raised when qubits contains invalid qubit indices. - Raised when there are no instructions in the circuit to execute. - Raised when num_cores is not a positive integer or exceeds the number of available CPU cores.
Notes
If return_statevector is set to True, the function will return the final statevector of the quantum circuit instead of the probabilities. In this case, the qubits argument will be ignored and the statevector for all qubits will be returned.
- run_with_density_matrix(qubits: list[int], num_cores: int = 1) ndarray[float64][source]
Runs the quantum circuit with the density matrix solver.
- Parameters:
qubits (list[int]) – List of qubits to be measured.
num_cores (int) – The number of CPU cores to use for parallel execution. Defaults to 1.
- Returns:
Probabilities of measuring each qubit in the computational basis.
- Return type:
np.ndarray[np.float64]
- Raises:
TypeError –
Raised when qubits is not a list of integers. - Raised when num_cores is not an integer.
ValueError –
Raised when qubits contains invalid qubit indices. - Raised when there are no instructions in the circuit to execute. - Raised when num_cores is not a positive integer or exceeds the number of available CPU cores.
- property sim_backend: str
Getter for the _sim_backend attribute
- Returns:
The name of the current simulation backend.
- Return type:
str
QuantumCircuitMPI
This module allows users to create and simulate quantum circuits with noise models based on quantum machine calibration data in a distributed memory environment using MPI. It provides methods for adding gates, executing the circuit with Monte-Carlo simulations, and visualizing the circuit. It considers both single and two-qubit gate errors as well as measurement errors. The functionality is similar to the QuantumCircuit module.
Example:
- class NoisyCircuits.QuantumCircuitMPI.QuantumCircuitMPI(num_qubits: int, noise_model: dict, use_fractional: bool = True, backend_qpu_type: str = 'heron', basis_gates: list[list[str]] = [['x', 'sx', 'rz', 'rx'], ['rzz', 'cz']], num_nodes: int = 1, cores_per_node: int = 1, cores_per_trajectory: int = 1, threshold: float = 1e-06, verbose: bool = False)[source]
Bases:
objectThis class allows a user to create a quantum circuit with error model from IBM machines (or an available CSV data file with the machine calibration data) where selected gates (both parameterized and non-parameterized) are implemented as methods. The gate decomposition uses the basis gates of the IBM Eagle (\(\sqrt{X}\), \(X\), \(R_Z(\theta)\) and \(ECR\)) / Heron (\(\sqrt{X}\), \(X\), \(R_Z(\theta)\), \(R_X(\theta)\), \(CZ\) and \(RZZ(\theta)\)) QPUs. Users can use custom gate decompositions/basis gates by utilizing the OpenQasm 3.0 data format to read-in quantum circuits.
Currently, it is only possible to apply a limited selection of single and two-qubit gates to the circuit simulation. For a full list of supported gates (except when imported via OpenQasm), please refer to the Decomposition
NoisyCircuits.utils.Decomposition()class documentation.- Parameters:
num_qubits (int) – The number of qubits in the quantum circuit.
noise_model (dict) – A dictionary containing the raw noise model for the quantum circuit.
use_fractional (bool) – A flag to determine whether to use fractional gates or not. Applicable to QPUs which allow fractional gates (Defaults to True)
backend_qpu_type (str) – The QPU architecture to use. Supported options are eagle and heron. (Defaults to heron)
basis_gates (list[list[str]]) – A list of list of basis gates given in the format [[single qubit gates], [two qubit gates]]. (Defaults to the basis gates of the Heron QPU)
num_nodes (int) – The total number of nodes in the HPC that can be used for the computation. (Defaults to 1)
cores_per_node (int) – The total number of cores available for computation within a single node. (Defaults to 1)
cores_per_trajectory (int) – The total number of cores that can be used by a single trajectory at any given time. (Defaults to 1)
threshold (float) – The threshold for pruning errors in the noise model. (Defaults to 1e-6)
verbose (bool) – Whether to print verbose output during the noise model construction. (Defaults to False)
- Raises:
TypeError : –
num_qubits must be an integer. - noise_model must be a dictionary. - use_fractional must be a boolean. - backend_qpu_type must be a string. - basis_gates must be a list of list of strings. - num_nodes must be an integer. - num_nodes must be an integer. - cores_per_node must be an integer. - cores_per_trajectory must be an integer. - threshold must be a float. - verbose must be a boolean.
ValueError : –
num_qubits must be a positive integer. - backend_qpu_type must be one of the supported QPU architectures. - num_nodes must be \(\geq 1\) - cores_per_node must be \(\geq 1\) - cores_per_trajectory must be \(\geq 1\) - threshold must be between 0 and 1 (exclusive).
Notes
Using the default values of the arguements num_nodes, core_per_node and cores_per_trajectory results in a fully serial execution.
Users need to be aware of the MPI environment of the HPC they have access to and build necessary dependencies themselves as a pre-compiled pip package install or conda install may result in dependency clashes. (See HPC Installation guidelines for more information)
For using a different backend QPU, write in the basis gate set of the QPU in the arguement and leave the backend_qpu_type empty to let it default to “heron” but the customized basis gate set is given priority.
- basis_gate_set = {'eagle': {'basis_gates': [['x', 'sx', 'rz'], ['ecr']], 'gate_decomposition': <class 'NoisyCircuits.utils.EagleDecomposition.EagleDecomposition'>}, 'heron': {'basis_gates': [['x', 'sx', 'rz', 'rx'], ['rzz', 'cz']], 'gate_decomposition': <class 'NoisyCircuits.utils.HeronDecomposition.HeronDecomposition'>}}
- execute(qubits: list[int] | None = None, num_trajectories: int = 100) ndarray[float64][source]
Executes the quantum circuit simulation using the Monte-Carlo Wavefunction method on a HPC cluster in a distributed memory environment.
- Parameters:
qubits (list[int], optional) – List of qubits to be measured. If None, all qubits will be measured.
num_trajectories (int) – The total number of trajectories to run. Defaults to 100.
- Returns:
An array containing the probabilities of the executed quantum circuit.
- Return type:
np.ndarray[np.float64]
- Raises:
TypeError –
Raised when qubits is not a list of integers. - Raised when num_trajectories is not an integer.
ValueError –
Raised when qubits contains invalid qubit indices. - Raised when there are no instructions in the circuit to execute. - Raised when num_trajectories is not a positive integer.
RuntimeError –
Raised when the total estimated MPI ranks does not equal the total number of available MPI ranks (See Notes for more information)
Notes
The module first estimates the total number of ranks required to safely run the simulation based on the user input data of num_nodes (mathematically denoted as \(n_N\)), cores_per_node (mathematically denoted as \(c_N\)) and cores_per_trajectory (mathematically denoted as \(c_T\)). The total number of ranks (mathematically denoted as \(n_R\)) required is estimated by using the following formula:
\[n_R = n_N \cdot \lfloor\frac{c_N}{c_T}\rfloor\]The above estimate provides the minimum number of MPI ranks to safely perform the simulation. If this estimate does not match the available number of MPI ranks set by the MPI execution environment (e.g., SLURM/mpiexec), then a RuntimeError is raised in order to avoid any potential issues.
- read_openqasm(file_path: str, append_to_circuit: bool = False) None[source]
Reads an OpenQASM 3.0 file and adds the required instructions to build the quantum circuit.
- Parameters:
file_path (str) – The path to the OpenQASM 3.0 file.
append_to_circuit (bool, optional) – A flag to determine whether to add the parser
RunOnHardware
This module contains the RunOnHardware class which allows users to run quantum circuits on IBM Quantum Hardware. The class provides methods to create circuits (according to hardware requirements), set them up for execution, submit them to the hardware, check job status, cancel jobs, and retrieve results. IBM Quantum’s Qiskit Runtime Service is utilized for backend communication and job management. The retrieved results are formatted as probability distributions from little Endian format to big Endian format for easy analysis.
- Example usage:
>>> from NoisyCircuits.RunOnHardware import RunOnHardware >>> from NoisyCircuits.QuantumCircuit import QuantumCircuit >>> circuit = QuantumCircuit(num_qubits=2, noise_model=noise_model, num_cores=2, num_trajectories=100) >>> circuit.H(qubit=0) >>> circuit.CX(control=0, target=1) >>> runner = RunOnHardware(token=your_token, backend="ibm_fez", shots=1024) >>> runner.create_circuits(circuit=circuit, measure_qubits=[0, 1]) >>> runner.setup_circuits() >>> job_id = runner.run() >>> status = runner.status(job_id=job_id) 'DONE' >>> results = runner.get_results(job_id=job_id) >>> circuit.execute(qubits=[0, 1]) [0.40313749, 0.09695667, 0.09692287, 0.40298296] >>> print(results) [0.40813749, 0.09195667, 0.10892288, 0.39098296]
It should be noted that the IBM Quantum backends have a maximum limit of 10,000,000 circuit executions per job submission. Users should ensure that the product of the number of circuits and shots does not exceed this limit to avoid job submission errors.
- class NoisyCircuits.RunOnHardware.RunOnHardware(token: str | None = None, backend: str | None = None, shots: int | None = None)[source]
Bases:
objectA class to run quantum circuits on IBM Quantum Hardware.
- Parameters:
token (str) – IBM Quantum token.
backend (str) – Backend name.
shots (int) – Number of shots.
- Raises:
TypeError: –
When the token, backend or shots are not of the expected type -> (str, str, int) respectively.
ValueError: –
When the token, backend or shots are not provided. - When the shots are less than or equal to zero. - When the backend is not available.
- cancel(job_id: str | None = None) None[source]
Method to cancel the submitted job.
- Parameters:
job_id (str, optional) – The Job Id for the submitted batch of PUBs. If not provided, uses the last submitted job_id provided the class object is not destroyed.
- Raises:
TypeError – When the job_id is not of the expected type -> str.
ValueError – When the job_id is not provided and the class object is destroyed.
- create_circuits(circuit: QuantumCircuit | None = None, measure_qubits: list[int] | None = None) None[source]
Method that generates the fully decomposed circuit list in qiskit for IBM Hardware execution.
- Parameters:
circuit (NoisyCircuit.QuantumCircuit.QuantumCircuit) – The quantum circuit to add in qiskit version.
measure_qubits (list[int]) – The list of qubits to measure.
- Raises:
TypeError: –
When the circuit is not of the expected type -> NoisyCircuits.QuantumCircuit. - When the measure_qubits is not of the expected type -> list. - When the elements of measure_qubits are not of the expected type -> int.
ValueError: –
When the circuit is not provided. - When the circuit is empty. - When the measure_qubits contain invalid qubit indices. - When the circuit contains gates that are not in the backend’s basis gates.
- get_results(job_id: str | None = None) list[ndarray][source]
Method that retrieves the results of the submitted job.
- Parameters:
job_id (str, optional) – The Job Id for the submitted batch of PUBs. If not provided, uses the last submitted job_id provided the class object is not destroyed.
- Returns:
The results of the submitted job. The list contains probabilities of the quantum circuit and the ordering is as inputted into the PUBs.
- Return type:
list[np.ndarray]
- Raises:
TypeError – When the job_id is not of the expected type -> str.
ValueError – When the job_id is not provided and the class object is destroyed.
- run() str[source]
Method that submits the generated PUB for execution on the IBM Hardware.
- Returns:
Returns the Job Id for the submitted batch of PUBs for retrieval.
- Return type:
str
- Raises:
ValueError: – When the maximum circuit execution limit of 10,000,000 is exceeded.
- setup_circuits() None[source]
Method that generates the PUB for the circuit list for execution on the IBM Hardware.
- status(job_id: str | None = None) str[source]
Method that retrieves the status of the submitted job.
- Parameters:
job_id (str, optional) – The Job Id for the submitted batch of PUBs. If not provided, uses the last submitted job_id provided the class object is not destroyed.
- Returns:
The status of the submitted job.
- Return type:
str
- Raises:
TypeError – When the job_id is not of the expected type -> str.
ValueError – When the job_id is not provided and the class object is destroyed.