Simulators#
Simulators for running circuits locally.
Contains a state-vector simulator able to return the resulting state vector after running a circuit on an all-zero initialized basis state. Supports both the little- and big-endian conventions.
Operation-Generation Module#
- class CFuncInfo(function_definition, cython_qubit_args, cython_header)[source]#
Bases:
NamedTuple
- class OpInfo(function_definition, cython_header, cython_function)[source]#
Bases:
NamedTuple
- binary(i: int, n: int) Tuple[int, ...] [source]#
Return the bitstring of i (with n total bits) as a tuple.
- compile_gate(gate_matrix: ndarray | List[List[float | Value]], skip_identity: bool = True, skip_zeros: bool = False) Tuple[Set[int], Dict[int, List[Tuple[int, float | Value]]]] [source]#
Compile a given gate into a minimal set of instructions.
- Parameters:
gate_matrix – Square matrix representing the gate. Values may be scalars or C variables that refer to floats.
skip_identity – If true, rows of given matrix that are equivalent to the identity operation are ignored.
skip_zeros – If true, rows of given matrix that have no values will be ignored.
- Returns:
A 3-tuple where the first item is the set of “output” substates affected by the gate at each iteration, the second item is the set of “input” sub states that the output depends on at each iteration, and the third item is a dictionary mapping output substates to corresponding multiplication instructions.
- generate_op(op_name: str, num_targets: int, num_controls: int, sparse_gate_mask: ndarray | None = None, precompile_gate: ndarray | None = None, collect_norm: bool = False) OpInfo [source]#
Generate C code with cython wrapper for a fast implementation of the given gate specification. See
generate_op_c_code()
for more detail.This will generate both little and big-endian versions of the gate.
- Parameters:
op_name – A name for the gate which will be used in the names of the C functions.
num_targets – Number of targets (non-control qubits) of the gate.
num_controls – Number of controls in the gate.
sparse_gate_mask – A 2-d binary matrix. If the entry is 0, the entry will not be used when applying this gate. This can improve performance, as some multiplications/adds can be skipped.
precompile_gate – A 2-d gate matrix. If supplied, this values of the matrix will be inserted into the C code, which may save time when doing the matrix multiplication.
little_endian – If true, use little-endian convention for the basis states. Otherwise, use big-endian.
collect_norm –
- If true, the given gate will be applied, and then the sum of the norms
(square magnitudes) of the affected sub states will be collected and returned by the generated function.
- Returns:
C code/cython wrapper that implement the gate.
- generate_op_c_code(op_name: str, num_targets: int, num_controls: int, sparse_gate_mask: ndarray | None = None, precompile_gate: ndarray | None = None, little_endian: bool = True, collect_norm: bool = False, provide_amplitude_scale_factor: bool = False) CFuncInfo [source]#
Generate the C code (and some cython headers) that implements a gate in an optimal way.
The main algorithm is to iterate (in parallel) over all basis states of the qubits unaffected by the gate. At each iteration, create a “basis template”, essentially a bit string of the basis state with gaps for each of the affected qubits. Then, depending on if the affected qubits are targets or controls, the full basis states that we need to select to do the matrix operation are generated.
This is done with bitwise operations. We start by looping from 0 to 2^(num unaffected qubits). The iteration variable (partial_basis) is equal to the basis state of all unaffected qubits (but with no gaps for affected qubits in the bits), and then for each affected qubit shifting the upper bits over one and ORing back in the lower bits.
Example: 5 qubits, gate affects qubits 1 and 3, and partial_basis is 6
mask0 = 0b00001 is for qubit 1, mask1 = 0b00111 is for qubit 3
start with basis_template = partial_basis = 6 = 0b00110
we want to create a basis_template 1x1x0 (where xs will be set to zero so we can fill in later)
- first iteration:
- basis_template = ((basis_template & ~mask0) << 1) | (basis_template & mask0)
= ((0b00110 & 0b11110) << 1) | (0b00110 & 0b0001) = 0b01100 | 0b00000 = 0b01100
- second iteration:
- basis_template = ((basis_template & ~mask1) << 1) | (basis_template & mask1)
= ((0b01100 & 0b11000) << 1) | (0b01100 & 0b00111) = 0b10000 | 0b00100 = 0b10100
now basis_template can be used to generate all the indices needed to apply the gate while holding unaffected qubits fixed to one basis state.
- Parameters:
op_name – A name for the gate which will be used in the names of the C functions.
num_targets – Number of targets (non-control qubits) of the gate.
num_controls – Number of controls in the gate.
sparse_gate_mask – A 2-d binary matrix. If the entry is 0, the entry will not be used when applying this gate. This can improve performance, as some multiplications/adds can be skipped.
precompile_gate – A 2-d gate matrix. If supplied, this values of the matrix will be inserted into the C code, which may save time when doing the matrix multiplication.
little_endian – If true, use little-endian convention for the basis states. Otherwise, use big-endian.
collect_norm –
- If true, the given gate will be applied, and then the sum of the norms
(square magnitudes) of the affected sub states will be collected and returned by the generated function.
provide_amplitude_scale_factor – If true, the returned C/cython functions will have an “amplitude factor” argument that will scale multiply all the affected amplitudes of the operation. Useful for normalizing the state after measurement/noise.
- Returns:
C code/cython headers that implement the gate.
- generate_op_set(op_name: str, num_targets: int, num_controls: int, operators: List[ndarray]) OpInfo [source]#
Generate C code and cython wrapper for a fast implementation of the given set of operators. These should follow the rule (where {E_k} is the set of operators)
sum_k E_k^{dagger} E_k = I.
The operators are applied one by one, and the norm of the resulting state is computed. Then, one of the operators is chosen probabilistically according to the relative probabilities as determined by the norms. Finally, this operator is applied to the state (if apply_operator is set to True). The index of the chosen operator is returned.
This should be useful both for generating measurement operations as well as simulating noise with Kraus operators.
See
generate_op_c_code()
for more detail of how each operator is generated/compiled.This will generate both little and big-endian versions of the operator sets.
- Parameters:
op_name – A name for the gate which will be used in the names of the C functions.
num_targets – Number of targets (non-control qubits) of the gate.
num_controls – Number of controls for the operator set.
little_endian – If true, use little-endian convention for the basis states. Otherwise, use big-endian.
operators – The list of operators in the form of numpy arrays.
- Returns:
C code/cython wrapper that implement the gate.
Simulator Module#
- sample_qubit(qubit: int, state: ndarray[Any, dtype[_ScalarType_co]], rng: Generator, collapse_state: bool = True, little_endian: bool = False) int #
Sample a single qubit.
- Parameters:
qubit – The qubit index that is measured.
state – The state to sample from.
rng – Random number generator to use for measuring in the computational basis.
collapse_state – Whether to collapse the state after measuring.
little_endian – If true, return the state vector using little-endian indexing for the qubits. Otherwise use big-endian.
- Returns:
The measurement sample (0 or 1).
- Return type:
- simulate(circuit: Circuit, mixed_state: bool = False, little_endian: bool = False, rng_seed: int | None = None) None #
Simulate the given circuit with either a state vector or density matrix simulation.
The resulting state is stored in the circuit object, together with the measured value in the classical register.
- Parameters:
circuit – The circuit to simulate.
mixed_state – If true, use the full density matrix method to simulate the circuit. Otherwise, simulate using the state vector method.
little_endian – If true, return the state vector using little-endian indexing for the qubits. Otherwise use big-endian.