mimiqcircuits package#

class mimiqcircuits.AbstractAnnotation[source]#

Bases: Operation


Abstract base class for annotations in quantum circuits.

This class is used as a base for defining various annotation types, such as Detector, QubitCoordinates, ShiftCoordinates,`Tick`, ObservableInclude, which provide metadata or structural information for quantum circuits.

class mimiqcircuits.AbstractMeasurement[source]#

Bases: Operation

iswrapper()[source]#
class mimiqcircuits.AbstractOperator[source]#

Bases: Operation

Supertype for all N-qubit operators.

Note that objects of type AbstractOperator do not need to be unitary.

Operators can be used to define Kraus channels (noise) (see krauschannel), or to compute expectation values (see ExpectationValue). However, they will return an error if directly applied to states.

See also

matrix(), isunitary()

evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

static isunitary()[source]#

Check if the object is unitary.

This static method returns False for all instances, indicating that the object is not unitary by default.

Returns:

Always returns False.

Return type:

bool

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

matrix()[source]#

Compute the matrix representation of the operator.

This method returns a symengine Matrix object representing the operator. It simplifies the matrix expression and evaluates it to a floating-point precision.

Returns:

The matrix representation of the operator.

Return type:

symengine.Matrix

opname()[source]#
power(n)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

unwrappedmatrix()[source]#

Compute the matrix representation with all parameters evaluated.

This method returns the matrix representation of the operator with all symbolic parameters substituted with their numerical values. If any parameter cannot be evaluated to a numerical value, a ValueError is raised.

Returns:

The evaluated matrix representation of the operator.

Return type:

symengine.Matrix

Raises:

ValueError – If a parameter cannot be evaluated to a numerical value.

class mimiqcircuits.Amplitude(bs: BitString)[source]#

Bases: Operation

Amplitude operation

multi qubit Amplitude operation in the computational basis

The operation projects the quantum states complex variables and stores in a z-register.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(Amplitude(BitString(2)),0)
0-qubit circuit with 1 instructions:
└── Amplitude(bs"00") @ z[0]
inverse()[source]#
iswrapper()[source]#
property zregsizes#
class mimiqcircuits.AmplitudeDamping(gamma)[source]#

Bases: krauschannel

One-qubit amplitude damping noise channel.

The amplitude damping channel is defined by the Kraus operators .. seealso:: GeneralizedAmplitudeDamping()

Kraus Matrices representation:

\[\begin{split}\operatorname E_1 = \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1-\gamma} \end{pmatrix} ,\quad \operatorname E_2 = \begin{pmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \end{pmatrix},\end{split}\]
Parameters:

gammagamma in [0,1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(AmplitudeDamping(0.1), 0)
1-qubit circuit with 1 instructions:
└── AmplitudeDamping(0.1) @ q[0]
evaluate(d={})[source]#
iswrapper()[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

property opname#
property parnames#
class mimiqcircuits.AsciiCanvas(width=None)[source]#

Bases: object

draw_box(row, col, width, height, clean=False)[source]#
draw_double_hline(row, col, width)[source]#
draw_double_vline(row, col, height)[source]#
draw_empty(row, col, width, height)[source]#
draw_fill(char, row, col, width, height)[source]#
draw_hline(row, col, width)[source]#
draw_text(text, row, col)[source]#
draw_vline(row, col, height)[source]#
draw_vtext(text, row, col)[source]#
get_cols()[source]#
get_rows()[source]#
push_line()[source]#
reset()[source]#
class mimiqcircuits.AsciiCircuit(width=None)[source]#

Bases: object

draw_barrier(barrier, qubits, bits, zvars)[source]#
draw_control(operation, qubits, _)[source]#
draw_ifstatement(g, qubits, bits, zvars)[source]#
draw_instruction(instruction)[source]#
draw_operation(operation, qubits, bits=None, zvars=None)[source]#
draw_parallel(parallel, qubits, _, zvars)[source]#
draw_paulistring(g, qubits, bits, zvars)[source]#
draw_reset(reset, qubits, _, zvars)[source]#
draw_wires(qubits, bits, zvars)[source]#
get_bit_row()[source]#
get_current_col()[source]#
get_qubit_row(qubit)[source]#
get_zvars_row()[source]#
reset()[source]#
set_current_col(col)[source]#
class mimiqcircuits.Barrier(*args)[source]#

Bases: Operation

Barrier operation.

A barrier is a special operation that does not affect the quantum state or the execution of a circuit, but it prevents compression or optimization operation from being applied across it.

Examples

Adding Barrier operation to the Circuit (The args can be: range, list, tuple, set or int)

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Barrier(1), 1)
2-qubit circuit with 1 instructions:
└── Barrier @ q[1]
>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Barrier(1), range(0,4))
4-qubit circuit with 4 instructions:
├── Barrier @ q[0]
├── Barrier @ q[1]
├── Barrier @ q[2]
└── Barrier @ q[3]

Adding Barrier to the circuit as a multi-qubits gate

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Barrier(5),1,2,3,4,5)
6-qubit circuit with 1 instructions:
└── Barrier @ q[1,2,3,4,5]
asciiwidth(qubits, bits, zvars)[source]#
control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.BitString(arg, indices=None)[source]#

Bases: object

BitString for the quantum states.

Representation of the quantum state of a quantum register with definite values for each qubit.

Examples

Initialization:

>>> from mimiqcircuits import *
>>> from bitarray import bitarray
>>> BitString(16) # number of qubits
bs"0000000000000000"
>>> BitString('10101') # binary string
bs"10101"
>>> BitString([1,0,0,0,1]) # binary string
bs"10001"
>>> BitString((1,0,0,0,1)) # binary string
bs"10001"
>>> BitString(bitarray('101010')) # bitarray
bs"101010"

Other initializations:

>>> BitString.fromnonzeros(16, [1, 3, 5, 7, 9, 11, 13, 15])
bs"0101010101010101"
>>> BitString.fromfunction(16, lambda i: i % 2 == 1)
bs"0101010101010101"
>>> BitString.fromstring('10101')
bs"10101"
>>> BitString.fromint(16, 21)
bs"1010100000000000"
>>> BitString.fromint(16, 21, 'little')
bs"0000000000010101"

Accessing the bits:

>>> bs = BitString(16)
>>> bs[0] # get the 0th bit
0
>>> bs[0:4] # get the first 4 bits
bs"0000"

Bitwise operations:

>>> bs1 = BitString('10101')
>>> bs2 = BitString('11100')
>>> bs1 | bs2 # OR
bs"11101"
>>> bs1 & bs2 # AND
bs"10100"
>>> bs1 ^ bs2 # XOR
bs"01001"
>>> ~bs1 # NOT
bs"01010"
>>> bs1 << 2 # left shift
bs"10100"
>>> bs1 >> 2 # right shift
bs"00101"

Other operations:

>>> bs1 + bs2 # concatenation
bs"1010111100"
>>> bs1 * 2 # repetition
bs"1010110101"
property bits#
static fromfunction(num_qubits: int, f: type[~mimiqcircuits.bitstrings.BitString.<lambda>])[source]#

Initialize a BitString from a function.

Parameters:
  • num_qubits (int) – The number of qubits in the BitString.

  • f (function) – A function that takes an integer and returns a boolean.

Returns:

A BitString.

static fromint(num_qubits: int, integer: int, endianess: str = 'big')[source]#

Initialize a BitString from an integer.

Parameters:
  • num_qubits (int) – The number of qubits in the BitString.

  • integer (int) – The integer value of the BitString.

  • endianess (str) – The endianess of the integer. Default is ‘big’.

Returns:

A BitString.

static fromnonzeros(num_qubits: int, nonzeros: list)[source]#

Initialize a BitString with specific non-zero qubits.

Parameters:
  • num_qubits (int) – The number of qubits in the BitString.

  • nonzeros (list) – A list of non-zero qubit indices to set in the BitString.

Returns:

A BitString with the specified non-zero qubits.

static fromstring(bitstring: str)[source]#

Initialize a BitString from a string.

Parameters:

bitstring (str) – The string representation of the BitString.

Returns:

A BitString.

nonzeros()[source]#

Return the indices of the non-zero qubits.

num_qubits()[source]#

Return the number of qubits in the BitString.

to01(endianess='big')[source]#

Return the binary string representation of the BitString.

Parameters:

endianess (str) – The endianess of the integer. Default is ‘big’

Retruns:

The binary string representation of the BitString.

toindex(endianess: str = 'big')[source]#

Return the integer index of the BitString.

Parameters:

endianess (str) – The endianess of the integer. Default is ‘big’.

Returns:

The integer index of the BitString.

tointeger(endianess: str = 'big')[source]#

Return the integer value of the BitString.

Parameters:

endianess (str) – The endianess of the integer. Default is ‘big’.

Returns:

The integer value of the BitString.

zeros()[source]#

Return the indices of the zero qubits.

class mimiqcircuits.BondDim[source]#

Bases: Operation

Operation to get the bond dimension between two halves of the system and store it in a z-register.

The bond dimension is only defined for a matrix-product state (MPS), which can be written as:

State Representation

\[|\psi \rangle = \sum_{s_1,s_2,\ldots=1}^2 \sum_{i_1}^{\chi_1} \sum_{i_2}^{\chi_2} \ldots \sum_{i_N}^{\chi_N} A^{(s_1)}_{i_0i_1} A^{(s_2)}_{i_1 i_2} A^{(s_3)}_{i_2 i_3} \ldots A^{(s_N)}_{i_{N-1}i_N} | s_1, s_2, s_3, \ldots, s_N \rangle .\]

Here, \(\chi_k\) is the bond dimension, i.e., the dimension of the index \(i_k\). The first and last bond dimensions are dummies, \(\chi_0=\chi_N=1\). A bond dimension of 1 means there is no entanglement between the two halves of the system.

Examples

When pushing to a circuit, the qubit index k that we give will return the bond dimension \(i_{k-1}\) in the above notation. In other words, we associate link k with qubit k+1. For k=1, the bond dimension returned will always be 1.

>>> from mimiqcircuits import *
>>> k = 5
>>> c = Circuit()
>>> c.push(BondDim(), k, 1)
6-qubit circuit with 1 instructions:
└── BondDim @ q[5], z[1]
control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
property num_qubits#
property parnames#
power(p)[source]#
class mimiqcircuits.Circuit(instructions=None)[source]#

Bases: object

Representation of a quantum circuit.

Operation can be added one by one to a circuit with the c.push(operation, targets...) function

Parameters:

instructions (list of Instruction) – Instructiuons to add at construction.

Raises:

TypeError – If initialization list contains non-Instruction objects.

Examples

>>> from mimiqcircuits import *
>>> from symengine import pi

Create a new circuit object

>>> c = Circuit()

Add a GateX (Pauli-X) gate on qubit 0

>>> c.push(GateX(), 0)
1-qubit circuit with 1 instructions:
└── X @ q[0]

Add a Controlled-NOT (CX) gate with control qubit 0 and target qubit 1

>>> c.push(GateCX(), 0, 1)
2-qubit circuit with 2 instructions:
├── X @ q[0]
└── CX @ q[0], q[1]

Add a Parametric GateRX gate with parameters pi/4

>>> c.push(GateRX(pi / 4),0)
2-qubit circuit with 3 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
└── RX((1/4)*pi) @ q[0]

Add a Reset gate on qubit 0

>>> c.push(Reset(), 0)
2-qubit circuit with 4 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── RX((1/4)*pi) @ q[0]
└── Reset @ q[0]

Add a Barrier gate on qubits 0 and 1

>>> c.push(Barrier(2), 0, 1)
2-qubit circuit with 5 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── RX((1/4)*pi) @ q[0]
├── Reset @ q[0]
└── Barrier @ q[0,1]

Add a Measurement gate on qubit 0, storing the result in bit 0.

>>> c.push(Measure(), 0, 0)
2-qubit circuit with 6 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── RX((1/4)*pi) @ q[0]
├── Reset @ q[0]
├── Barrier @ q[0,1]
└── M @ q[0], c[0]

Add a Control gate with GateX as the target gate. The first 3 qubits are the control qubits.

>>> c.push(Control(3, GateX()), 0, 1, 2, 3)
4-qubit circuit with 7 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── RX((1/4)*pi) @ q[0]
├── Reset @ q[0]
├── Barrier @ q[0,1]
├── M @ q[0], c[0]
└── C₃X @ q[0,1,2], q[3]

Add a 3-qubit Parallel gate with GateX

>>> c.push(Parallel(3,GateX()),0, 1, 2)
4-qubit circuit with 8 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── RX((1/4)*pi) @ q[0]
├── Reset @ q[0]
├── Barrier @ q[0,1]
├── M @ q[0], c[0]
├── C₃X @ q[0,1,2], q[3]
└── ⨷ ³ X @ q[0], q[1], q[2]

To add operations without constructing them first, use the c.emplace(…) function.

Available operations#

Gates

Single qubit gates

GateX() GateY() GateZ() GateH() GateS() GateSDG() GateT() GateTDG() GateSX() GateSXDG() GateID()

Single qubit gates (parametric)

GateU() GateP() GateRX() GateRY() GateRZ() GateP()

Two qubit gates

GateCX() GateCY() GateCZ() GateCH() GateSWAP() GateISWAP() GateCS() GateCSX() GateECR() GateDCX()

Two qubit gates (parametric)

GateCU() GateCP() GateCRX() GateCRY() GateCRZ() GateRXX() GateRYY() GateRZZ() GateXXplusYY() GateXXminusYY()

Other

GateCustom()

No-ops

Barrier()

Non-unitary operations

Measure() Reset()

Composite operations

Control() Parallel()

Power & Inverse operations

Power() Inverse()

Generalized gates

QFT() PhaseGradient()

add_noise(g: Operation | List[Operation], kraus: krauschannel | Gate | List[Gate] | List[krauschannel], before: bool | List[bool] = False, parallel: bool | List[bool] = False)[source]#

Adds a noise operation kraus to every instance of the operation g in the circuit.

The noise operation kraus can be a Kraus channel or a gate and will act on the same qubits as the operation g to which it is being added.

The operations g and kraus must act on the same number of qubits.

Parameters:
  • g (Operation or list of Operation) – The operation(s) to which the noise will be added.

  • kraus (krauschannel or list of krauschannel) – The noise operation(s) to be added.

  • before (bool or list of bool, optional) – If True, the noise is added before the operation. Default is False.

  • parallel (bool or list of bool, optional) – If True, noise is added as a block. Default is False.

Raises:
  • ValueError – If g and kraus are not of the same length, or if their number of qubits differ.

  • TypeError – If before or parallel are not a bool or a list of bool.

Returns:

The modified circuit with the noise added.

Return type:

Circuit

Examples:

Adding noise sequentially (not parallel):

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GateH(), [1,2,3])
4-qubit circuit with 3 instructions:
├── H @ q[1]
├── H @ q[2]
└── H @ q[3]

>>> c.add_noise(GateH(), AmplitudeDamping(0.2))
4-qubit circuit with 6 instructions:
├── H @ q[1]
├── AmplitudeDamping(0.2) @ q[1]
├── H @ q[2]
├── AmplitudeDamping(0.2) @ q[2]
├── H @ q[3]
└── AmplitudeDamping(0.2) @ q[3]

Adding noise in parallel:

>>> c = Circuit()
>>> c.push(GateH(), [1, 2, 3])
4-qubit circuit with 3 instructions:
├── H @ q[1]
├── H @ q[2]
└── H @ q[3]

>>> c.add_noise(GateH(), AmplitudeDamping(0.2), parallel=True)
4-qubit circuit with 6 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── AmplitudeDamping(0.2) @ q[1]
├── AmplitudeDamping(0.2) @ q[2]
└── AmplitudeDamping(0.2) @ q[3]

Parallel will not work if gates aren’t transversal.

>>> c = Circuit()
>>> c.push(GateCZ(), 1, range(2,5))
5-qubit circuit with 3 instructions:
├── CZ @ q[1], q[2]
├── CZ @ q[1], q[3]
└── CZ @ q[1], q[4]

>>> c.add_noise(GateCZ(), Depolarizing2(0.1), parallel=True)
5-qubit circuit with 6 instructions:
├── CZ @ q[1], q[2]
├── Depolarizing(0.1) @ q[1,2]
├── CZ @ q[1], q[3]
├── Depolarizing(0.1) @ q[1,3]
├── CZ @ q[1], q[4]
└── Depolarizing(0.1) @ q[1,4]

Adding noise before measurement (The before=True option is mostly used for Measure):

>>> c = Circuit()
>>> c.push(Measure(), [1, 2, 3], [1, 2, 3])
4-qubit circuit with 3 instructions:
├── M @ q[1], c[1]
├── M @ q[2], c[2]
└── M @ q[3], c[3]

>>> c.add_noise(Measure(), PauliX(0.1), before=True)
4-qubit circuit with 6 instructions:
├── PauliX(0.1) @ q[1]
├── M @ q[1], c[1]
├── PauliX(0.1) @ q[2]
├── M @ q[2], c[2]
├── PauliX(0.1) @ q[3]
└── M @ q[3], c[3]

Adding unitary gates as noise in the same way:

>>> c = Circuit()
>>> c.push(GateH(), [1, 2, 3])
4-qubit circuit with 3 instructions:
├── H @ q[1]
├── H @ q[2]
└── H @ q[3]

>>> c.add_noise(GateH(), GateRX(0.01))
4-qubit circuit with 6 instructions:
├── H @ q[1]
├── RX(0.01) @ q[1]
├── H @ q[2]
├── RX(0.01) @ q[2]
├── H @ q[3]
└── RX(0.01) @ q[3]
add_noise_to_gate_parallel(g, kraus: krauschannel | Gate, before=False)[source]#

Adds a block of noise operations kraus after/before every block of a given operation g.

The function identifies blocks of consecutive transversal operations of type g and adds a block of transversal noise operations kraus after each such block. The noise operation kraus can be a Kraus channel or a gate and will act on the same qubits as the operation g to which it is being added.

Parameters:
  • g (Operation) – The operation to which the noise will be added.

  • kraus (Operation) – The noise operation to be added, which can be a Kraus channel or any valid gate.

  • before (bool, optional) – If True, the noise is added before the operation g. Default is False.

Raises:

ValueError – If the noise operation is the same as the operation g, to avoid recursion.

See also

add_noise(): Adds noise to multiple operations at once or in a parallel block.

add_noise_to_gate_single(g, kraus: krauschannel | Gate, before=False)[source]#

Adds a noise operation kraus before or after every instance of a given operation g.

The noise operation kraus can be a Kraus channel or a gate and will act on the same qubits as the operation g to which it is being added.

Parameters:
  • g (Operation) – The operation to which the noise will be added.

  • kraus (Operation) – The noise operation to be added, which can be a Kraus channel or any valid gate.

  • before (bool, optional) – If True, the noise is added before the operation g. Default is False.

Raises:

ValueError – If the noise operation is the same as the operation g, to avoid recursion.

See also

add_noise(): Adds noise to multiple operations at once or in a parallel block.

append(other)[source]#

Appends all the gates of the given circuit at the end of the current circuit.

Parameters:

other (Circuit) – the circuit to append.

copy()[source]#
decompose()[source]#

Decompose all the gates in the circuit.

If applied multiple times, will reduce the circuit to a basis set of U and CX gates.

deepcopy()[source]#
depth()[source]#

Computes the depth of the quantum circuit.

draw()[source]#

Draws the entire quantum circuit on the ASCII canvas and handles the layout of various quantum operations.

This method iterates through all instructions in the circuit, determines the required width for each operation, and delegates the drawing of each operation to the appropriate specialized method based on the operation type. If an operation’s width exceeds the available space in the current row of the canvas, the canvas is printed and reset to continue drawing from a new starting point.

The method manages different operation types including control, measurement, reset, barrier, parallel, and conditional (if) operations using specific drawing methods from the AsciiCircuit class.

Raises:
  • TypeError – If any item in the circuit’s instructions is not an instance of Instruction.

  • ValueError – If an operation cannot be drawn because it exceeds the available canvas width even after a reset.

Prints:

The current state of the ASCII canvas, either incrementally after each operation if space runs out, or entirely at the end of processing all instructions.

Returns:

None

emplace(op, *regs)[source]#

Constructs and adds an Operation to the end of the circuit.

It is useful to add to the circuit operations that are dependent on the number of qubits.

Parameters:
  • operation (Type subclass of Operation) – the type of operation to add.

  • args (vararg of list) – A variable number of arguments compriseing a list of parameters (if the operation is parametric), one list of qubits for each quantum register, and one list of bits of every classical register supported.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.emplace(GateX(), [0])
1-qubit circuit with 1 instructions:
└── X @ q[0]

>>> c.emplace(GateRX(0.2), [0])
1-qubit circuit with 2 instructions:
├── X @ q[0]
└── RX(0.2) @ q[0]

>>> c.emplace(QFT(), range(10))
10-qubit circuit with 3 instructions:
├── X @ q[0]
├── RX(0.2) @ q[0]
└── QFT @ q[0,1,2,3,4,5,6,7,8,9]
empty()[source]#

Checks if the circuit is empty.

evaluate(d)[source]#
get_on_qubits(target_qubits)[source]#

Get instructions that involve the specified target qubits.

Parameters:

target_qubits (list or int) – Qubits for which to retrieve instructions.

Returns:

A new Circuit containing only the instructions that involve the specified qubits.

Return type:

Circuit

insert(index: int, operation, *args)[source]#

Inserts an operation or another circuit at a specific index in the circuit.

Parameters:
  • index (int) – The index at which the operation should be inserted.

  • operation (Operation or Instruction) – the quantum operation to add.

  • args (integers or iterables) – Target qubits and bits for the operation (not instruction), given as variable number of arguments.

Raises:
  • TypeError – If operation is not an Operation object.

  • ValueError – If the number of arguments is incorrect or the target qubits specified are invalid.

Examples

Inserting an operation to the specify index of the circuit

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(GateX(), 0)
1-qubit circuit with 1 instructions:
└── X @ q[0]

>>> c.push(GateCX(),0,1)
2-qubit circuit with 2 instructions:
├── X @ q[0]
└── CX @ q[0], q[1]

>>> c.insert(1, GateH(), 0)
2-qubit circuit with 3 instructions:
├── X @ q[0]
├── H @ q[0]
└── CX @ q[0], q[1]
inverse()[source]#

Returns the inverse of the circuit.

is_symbolic()[source]#

Check whether the circuit contains any symbolic (unevaluated) parameters.

This method examines each instruction in the circuit to determine if any parameter remains symbolic (i.e., unevaluated). It recursively checks through each instruction and its nested operations, if any.

Returns:

True if any parameter is symbolic (unevaluated), False if all parameters are fully evaluated.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> x, y = symbols("x y")
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]

>>> c.is_symbolic()
False
>>> c.push(GateP(x), 0)
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── P(x) @ q[0]

>>> c.is_symbolic()
True
>>> c = c.evaluate({x: 1, y: 2})
>>> c
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── P(1) @ q[0]

>>> c.is_symbolic()
False
static loadproto(file)[source]#

Loads a circuit from a protobuf (binary) file.

Parameters:

filename (str) – The name of the file to load the circuit from.

Returns:

The circuit loaded from the file.

Return type:

Circuit

Note

Look for example in Circuit.saveproto()

num_bits()[source]#

Returns the number of bits in the circuit.

num_qubits()[source]#

Returns the number of qubits in the circuit.

num_zvars()[source]#

Returns the number of z-variables in the circuit.

push(operation, *args)[source]#

Adds an Operation or an Instruction to the end of the circuit.

Parameters:
  • operation (Operation or Instruction) – the quantum operation to add.

  • args (integers or iterables) – Target qubits and bits for the operation (not instruction), given as variable number of arguments.

Raises:
  • TypeError – If operation is not an Operation object.

  • ValueError – If the number of arguments is incorrect or the target qubits specified are invalid.

Examples

Adding multiple operations to the Circuit (The args can be integers or integer-valued iterables)

>>> from mimiqcircuits import *
>>> from symengine import pi
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]

>>> c.push(GateT(), 0)
1-qubit circuit with 2 instructions:
├── H @ q[0]
└── T @ q[0]

>>> c.push(GateH(), [0,2])
3-qubit circuit with 4 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
└── H @ q[2]

>>> c.push(GateS(), 0)
3-qubit circuit with 5 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
└── S @ q[0]

>>> c.push(GateCX(), [2, 0], 1)
3-qubit circuit with 7 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
├── S @ q[0]
├── CX @ q[2], q[1]
└── CX @ q[0], q[1]

>>> c.push(GateH(), 0)
3-qubit circuit with 8 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
├── S @ q[0]
├── CX @ q[2], q[1]
├── CX @ q[0], q[1]
└── H @ q[0]

>>> c.push(Barrier(3), *range(3)) # equivalent to c.push(Barrier(3), 0, 1, 2)
3-qubit circuit with 9 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
├── S @ q[0]
├── CX @ q[2], q[1]
├── CX @ q[0], q[1]
├── H @ q[0]
└── Barrier @ q[0,1,2]

>>> c.push(Measure(), range(3), range(3))
3-qubit circuit with 12 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
├── S @ q[0]
├── CX @ q[2], q[1]
├── CX @ q[0], q[1]
├── H @ q[0]
├── Barrier @ q[0,1,2]
├── M @ q[0], c[0]
├── M @ q[1], c[1]
└── M @ q[2], c[2]

>>> c
3-qubit circuit with 12 instructions:
├── H @ q[0]
├── T @ q[0]
├── H @ q[0]
├── H @ q[2]
├── S @ q[0]
├── CX @ q[2], q[1]
├── CX @ q[0], q[1]
├── H @ q[0]
├── Barrier @ q[0,1,2]
├── M @ q[0], c[0]
├── M @ q[1], c[1]
└── M @ q[2], c[2]
remove(index: int)[source]#

Removes an instruction at a specific index from the circuit.

Parameters:

index (int) – The index of the gate to remove.

Raises:

IndexError – If index is out of range.

sample_mixedunitaries(rng=None, ids=False)[source]#

sample_mixedunitaries(rng=None, ids=False)

Samples one unitary gate for each mixed unitary Kraus channel in the circuit.

This is possible because for mixed unitary noise channels, the probabilities of each Kraus operator are fixed (state-independent).

Note: This function is internally called (before applying any gate) when executing a circuit with noise using trajectories. It can also be used to generate samples of circuits without running them.

Parameters:
  • rng (optional) – Random number generator. If not provided, Python’s default random number generator is used.

  • ids (optional) – Boolean, default=False. Determines whether to include identity Kraus operators in the sampled circuit. If True, identity gates are added to the circuit; otherwise, they are omitted. Usually, most selected Kraus operators will be identity gates.

Returns:

A copy of the circuit with every mixed unitary Kraus channel replaced by one of the unitary gates of the channel. Identity gates are omitted unless ids=True.

Return type:

Circuit

Examples

Gates and non-mixed-unitary Kraus channels remain unchanged:

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GateH(), [1, 2, 3])
4-qubit circuit with 3 instructions:
├── H @ q[1]
├── H @ q[2]
└── H @ q[3]

>>> c.push(Depolarizing1(0.5), [1, 2, 3])
4-qubit circuit with 6 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── Depolarizing(0.5) @ q[1]
├── Depolarizing(0.5) @ q[2]
└── Depolarizing(0.5) @ q[3]

>>> c.push(AmplitudeDamping(0.5), [1, 2, 3])
4-qubit circuit with 9 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── Depolarizing(0.5) @ q[1]
├── Depolarizing(0.5) @ q[2]
├── Depolarizing(0.5) @ q[3]
├── AmplitudeDamping(0.5) @ q[1]
├── AmplitudeDamping(0.5) @ q[2]
└── AmplitudeDamping(0.5) @ q[3]
>>> rng = random.Random(42)
>>> new_circuit = c.sample_mixedunitaries(rng=rng, ids=True)
>>> print(new_circuit)
4-qubit circuit with 9 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── X @ q[1]
├── I @ q[2]
├── I @ q[3]
├── AmplitudeDamping(0.5) @ q[1]
├── AmplitudeDamping(0.5) @ q[2]
└── AmplitudeDamping(0.5) @ q[3]

By default, identities are not included:

>>> new_circuit = c.sample_mixedunitaries(rng=rng)
>>> print(new_circuit)
4-qubit circuit with 8 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── Y @ q[2]
├── Y @ q[3]
├── AmplitudeDamping(0.5) @ q[1]
├── AmplitudeDamping(0.5) @ q[2]
└── AmplitudeDamping(0.5) @ q[3]

Different calls to the function generate different results:

>>> new_circuit = c.sample_mixedunitaries(rng=rng)
>>> print(new_circuit)
4-qubit circuit with 7 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── Z @ q[1]
├── AmplitudeDamping(0.5) @ q[1]
├── AmplitudeDamping(0.5) @ q[2]
└── AmplitudeDamping(0.5) @ q[3]
>>> new_circuit = c.sample_mixedunitaries(rng=rng)
>>> print(new_circuit)
4-qubit circuit with 7 instructions:
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── X @ q[3]
├── AmplitudeDamping(0.5) @ q[1]
├── AmplitudeDamping(0.5) @ q[2]
└── AmplitudeDamping(0.5) @ q[3]
saveproto(file)[source]#

Saves the circuit as a protobuf (binary) file.

Parameters:

filename (str) – The name of the file to save the circuit to.

Returns:

The number of bytes written to the file.

Return type:

int

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> import tempfile
>>> x, y = symbols("x y")
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]

>>> c.push(GateXXplusYY(x**2, y),0,1)
2-qubit circuit with 2 instructions:
├── H @ q[0]
└── XXplusYY(x**2, y) @ q[0,1]

>>> c.push(Measure(),0,0)
2-qubit circuit with 3 instructions:
├── H @ q[0]
├── XXplusYY(x**2, y) @ q[0,1]
└── M @ q[0], c[0]

>>> tmpfile = tempfile.NamedTemporaryFile(suffix=".pb", delete=True)
>>> c.saveproto(tmpfile.name)
64
>>> c.loadproto(tmpfile.name)
2-qubit circuit with 3 instructions:
├── H @ q[0]
├── XXplusYY(x**2, y) @ q[0,1]
└── M @ q[0], c[0]
Note:

This example uses a temporary file to demonstrate the save and load functionality. You can save your file with any name at any location using:

c.saveproto("example.pb")
c.loadproto("example.pb")
specify_operations()[source]#

Summarizes the types and numbers of operations in the circuit.

This function inspects each instruction in the circuit and categorizes it by the number of qubits, bits, and z-variables involved in the operation. It then prints a summary of the total number of operations in the circuit and a breakdown of the number of operations grouped by their type.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()

Add a Pauli-X (GateX) gate on qubit 0

>>> c.push(GateX(), 0)
1-qubit circuit with 1 instructions:
└── X @ q[0]

Add a Controlled-NOT (CX) gate with control qubit 0 and target qubit 1

>>> c.push(GateCX(), 0, 1)
2-qubit circuit with 2 instructions:
├── X @ q[0]
└── CX @ q[0], q[1]

Add a Measurement operation on qubit 0, storing the result in bit 0

>>> c.push(Measure(), 0, 0)
2-qubit circuit with 3 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
└── M @ q[0], c[0]

Add an ExpectationValue operation with GateX on qubit 1, storing the result in z-variable 2.

>>> c.push(ExpectationValue(GateX()), 1, 2)
2-qubit circuit with 4 instructions:
├── X @ q[0]
├── CX @ q[0], q[1]
├── M @ q[0], c[0]
└── ⟨X⟩ @ q[1], z[2]

Print a summary of the types and numbers of operations

>>> c.specify_operations()
Total number of operations: 4
├── 1 x 1_qubits
├── 1 x 2_qubits
├── 1 x 1_qubits & 1_bits
└── 1 x 1_qubits & 1_zvars
class mimiqcircuits.Control(num_controls, operation, *args, **kwargs)[source]#

Bases: Gate

Control operation.

A Control is a special operation that applies multi-control gates to the Circuit at once.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(Control(3,GateX()),1,2,3,4)
5-qubit circuit with 1 instructions:
└── C₃X @ q[1,2,3], q[4]

>>> Control(2, GateX()).matrix()
[1.0, 0, 0, 0, 0, 0, 0, 0]
[0, 1.0, 0, 0, 0, 0, 0, 0]
[0, 0, 1.0, 0, 0, 0, 0, 0]
[0, 0, 0, 1.0, 0, 0, 0, 0]
[0, 0, 0, 0, 1.0, 0, 0, 0]
[0, 0, 0, 0, 0, 1.0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1.0]
[0, 0, 0, 0, 0, 0, 1.0, 0]
control(*args)[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
get_operation()[source]#
getparams()[source]#
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

property num_controls#
property num_targets#
property op#
parallel(*args)[source]#
power(*args)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.Delay(t)[source]#

Bases: Gate

1-qubit delay gate

This gate is equivalent to a GateID gate, except that it is parametrized by a time parameter t. The parameter does not affect the action of the gate. The only purpose of this gate is to act as a placeholder to indicate idle noise, in which case the parameter t can later be used to further specify the noise properties.

The gate can be created by calling Delay(t) where t is a number.

Matrix representation:

\[\begin{split}\operatorname{Delay}(t) = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}\end{split}\]

Examples

>>> Delay(0.3)
Delay(0.3)
>>> delay_gate = Delay(0.1)
>>> delay_gate.matrix()
[1.0, 0]
[0, 1.0]
>>> c = Circuit()
>>> c.push(delay_gate, 0)
1-qubit circuit with 1 instructions:
└── Delay(0.1) @ q[0]

Decomposition

>>> delay_gate = Delay(0.2)
>>> delay_gate.decompose()
1-qubit circuit with 1 instructions:
└── ID @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

power(_)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.Depolarizing(N, p)[source]#

Bases: krauschannel

N-qubit depolarizing noise channel.

The Kraus operators for the depolarizing channel are given by:

\[E_1 = \sqrt{1-p} I_N, \quad E_i = \sqrt{p/(4^N-1)} P_i,\]

where \(p \in [0,1]\) is a probability, and \(P_i\) is an \(N\)-qubit Pauli string operator, i.e., a tensor product of one-qubit Pauli operators.

There is exactly one Kraus operator \(E_{i>1}\) for each distinct combination of Pauli operators \(P_i\), except for the \(N\)-qubit identity \(I_N = I \otimes I \otimes I \otimes \dots\).

For example:

  • For one qubit, we have 3 operators \(P_i \in \{X, Y, Z\}\).

  • For two qubits, we have 15 operators \(P_i \in \{ I\otimes X, I\otimes Y, I\otimes Z, X\otimes I, Y\otimes I, Z\otimes I, X\otimes X, X\otimes Y, X\otimes Z, Y\otimes X, Y\otimes Y, Y\otimes Z, Z\otimes X, Z\otimes Y, Z\otimes Z \}\).

This channel is a mixed unitary channel (see ismixedunitary()), and is a special case of PauliNoise.

Parameters:
  • N (int) – Number of qubits.

  • p (float) – Probability of error, i.e., the probability of not applying the identity.

Examples

Depolarizing channels can be defined for any number of qubits:

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(Depolarizing(1, 0.1), 1)
2-qubit circuit with 1 instructions:
└── Depolarizing(0.1) @ q[1]
>>> c.push(Depolarizing(5, 0.1), 1, 2, 3, 4, 5)
6-qubit circuit with 2 instructions:
├── Depolarizing(0.1) @ q[1]
└── Depolarizing(0.1) @ q[1,2,3,4,5]

For one and two qubits, you can use the shorthand notation:

>>> c.push(Depolarizing(1, 0.1), 1)
6-qubit circuit with 3 instructions:
├── Depolarizing(0.1) @ q[1]
├── Depolarizing(0.1) @ q[1,2,3,4,5]
└── Depolarizing(0.1) @ q[1]
>>> c.push(Depolarizing(2, 0.1), 1, 2)
6-qubit circuit with 4 instructions:
├── Depolarizing(0.1) @ q[1]
├── Depolarizing(0.1) @ q[1,2,3,4,5]
├── Depolarizing(0.1) @ q[1]
└── Depolarizing(0.1) @ q[1,2]
evaluate(d={})[source]#
classmethod ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
iswrapper()[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

property num_qubits#
property parnames#
probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
class mimiqcircuits.Depolarizing1(p)[source]#

Bases: krauschannel

class mimiqcircuits.Depolarizing2(p)[source]#

Bases: krauschannel

class mimiqcircuits.Detector(*args)[source]#

Bases: AbstractAnnotation

Detector operation.#

An annotation class representing a detector in a quantum circuit.

The Detector operation monitors the parity of measurement results over N classical bits, where the parity should remain deterministic under ideal, noiseless execution. The Detector ensures that the combined parity (even or odd) of a set of measurement results is consistent. If noise or errors disrupt the circuit, the Detector can identify this through unexpected changes in parity, signaling potential measurement errors. This functionality helps in error detection by revealing inconsistencies caused by disturbances.

Examples

Adding Detector operation to a Circuit (applied to classical bits):

>>> from mimiqcircuits import *
>>> op = Detector([0.1,0.9])
>>> op
lazy Detector(?, [0.1, 0.9])

Fill the Lazy argument by calling the number of bits

>>> op_filled = op(1)
>>> op_filled
Detector(0.1, 0.9)
>>> op_filled.num_bits
1

Getting the nots

>>> op_filled.get_notes()
[0.1, 0.9]

Define a new Operation

>>> op = Detector(1, [0.5, 1.0])
>>> op.get_notes()
[0.5, 1.0]

Add to the Circuit

>>> c = Circuit()
>>> c.push(Detector(1), 0)
0-qubit circuit with 1 instructions:
└── Detector() @ c[0]

>>> c.push(Detector(1, [0.5, 1.0]), 1)
0-qubit circuit with 2 instructions:
├── Detector() @ c[0]
└── Detector(0.5, 1.0) @ c[1]
get_notes()[source]#
iswrapper()[source]#
static opname()[source]#
class mimiqcircuits.DiagonalOp(a, b)[source]#

Bases: AbstractOperator

One-qubit diagonal operator.

The corresponding matrix is given by:

Matrix Representation

\[\begin{split}\begin{pmatrix} a & 0 \\ 0 & b \end{pmatrix}\end{split}\]

where a and b are complex numbers.

Parameters:
  • a (complex) – The top-left entry of the diagonal matrix.

  • b (complex) – The bottom-right entry of the diagonal matrix.

Examples

>>> from mimiqcircuits import *
>>> op = DiagonalOp(1, 0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(DiagonalOp(1, 0.5)), 1, 2)
2-qubit circuit with 1 instructions:
└── ⟨D(1, 0.5)⟩ @ q[1], z[2]
opsquared()[source]#

Return the operator with each parameter squared.

property parnames#
rescale(scale)[source]#

Return a new rescaled DiagonalOp operator.

rescale_inplace(scale)[source]#

In-place rescaling of the DiagonalOp operator.

class mimiqcircuits.Diffusion(*args)[source]#

Bases: Gate

Grover’s diffusion operator.

Parameters:

num_qubits (int) – The number of qubits.

Raises:

ValueError – If the number of qubits is not an integer or less than 1.

Returns:

Grover’s diffusion operator.

Return type:

Diffusion

num_qubits#

The number of qubits for the diffusion operator.

Type:

int

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(Diffusion(2), 1, 2)
3-qubit circuit with 1 instructions:
└── Diffusion @ q[1,2]
class mimiqcircuits.ExpectationValue(op: AbstractOperator)[source]#

Bases: Operation

Operation to compute and store the expectation value of an Operator in a z-register.

An expectation value for a pure state \(| \psi \rangle\) is defined as:

Expectation Value for Pure State

\[\langle O \rangle = \langle \psi | O | \psi \rangle\]

where \(O\) is an operator. With respect to a density matrix \(\rho\), it’s given by:

Expectation Value for Density Matrix

\[\langle O \rangle = \mathrm{Tr}(\rho O).\]

However, when using quantum trajectories to solve noisy circuits, the expectation value is computed with respect to the pure state of each trajectory.

The argument op can be any gate or non-unitary operator.

Note

ExpectationValue is currently restricted to one and two qubit operators.

Examples

In push!, the first argument corresponds to the qubit, and the second to the z-register.

>>> from mimiqcircuits import *
>>> ExpectationValue(GateX())
⟨X⟩
>>> c = Circuit()
>>> c.push(ExpectationValue(GateX()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨X⟩ @ q[1], z[1]
>>> c.push(ExpectationValue(SigmaPlus()), 1, 2)
2-qubit circuit with 2 instructions:
├── ⟨X⟩ @ q[1], z[1]
└── ⟨SigmaPlus(1)⟩ @ q[1], z[2]
asciiwidth(qubits, bits=[], zvars=[])[source]#

Calculate the width for ASCII drawing.

property cregsizes#
get_operation()[source]#
inverse()[source]#
isunitary()[source]#
iswrapper()[source]#
opname()[source]#
power(_)[source]#
property qregsizes#
property zregsizes#
class mimiqcircuits.Gate[source]#

Bases: AbstractOperator

control(*args)[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

parallel(*args)[source]#
power(*args)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateC3X[source]#

Bases: Control

Four qubit Controlled-Controlled-Controlled-X gate.

By convention, the first three qubits are the controls and the fourth is the target

Examples

>>> from mimiqcircuits import *
>>> GateC3X(), GateC3X().num_controls, GateC3X().num_targets, GateC3X().num_qubits
(C₃X, 3, 1, 4)
>>> GateC3X().matrix()
[1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0]

>>> c = Circuit().push(GateC3X(), 0, 1, 2, 3)
>>> c
4-qubit circuit with 1 instructions:
└── C₃X @ q[0,1,2], q[3]

>>> GateC3X().power(2), GateC3X().inverse()
(C₃ID, C₃X)
>>> GateC3X().decompose()
4-qubit circuit with 31 instructions:
├── H @ q[3]
├── P((1/8)*pi) @ q[0]
├── P((1/8)*pi) @ q[1]
├── P((1/8)*pi) @ q[2]
├── P((1/8)*pi) @ q[3]
├── CX @ q[0], q[1]
├── P((-1/8)*pi) @ q[1]
├── CX @ q[0], q[1]
├── CX @ q[1], q[2]
├── P((-1/8)*pi) @ q[2]
├── CX @ q[0], q[2]
├── P((1/8)*pi) @ q[2]
├── CX @ q[1], q[2]
├── P((-1/8)*pi) @ q[2]
├── CX @ q[0], q[2]
├── CX @ q[2], q[3]
├── P((-1/8)*pi) @ q[3]
├── CX @ q[1], q[3]
├── P((1/8)*pi) @ q[3]
⋮   ⋮
└── H @ q[3]
class mimiqcircuits.GateCCP(*args, **kwargs)[source]#

Bases: Control

Three qubit Controlled-Controlled-Phase gate.

By convention, the first two qubits are the controls and the third is the target

Parameters:

lmbda – Phase angle.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lmbda')
>>> GateCCP(lmbda), GateCCP(lmbda).num_controls, GateCCP(lmbda).num_targets, GateCCP(lmbda).num_qubits
(C₂P(lmbda), 2, 1, 3)
>>> GateCCP(lmbda).matrix()
[1.0, 0, 0, 0, 0, 0, 0, 0]
[0, 1.0, 0, 0, 0, 0, 0, 0]
[0, 0, 1.0, 0, 0, 0, 0, 0]
[0, 0, 0, 1.0, 0, 0, 0, 0]
[0, 0, 0, 0, 1.0, 0, 0, 0]
[0, 0, 0, 0, 0, 1.0, 0, 0]
[0, 0, 0, 0, 0, 0, 1.0, 0]
[0, 0, 0, 0, 0, 0, 0, exp(I*lmbda)]

>>> c = Circuit().push(GateCCP(lmbda), 0, 1, 2)
>>> c
3-qubit circuit with 1 instructions:
└── C₂P(lmbda) @ q[0,1], q[2]

>>> GateCCP(lmbda).power(2), GateCCP(lmbda).inverse()
(C₂P(2*lmbda), C₂P(-lmbda))
>>> GateCCP(lmbda).decompose()
3-qubit circuit with 5 instructions:
├── CP((1/2)*lmbda) @ q[1], q[2]
├── CX @ q[0], q[1]
├── CP((-1/2)*lmbda) @ q[1], q[2]
├── CX @ q[0], q[1]
└── CP((1/2)*lmbda) @ q[0], q[2]
class mimiqcircuits.GateCCX[source]#

Bases: Control

Three qubit Controlled-Controlled-X gate.

By convention, the first two qubits are the controls and the third is the target.

Examples

>>> from mimiqcircuits import *
>>> GateCCX(), GateCCX().num_controls, GateCCX().num_targets, GateCCX().num_qubits
(C₂X, 2, 1, 3)
>>> GateCCX().matrix()
[1.0, 0, 0, 0, 0, 0, 0, 0]
[0, 1.0, 0, 0, 0, 0, 0, 0]
[0, 0, 1.0, 0, 0, 0, 0, 0]
[0, 0, 0, 1.0, 0, 0, 0, 0]
[0, 0, 0, 0, 1.0, 0, 0, 0]
[0, 0, 0, 0, 0, 1.0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1.0]
[0, 0, 0, 0, 0, 0, 1.0, 0]

>>> c = Circuit().push(GateCCX(), 0, 1, 2)
>>> c
3-qubit circuit with 1 instructions:
└── C₂X @ q[0,1], q[2]

>>> GateCCX().power(2), GateCCX().inverse()
(C₂ID, C₂X)
>>> GateCCX().decompose()
3-qubit circuit with 15 instructions:
├── H @ q[2]
├── CX @ q[1], q[2]
├── T† @ q[2]
├── CX @ q[0], q[2]
├── T @ q[2]
├── CX @ q[1], q[2]
├── T† @ q[2]
├── CX @ q[0], q[2]
├── T @ q[1]
├── T @ q[2]
├── H @ q[2]
├── CX @ q[0], q[1]
├── T @ q[0]
├── T† @ q[1]
└── CX @ q[0], q[1]
class mimiqcircuits.GateCH[source]#

Bases: Control

Two qubit Controlled-Hadamard gate.

By convention, the first qubit is the control and the second is the target

Matrix representation:

\[\begin{split}\operatorname{CH} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ 0 & 0 & \frac{1}{\sqrt{2}} & -\frac{1}{\sqrt{2}} \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCH(), GateCH().num_controls, GateCH().num_targets, GateCH().num_qubits
(CH, 1, 1, 2)
>>> GateCH().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 0.707106781186548, 0.707106781186548]
[0, 0, 0.707106781186548, -0.707106781186548]

>>> c = Circuit().push(GateCH(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CH @ q[0], q[1]

>>> GateCH().power(2), GateCH().inverse()
(CID, CH)
>>> GateCH().decompose()
2-qubit circuit with 7 instructions:
├── S @ q[1]
├── H @ q[1]
├── T @ q[1]
├── CX @ q[0], q[1]
├── T† @ q[1]
├── H @ q[1]
└── S† @ q[1]
class mimiqcircuits.GateCP(*args, **kwargs)[source]#

Bases: Control

Two qubit Controlled-Phase gate.

By convention, the first qubit is the control and the second is the target

See Also GateP()

Matrix representation:

\[\begin{split}\operatorname{CP}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\lambda} \end{pmatrix}\end{split}\]
Parameters:

lambda – Phase angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lambda')
>>> GateCP(lmbda), GateCP(lmbda).num_controls, GateCP(lmbda).num_targets, GateCP(lmbda).num_qubits
(CP(lambda), 1, 1, 2)
>>> GateCP(lmbda).matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 1.0, 0]
[0, 0, 0, exp(I*lambda)]

>>> c = Circuit().push(GateCP(lmbda), 10, 11)
>>> c
12-qubit circuit with 1 instructions:
└── CP(lambda) @ q[10], q[11]

>>> GateCP(lmbda).decompose()
2-qubit circuit with 5 instructions:
├── P((1/2)*lambda) @ q[0]
├── CX @ q[0], q[1]
├── P((-1/2)*lambda) @ q[1]
├── CX @ q[0], q[1]
└── P((1/2)*lambda) @ q[1]
class mimiqcircuits.GateCRX(*args, **kwargs)[source]#

Bases: Control

Two qubit Controlled-RX gate.

By convention, the first qubit is the control and the second is the target

See Also GateRX(), GateCRY(), GateCRZ()

Matrix representation:

\[\begin{split}\operatorname{CRX}(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} \\ 0 & 0 & -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}\end{split}\]
Parameters:

theta – The rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateCRZ(theta), GateCRZ(theta).num_controls, GateCRZ(theta).num_targets, GateCRZ(theta).num_qubits
(CRZ(theta), 1, 1, 2)
>>> GateCRZ(theta).matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, exp(-1/2*I*theta), 0]
[0, 0, 0, exp(1/2*I*theta)]

>>> c = Circuit().push(GateCRZ(theta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CRZ(theta) @ q[0], q[1]

>>> GateCRZ(theta).power(2), GateCRZ(theta).inverse()
(CRZ(2*theta), CRZ(-theta))
>>> GateCRZ(theta).decompose()
2-qubit circuit with 4 instructions:
├── RZ((1/2)*theta) @ q[1]
├── CX @ q[0], q[1]
├── RZ((-1/2)*theta) @ q[1]
└── CX @ q[0], q[1]
class mimiqcircuits.GateCRY(*args, **kwargs)[source]#

Bases: Control

Two qubit Controlled-RY gate.

By convention, the first qubit is the control and the second is the target

See Also GateRY(), GateCRX(), GateCRZ()

Matrix representation:

\[\begin{split}\operatorname{CRY}(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos\frac{\theta}{2} & -\sin\frac{\theta}{2} \\ 0 & 0 & \sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}\end{split}\]
Parameters:

theta – The rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateCRY(theta), GateCRY(theta).num_controls, GateCRY(theta).num_targets, GateCRY(theta).num_qubits
(CRY(theta), 1, 1, 2)
>>> GateCRY(theta).matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, cos((1/2)*theta), -sin((1/2)*theta)]
[0, 0, sin((1/2)*theta), cos((1/2)*theta)]

>>> c = Circuit().push(GateCRY(theta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CRY(theta) @ q[0], q[1]

>>> GateCRY(theta).power(2), GateCRY(theta).inverse()
(CRY(2*theta), CRY(-theta))
>>> GateCRY(theta).decompose()
2-qubit circuit with 4 instructions:
├── RY((1/2)*theta) @ q[1]
├── CX @ q[0], q[1]
├── RY((-1/2)*theta) @ q[1]
└── CX @ q[0], q[1]
class mimiqcircuits.GateCRZ(*args, **kwargs)[source]#

Bases: Control

Two qubit Controlled-RZ gate.

By convention, the first qubit is the control and the second is the target

See Also GateRZ(), GateCRX(), GateCRY()

Matrix representation:

\[\begin{split}\operatorname{CRZ}(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{-i\frac{\lambda}{2}} & 0 \\ 0 & 0 & 0 & e^{i\frac{\lambda}{2}} \end{pmatrix}\end{split}\]
Parameters:

theta – The rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lambda')
>>> GateCRZ(lmbda), GateCRZ(lmbda).num_controls, GateCRZ(lmbda).num_targets, GateCRZ(lmbda).num_qubits
(CRZ(lambda), 1, 1, 2)
>>> GateCRZ(lmbda).matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, exp(-1/2*I*lambda), 0]
[0, 0, 0, exp(1/2*I*lambda)]

>>> c = Circuit().push(GateCRZ(lmbda), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CRZ(lambda) @ q[0], q[1]

>>> GateCRZ(lmbda).power(2), GateCRZ(lmbda).inverse()
(CRZ(2*lambda), CRZ(-lambda))
>>> GateCRZ(lmbda).decompose()
2-qubit circuit with 4 instructions:
├── RZ((1/2)*lambda) @ q[1]
├── CX @ q[0], q[1]
├── RZ((-1/2)*lambda) @ q[1]
└── CX @ q[0], q[1]
class mimiqcircuits.GateCS[source]#

Bases: Control

Two qubit Controlled-S gate.

By convention, the first qubit is the control and the second is the target

See Also GateS()

Matrix representation::

\[\begin{split}\operatorname{CS} =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCS(), GateCS().num_controls, GateCS().num_targets, GateCS().num_qubits
(CS, 1, 1, 2)
>>> GateCS().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 1.0, 0]
[0, 0, 0, 0.0 + 1.0*I]

>>> c = Circuit().push(GateCS(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CS @ q[0], q[1]

>>> GateCS().power(2), GateCS().inverse()
(CZ, CS†)
>>> GateCS().decompose()
2-qubit circuit with 1 instructions:
└── CP((1/2)*pi) @ q[0], q[1]
class mimiqcircuits.GateCSDG[source]#

Bases: Control

Adjoint of two qubit Controlled-S gate.

By convention, the first qubit is the control and the second is the target

Matrix representation:

\[\begin{split}\operatorname{CS}^{\dagger} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCSDG(), GateCSDG().num_controls, GateCSDG().num_targets, GateCSDG().num_qubits
(CS†, 1, 1, 2)
>>> GateCSDG().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 1.0, 0]
[0, 0, 0, 6.12323399573677e-17 - 1.0*I]

>>> c = Circuit().push(GateCSDG(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CS† @ q[0], q[1]

>>> GateCSDG().power(2), GateCSDG().inverse()
(C(S†**2), CS)
>>> GateCSDG().decompose()
2-qubit circuit with 1 instructions:
└── CP((-1/2)*pi) @ q[0], q[1]
class mimiqcircuits.GateCSWAP[source]#

Bases: Control

Three qubit Controlled-SWAP gate.

By convention, the first qubit is the control and last two are the targets.

Examples

>>> from mimiqcircuits import *
>>> GateCSWAP(), GateCSWAP().num_controls, GateCSWAP().num_targets, GateCSWAP().num_qubits
(CSWAP, 1, 2, 3)
>>> GateCSWAP().matrix()
[1.0, 0, 0, 0, 0, 0, 0, 0]
[0, 1.0, 0, 0, 0, 0, 0, 0]
[0, 0, 1.0, 0, 0, 0, 0, 0]
[0, 0, 0, 1.0, 0, 0, 0, 0]
[0, 0, 0, 0, 1.0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1.0, 0]
[0, 0, 0, 0, 0, 1.0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1.0]

>>> c = Circuit().push(GateCSWAP(), 0, 1, 2)
>>> GateCSWAP().power(2), GateCSWAP().inverse()
(C(⨷ ² ID), CSWAP)
>>> c = Circuit().push(GateCSWAP(), 0, 1, 2)
>>> c
3-qubit circuit with 1 instructions:
└── CSWAP @ q[0], q[1,2]

>>> GateCSWAP().power(2), GateCSWAP().inverse()
(C(⨷ ² ID), CSWAP)
>>> GateCSWAP().decompose()
3-qubit circuit with 3 instructions:
├── CX @ q[2], q[1]
├── C₂X @ q[0,1], q[2]
└── CX @ q[2], q[1]
class mimiqcircuits.GateCSX[source]#

Bases: Control

Two qubit Controled-SX gate.

By convention, the first qubit is the control and second one is the targets.

Matrix representation:

\[\begin{split}\operatorname{CSX} =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1+i}{2} & \frac{1-i}{2} \\ 0 & 0 & \frac{1-i}{2} & \frac{1+i}{2} \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCSX(), GateCSX().num_controls, GateCSX().num_targets, GateCSX().num_qubits
(CSX, 1, 1, 2)
>>> GateCSX().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 0.5 + 0.5*I, 0.5 - 0.5*I]
[0, 0, 0.5 - 0.5*I, 0.5 + 0.5*I]

>>> c = Circuit().push(GateCSX(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CSX @ q[0], q[1]

>>> GateCSX().power(2), GateCSX().inverse()
(CX, CSX†)
>>> GateCSX().decompose()
2-qubit circuit with 3 instructions:
├── H @ q[1]
├── CU1((1/2)*pi) @ q[0], q[1]
└── H @ q[1]
class mimiqcircuits.GateCSXDG[source]#

Bases: Control

Two qubit \({CSX}^\dagger\) gate.

Matrix representation:

\[\begin{split}\operatorname{CSX}^{\dagger} =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1-i}{2} & \frac{1+i}{2} \\ 0 & 0 & \frac{1+i}{2} & \frac{1-i}{2} \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCSXDG(), GateCSXDG().num_controls, GateCSXDG().num_targets, GateCSXDG().num_qubits
(CSX†, 1, 1, 2)
>>> GateCSXDG().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 0.5 - 0.5*I, 0.5 + 0.5*I]
[0, 0, 0.5 + 0.5*I, 0.5 - 0.5*I]

>>> c = Circuit().push(GateCSXDG(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CSX† @ q[0], q[1]

>>> GateCSXDG().power(2), GateCSXDG().inverse()
(C(SX†**2), CSX)
>>> GateCSXDG().decompose()
2-qubit circuit with 3 instructions:
├── H @ q[1]
├── CU1((-1/2)*pi) @ q[0], q[1]
└── H @ q[1]
class mimiqcircuits.GateCU(*args, **kwargs)[source]#

Bases: Control

Two qubit controlled unitary gate.

Matrix representation:

\[\begin{split}\operatorname{CU}(\theta, \phi, \lambda, \gamma) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{i\gamma} \cos\left(\frac{\theta}{2}\right) & -e^{i\gamma} e^{i\lambda}\sin\left(\frac{\theta}{2}\right) \\ 0 & 0 & e^{i\gamma} \mathrm{e}^{i\phi}\sin\left(\frac{\theta}{2}\right) & e^{i\gamma} \mathrm{e}^{i(\phi+\lambda)}\cos\left(\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]
Parameters:
  • theta (float) – Euler angle 1 in radians.

  • phi (float) – Euler angle 2 in radians.

  • lmbda (float) – Euler angle 3 in radians.

  • gamma (float) – Global phase of the CU gate.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, phi, lmbda, gamma = symbols('theta phi lambda gamma')
>>> GateCU(theta, phi, lmbda, gamma), GateCU(theta, phi, lmbda, gamma).num_controls, GateCU(theta, phi, lmbda, gamma).num_targets, GateCU(theta, phi, lmbda, gamma).num_qubits
(CU(theta, phi, lambda, gamma), 1, 1, 2)
>>> GateCU(theta, phi, lmbda, gamma).matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, exp(I*gamma)*cos((1/2)*theta), -exp(I*(gamma + lambda))*sin((1/2)*theta)]
[0, 0, exp(I*(gamma + phi))*sin((1/2)*theta), exp(I*(gamma + lambda + phi))*cos((1/2)*theta)]

>>> c = Circuit().push(GateCU(theta, phi, lmbda, gamma), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CU(theta, phi, lambda, gamma) @ q[0], q[1]

>>> GateCU(theta, phi, lmbda, gamma).power(2), GateCU(theta, phi, lmbda, gamma).inverse()
(C(U(theta, phi, lambda, gamma)**2), CU(-theta, -lambda, -phi, -gamma))
>>> GateCU(theta, phi, lmbda, gamma).decompose()
2-qubit circuit with 7 instructions:
├── P(gamma) @ q[0]
├── P((1/2)*(lambda + phi)) @ q[0]
├── P((1/2)*(lambda - phi)) @ q[1]
├── CX @ q[0], q[1]
├── U((-1/2)*theta, 0, (-1/2)*(lambda + phi), 0.0) @ q[1]
├── CX @ q[0], q[1]
└── U((1/2)*theta, phi, 0, 0.0) @ q[1]
class mimiqcircuits.GateCX[source]#

Bases: Control

Two qubit Controlled-X gate (or CNOT).

By convention, the first qubit is the control and the second is the target

Matrix representation:

\[\begin{split}\operatorname{CX} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCX(), GateCX().num_controls, GateCX().num_targets
(CX, 1, 1)
>>> GateCX().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 0, 1.0]
[0, 0, 1.0, 0]

>>> c = Circuit().push(GateCX(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CX @ q[0], q[1]

>>> GateCX().power(2), GateCX().inverse()
(CID, CX)
>>> GateCX().decompose()
2-qubit circuit with 1 instructions:
└── CX @ q[0], q[1]
class mimiqcircuits.GateCY[source]#

Bases: Control

Two qubit Controlled-Y gate.

By convention, the first qubit is the control and the second is the target

Matrix representation:

\[\begin{split}\operatorname{CY} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & -i \\ 0 & 0 & i & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCY(), GateCY().num_controls, GateCY().num_targets
(CY, 1, 1)
>>> GateCY().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 0, -0.0 - 1.0*I]
[0, 0, 0.0 + 1.0*I, 0]

>>> c = Circuit().push(GateCY(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CY @ q[0], q[1]

>>> GateCY().power(2), GateCY().inverse()
(CID, CY)
>>> GateCY().decompose()
2-qubit circuit with 3 instructions:
├── S† @ q[1]
├── CX @ q[0], q[1]
└── S @ q[1]
class mimiqcircuits.GateCZ[source]#

Bases: Control

Two qubit Controlled-Z gate.

By convention, the first qubit is the control and the second is the target

Matrix representation:

\[\begin{split}\operatorname{CZ} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateCZ(), GateCZ().num_controls, GateCZ().num_targets
(CZ, 1, 1)
>>> GateCZ().matrix()
[1.0, 0, 0, 0]
[0, 1.0, 0, 0]
[0, 0, 1.0, 0]
[0, 0, 0, -1.0]

>>> c = Circuit().push(GateCZ(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── CZ @ q[0], q[1]

>>> GateCZ().power(2), GateCZ().inverse()
(CID, CZ)
>>> GateCZ().decompose()
2-qubit circuit with 3 instructions:
├── H @ q[1]
├── CX @ q[0], q[1]
└── H @ q[1]
class mimiqcircuits.GateCall(decl: GateDecl, args: Tuple[float, ...])[source]#

Bases: Gate

decompose()[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
class mimiqcircuits.GateCustom(matrix)[source]#

Bases: Gate

One or Two qubit Custom gates.

Examples

>>> from mimiqcircuits import Circuit, GateCustom
>>> import numpy as np
>>> matrix = np.array([[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 0, -1j],[0, 0, 1j, 0]])
>>> c = Circuit()
>>> c.push(GateCustom(matrix), 0, 1)
2-qubit circuit with 1 instructions:
└── Custom([1.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 0.0*I]...[0.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 1.0*I, 0.0 + 0.0*I]) @ q[0,1]
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

static is_unitary(matrix, tol=1e-08)[source]#
property num_qubits#
pretty_print()[source]#
class mimiqcircuits.GateDCX[source]#

Bases: Gate

Two qubit double-CNOT gate.

A two qubit Clifford gate consisting of two back-to-back CNOTs with alternate controls.

Matrix representation:

\[\begin{split}\operatorname{DCX} =\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateDCX()
DCX
>>> GateDCX().matrix()
[1.0, 0, 0, 0]
[0, 0, 1.0, 0]
[0, 0, 0, 1.0]
[0, 1.0, 0, 0]

>>> c = Circuit().push(GateDCX(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── DCX @ q[0,1]

>>> GateDCX().power(2), GateDCX().inverse()
(DCX†, DCX†)
>>> GateDCX().decompose()
2-qubit circuit with 2 instructions:
├── CX @ q[0], q[1]
└── CX @ q[1], q[0]
class mimiqcircuits.GateDecl(name: str, arguments: Tuple[str, ...], circuit: Circuit)[source]#

Bases: object

Simple declaration of gates using the @gatedecl decorator.

Examples

First way

>>> from symengine import symbols
>>> from mimiqcircuits import *

Define symbols for the gate arguments

>>> x, y = symbols('x y')

Declare a gate using the @gatedecl decorator

>>> @gatedecl("ansatz")
... def ansatz(x):
...     c = Circuit()
...     c.push(GateX(), 0)
...     c.push(GateRX(x), 1)
...     return c

Create a GateDecl object using the decorator

>>> ansatz(x)
ansatz(x)
>>> ansatz(y)
ansatz(y)

Decompose

>>> ansatz(x).decompose()
2-qubit circuit with 2 instructions:
├── X @ q[0]
└── RX(x) @ q[1]
>>> ansatz(2).decompose()
2-qubit circuit with 2 instructions:
├── X @ q[0]
└── RX(2) @ q[1]

Second Way

>>> from symengine import *
>>> from mimiqcircuits import *

Define symbols for the gate arguments

>>> x, y = symbols('x y')

From a circuit, create a GateDecl object directly

>>> c = Circuit()
>>> c.push(GateXXplusYY(x,y), 0,1)
2-qubit circuit with 1 instructions:
└── XXplusYY(x, y) @ q[0,1]

>>> c.push(GateRX(x), 1)
2-qubit circuit with 2 instructions:
├── XXplusYY(x, y) @ q[0,1]
└── RX(x) @ q[1]

>>> gate_decl = GateDecl("ansatz", ('x','y'), c)
>>> gate_decl
gate ansatz(x, y) =
├── XXplusYY(x, y) @ q[0,1]
└── RX(x) @ q[1]

>>> GateCall(gate_decl, (2,4))
ansatz(2, 4)

Decompose the GateCall into a quantum circuit

>>> GateCall(gate_decl, (2,4)).decompose()
2-qubit circuit with 2 instructions:
├── XXplusYY(2, 4) @ q[0,1]
└── RX(2) @ q[1]

Add to Circuit

>>> g = GateCall(gate_decl, (2,4))
>>> c = Circuit()
>>> c.push(g,10,22)
23-qubit circuit with 1 instructions:
└── ansatz(2, 4) @ q[10,22]
property num_bits#
property num_qubits#
property num_zvars#
class mimiqcircuits.GateECR[source]#

Bases: Gate

Two qubit ECR (echo) gate.

Matrix representation:

\[\begin{split}\operatorname{ECR} =\begin{pmatrix} 0 & \frac{1}{\sqrt{2}} & 0 & \frac{i}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} & 0 & \frac{-i}{\sqrt{2}} & 0 \\ 0 & \frac{i}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}} \\ \frac{-i}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}} & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateECR()
ECR
>>> GateECR().matrix()
[0, 0, 0.707106781186548, 0.0 + 0.707106781186548*I]
[0, 0, 0.0 + 0.707106781186548*I, 0.707106781186548]
[0.707106781186548, -0.0 - 0.707106781186548*I, 0, 0]
[-0.0 - 0.707106781186548*I, 0.707106781186548, 0, 0]

>>> c = Circuit().push(GateECR(), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── ECR @ q[0,1]

>>> GateECR().power(2), GateECR().inverse()
(⨷ ² ID, ECR)
>>> GateECR().decompose()
2-qubit circuit with 3 instructions:
├── RZX((1/4)*pi) @ q[0,1]
├── X @ q[0]
└── RZX((-1/4)*pi) @ q[0,1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateH[source]#

Bases: Gate

Single qubit Hadamard gate.

Matrix representation:

\[\begin{split}\operatorname{H} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateH()
H
>>> GateH().matrix()
[0.707106781186548, 0.707106781186548]
[0.707106781186548, -0.707106781186548]

>>> c = Circuit().push(GateH(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── H @ q[0]

>>> GateH().power(2), GateH().inverse()
(ID, H)
>>> GateH().decompose()
1-qubit circuit with 1 instructions:
└── U((1/2)*pi, 0, pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateHXY[source]#

Bases: Gate

Single qubit HXY gate.

Matrix representation:

\[\begin{split}\operatorname{HXY} = \frac{1}{\sqrt{2}} \begin{pmatrix} 0 & 1 - i \\ 1 + i & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateHXY()
HXY
>>> GateHXY().matrix()
[0, 0.707106781186548 - 0.707106781186548*I]
[0.707106781186548 + 0.707106781186548*I, 0]

>>> c = Circuit().push(GateHXY(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── HXY @ q[0]

>>> GateHXY().power(2), GateHXY().inverse()
(HXY**2, HXY)
>>> GateHXY().decompose()
1-qubit circuit with 5 instructions:
├── H @ q[0]
├── Z @ q[0]
├── H @ q[0]
├── S @ q[0]
└── U(0, 0, 0, (-1/4)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateHXZ[source]#

Bases: Gate

Single qubit HXZ gate (alias for GateH).

Matrix representation:

\[\begin{split}\operatorname{H} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix}\end{split}\]

Examples

The HXZ gate behaves exactly like the Hadamard gate:

>>> from mimiqcircuits import *
>>> GateHXZ()
H
>>> GateHXZ().matrix()
[0.707106781186548, 0.707106781186548]
[0.707106781186548, -0.707106781186548]

Adding GateHXZ to a circuit:

>>> c = Circuit().push(GateHXZ(), 0)

Power and inverse of the gate:

>>> GateHXZ().power(2), GateHXZ().inverse()
(ID, H)

Decomposition of the gate:

>>> GateHXZ().decompose()
1-qubit circuit with 1 instructions:
└── U((1/2)*pi, 0, pi, 0.0) @ q[0]
class mimiqcircuits.GateHYZ[source]#

Bases: Gate

Single qubit HYZ gate.

Matrix representation:

\[\begin{split}\operatorname{HYZ} = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -i \\ i & -1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateHYZ()
HYZ
>>> GateHYZ().matrix()
[0.707106781186548, -0.0 - 0.707106781186548*I]
[0.0 + 0.707106781186548*I, -0.707106781186548]

>>> c = Circuit().push(GateHYZ(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── HYZ @ q[0]

>>> GateHYZ().power(2), GateHYZ().inverse()
(HYZ**2, HYZ)
>>> GateHYZ().decompose()
1-qubit circuit with 5 instructions:
├── H @ q[0]
├── S @ q[0]
├── H @ q[0]
├── Z @ q[0]
└── U(0, 0, 0, (-1/4)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateID[source]#

Bases: Gate

Single qubit Identity gate.

Matrix representation:

\[\begin{split}\operatorname{ID} = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateID()
ID
>>> GateID().matrix()
[1.0, 0]
[0, 1.0]

>>> c = Circuit().push(GateID(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── ID @ q[0]

>>> GateID().power(2), GateID().inverse()
(ID, ID)
>>> GateID().decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, 0, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isidentity()[source]#
class mimiqcircuits.GateISWAP[source]#

Bases: Gate

Two qubit ISWAP gate.

See Also GateISWAPDG() and GateSWAP()

Matrix representation:

\[\begin{split}\operatorname{ISWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateISWAP()
ISWAP
>>> GateISWAP().matrix()
[1.0, 0, 0, 0]
[0, 0, 0.0 + 1.0*I, 0]
[0, 0.0 + 1.0*I, 0, 0]
[0, 0, 0, 1.0]

>>> c = Circuit().push(GateISWAP(), 0, 1)
>>> GateISWAP().power(2), GateISWAP().inverse()
(ISWAP**2, ISWAP†)
>>> GateISWAP().decompose()
2-qubit circuit with 6 instructions:
├── S @ q[0]
├── S @ q[1]
├── H @ q[0]
├── CX @ q[0], q[1]
├── CX @ q[1], q[0]
└── H @ q[1]
class mimiqcircuits.GateP(lmbda)[source]#

Bases: Gate

Single qubit Phase gate.

Matrix representation:

\[\begin{split}\operatorname{P}(\lambda) = \operatorname{U}(0,0,\lambda) = \begin{pmatrix} 1 & 0 \\ 0 & \mathrm{e}^{i\lambda} \end{pmatrix}\end{split}\]
Parameters:

lambda – Phase angle

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lambda')
>>> GateP(lmbda)
P(lambda)
>>> GateP(lmbda).matrix()
[1.0, 0]
[0, exp(I*lambda)]

>>> c = Circuit().push(GateP(lmbda), 0)
>>> c
1-qubit circuit with 1 instructions:
└── P(lambda) @ q[0]

>>> GateP(lmbda).power(2), GateP(lmbda).inverse()
(P(2*lambda), P(-lambda))
>>> GateP(lmbda).decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, lambda, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateR(theta, phi)[source]#

Bases: Gate

Single qubit Rotation gate around the axis \(\cos(\phi)\hat{x} + \sin(\phi)\hat{y}\).

Matrix representation:

\[\begin{split}\operatorname R(\theta,\phi) = \begin{pmatrix} \cos \frac{\theta}{2} & -i e^{-i\phi} \sin \frac{\theta}{2} \\ -i e^{i \phi} \sin \frac{\theta}{2} & \cos \frac{\theta}{2} \end{pmatrix}\end{split}\]
Parameters:
  • theta (float) – The rotation angle in radians.

  • phi (float) – The axis of rotation in radians.

Example

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, phi = symbols('theta phi')
>>> GateR(theta, phi)
R(theta, phi)
>>> GateR(theta, phi).matrix()
[cos((1/2)*theta), -I*exp(-I*phi)*sin((1/2)*theta)]
[-I*exp(I*phi)*sin((1/2)*theta), cos((1/2)*theta)]

>>> c = Circuit().push(GateR(theta, phi), 0)
>>> GateR(theta, phi).power(2), GateR(theta, phi).inverse()
(R(2*theta, phi), R(-theta, phi))
>>> GateR(theta, phi).decompose()
1-qubit circuit with 1 instructions:
└── U3(theta, phi + (-1/2)*pi, -phi + (1/2)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateRX(theta)[source]#

Bases: Gate

Single qubit Rotation gate around the axis \(\hat{x}\)

Matrix representation:

\[\begin{split}\operatorname{RX}(\theta) = \begin{pmatrix} \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} \\ -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}\end{split}\]
Parameters:

theta – Rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRX(theta)
RX(theta)
>>> GateRX(theta).matrix()
[cos((1/2)*theta), -I*sin((1/2)*theta)]
[-I*sin((1/2)*theta), cos((1/2)*theta)]

>>> c = Circuit().push(GateRX(theta), 0)
>>> c
1-qubit circuit with 1 instructions:
└── RX(theta) @ q[0]

>>> GateRX(theta).power(2), GateRX(theta).inverse()
(RX(2*theta), RX(-theta))
>>> GateRX(theta).decompose()
1-qubit circuit with 1 instructions:
└── U(theta, (-1/2)*pi, (1/2)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isidentity()[source]#
class mimiqcircuits.GateRXX(theta)[source]#

Bases: Gate

Two qubit RXX gate (rotation about XX).

This gate is symmetric, and is maximally entangling at \((\theta = \frac{\pi}{2})\)

Matrix representation:

\[\begin{split}\operatorname{RXX}(\theta) =\begin{pmatrix} \cos(\frac{\theta}{2}) & 0 & 0 & -i\sin(\frac{\theta}{2}) \\ 0 & \cos(\frac{\theta}{2}) & -i\sin(\frac{\theta}{2}) & 0 \\ 0 & -i\sin(\frac{\theta}{2}) & \cos(\frac{\theta}{2}) & 0 \\ -i\sin(\frac{\theta}{2}) & 0 & 0 & \cos(\frac{\theta}{2}) \end{pmatrix}\end{split}\]
Parameters:

theta – The angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRXX(theta)
RXX(theta)
>>> c = Circuit()
>>> c.push(GateRXX(theta), 0, 1)
2-qubit circuit with 1 instructions:
└── RXX(theta) @ q[0,1]

>>> GateRXX(theta).power(2), GateRXX(theta).inverse()
(RXX(theta)**2, RXX(-theta))
>>> GateRXX(theta).matrix()
[cos((1/2)*theta), 0, 0, -I*sin((1/2)*theta)]
[0, cos((1/2)*theta), -I*sin((1/2)*theta), 0]
[0, -I*sin((1/2)*theta), cos((1/2)*theta), 0]
[-I*sin((1/2)*theta), 0, 0, cos((1/2)*theta)]

>>> GateRXX(theta).decompose()
2-qubit circuit with 7 instructions:
├── H @ q[0]
├── H @ q[1]
├── CX @ q[0], q[1]
├── RZ(theta) @ q[1]
├── CX @ q[0], q[1]
├── H @ q[1]
└── H @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateRY(theta)[source]#

Bases: Gate

Single qubit Rotation gate around the axis \(\hat{y}\)

Matrix representation:

\[\begin{split}\operatorname{RY}(\theta) = \begin{pmatrix} \cos\frac{\theta}{2} & -\sin\frac{\theta}{2} \\ \sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{pmatrix}\end{split}\]
Parameters:

theta (float) – Rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRY(theta)
RY(theta)
>>> GateRY(theta).matrix()
[cos((1/2)*theta), -sin((1/2)*theta)]
[sin((1/2)*theta), cos((1/2)*theta)]

>>> c = Circuit().push(GateRY(theta), 0)
>>> c
1-qubit circuit with 1 instructions:
└── RY(theta) @ q[0]

>>> GateRY(theta).power(2), GateRY(theta).inverse()
(RY(2*theta), RY(-theta))
>>> GateRY(theta).decompose()
1-qubit circuit with 1 instructions:
└── U(theta, 0, 0, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isidentity()[source]#
class mimiqcircuits.GateRYY(theta)[source]#

Bases: Gate

Two qubit RYY gate (rotation about YY).

This gate is symmetric, and is maximally entangling at \((\theta = \frac{\pi}{2})\)

Matrix representation:

\[\begin{split}\operatorname{RYY}(\theta) =\begin{pmatrix} \cos(\frac{\theta}{2}) & 0 & 0 & i\sin(\frac{\theta}{2}) \\ 0 & \cos(\frac{\theta}{2}) & -i\sin(\frac{\theta}{2}) & 0 \\ 0 & -i\sin(\frac{\theta}{2}) & \cos(\frac{\theta}{2}) & 0 \\ i\sin(\frac{\theta}{2}) & 0 & 0 & \cos(\frac{\theta}{2}) \end{pmatrix}\end{split}\]
Parameters:

theta (float) – The angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRYY(theta)
RYY(theta)
>>> GateRYY(theta).matrix()
[cos((1/2)*theta), 0, 0, I*sin((1/2)*theta)]
[0, cos((1/2)*theta), -I*sin((1/2)*theta), 0]
[0, -I*sin((1/2)*theta), cos((1/2)*theta), 0]
[I*sin((1/2)*theta), 0, 0, cos((1/2)*theta)]

>>> c = Circuit().push(GateRYY(theta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── RYY(theta) @ q[0,1]

>>> GateRYY(theta).power(2), GateRYY(theta).inverse()
(RYY(theta)**2, RYY(-theta))
>>> GateRYY(theta).decompose()
2-qubit circuit with 7 instructions:
├── RX((1/2)*pi) @ q[0]
├── RX((1/2)*pi) @ q[1]
├── CX @ q[0], q[1]
├── RZ(theta) @ q[1]
├── CX @ q[0], q[1]
├── RX((-1/2)*pi) @ q[0]
└── RX((-1/2)*pi) @ q[1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateRZ(lmbda)[source]#

Bases: Gate

Single qubit Rotation gate around the axis \(\hat{z}\)

Matrix representation:

\[\begin{split}\operatorname{RZ}(\lambda) = \begin{pmatrix} e^{-i\frac{\lambda}{2}} & 0 \\ 0 & e^{i\frac{\lambda}{2}} \end{pmatrix}\end{split}\]
Parameters:

lambda – Rotation angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lambda')
>>> GateRZ(lmbda)
RZ(lambda)
>>> GateRZ(lmbda).matrix()
[exp(-1/2*I*lambda), 0]
[0, exp(1/2*I*lambda)]

>>> c = Circuit().push(GateRZ(lmbda), 0)
>>> c
1-qubit circuit with 1 instructions:
└── RZ(lambda) @ q[0]

>>> GateRZ(lmbda).power(2), GateRZ(lmbda).inverse()
(RZ(2*lambda), RZ(-lambda))
>>> GateRZ(lmbda).decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, lambda, (-1/2)*lambda) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isidentity()[source]#
class mimiqcircuits.GateRZX(theta)[source]#

Bases: Gate

Two qubit RZX gate.

This gate is maximally entangling at \((\theta = \frac{\pi}{2})\)

Matrix representation:

\[\begin{split}\operatorname{RZX}(\theta) =\begin{pmatrix} \cos(\frac{\theta}{2}) & -i\sin(\frac{\theta}{2}) & 0 & 0 \\ -i\sin(\frac{\theta}{2}) & \cos(\frac{\theta}{2}) & 0 & 0 \\ 0 & 0 & \cos(\frac{\theta}{2}) & i\sin(\frac{\theta}{2}) \\ 0 & 0 & i\sin(\frac{\theta}{2}) & \cos(\frac{\theta}{2}) \end{pmatrix}\end{split}\]
Parameters:

theta (float) – The angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRZX(theta)
RZX(theta)
>>> GateRZX(theta).matrix()
[cos((1/2)*theta), -I*sin((1/2)*theta), 0, 0]
[-I*sin((1/2)*theta), cos((1/2)*theta), 0, 0]
[0, 0, cos((1/2)*theta), I*sin((1/2)*theta)]
[0, 0, I*sin((1/2)*theta), cos((1/2)*theta)]

>>> c = Circuit().push(GateRZX(theta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── RZX(theta) @ q[0,1]

>>> GateRZX(theta).power(2), GateRZX(theta).inverse()
(RZX(theta)**2, RZX(-theta))
>>> GateRZX(theta).decompose()
2-qubit circuit with 5 instructions:
├── H @ q[1]
├── CX @ q[0], q[1]
├── RZ(theta) @ q[1]
├── CX @ q[0], q[1]
└── H @ q[1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateRZZ(theta)[source]#

Bases: Gate

Two qubit RZZ gate (rotation about ZZ)..

This gate is symmetric, and is maximally entangling at \((\theta = \frac{\pi}{2})\)

Matrix representation:

\[\begin{split}\operatorname{RZZ}(\theta) = \begin{pmatrix} e^{-i\frac{\theta}{2}} & 0 & 0 & 0 \\ 0 & e^{i\frac{\theta}{2}} & 0 & 0 \\ 0 & 0 & e^{i\frac{\theta}{2}} & 0 \\ 0 & 0 & 0 & e^{-i\frac{\theta}{2}} \end{pmatrix}\end{split}\]
Parameters:

theta (float) – The angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta = Symbol('theta')
>>> GateRZZ(theta)
RZZ(theta)
>>> GateRZZ(theta).matrix()
[exp(-1/2*I*theta), 0, 0, 0]
[0, exp(1/2*I*theta), 0, 0]
[0, 0, exp(1/2*I*theta), 0]
[0, 0, 0, exp(-1/2*I*theta)]

>>> c = Circuit().push(GateRZZ(theta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── RZZ(theta) @ q[0,1]

>>> GateRZZ(theta).power(2), GateRZZ(theta).inverse()
(RZZ(theta)**2, RZZ(-theta))
>>> GateRZZ(theta).decompose()
2-qubit circuit with 3 instructions:
├── CX @ q[0], q[1]
├── RZ(theta) @ q[1]
└── CX @ q[0], q[1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateS[source]#

Bases: Power

Single qubit gate S.

It induces a \(\frac{\pi}{2}\) phase gate.

Matrix representation:

\[\begin{split}\operatorname{S} = \begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateS()
S
>>> GateS().matrix()
[1.0, 0]
[0, 0.0 + 1.0*I]

>>> c = Circuit().push(GateS(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── S @ q[0]

>>> GateS().power(2), GateS().inverse()
(Z, S†)
>>> GateS().decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, (1/2)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateSDG[source]#

Bases: Inverse

Single qubit S-dagger gate (conjugate transpose of the S gate).

Matrix representation:

\[\begin{split}\operatorname{S}^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & -i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSDG()
S†
>>> GateSDG().matrix()
[1.0, 0]
[0, 6.12323399573677e-17 - 1.0*I]

>>> c = Circuit().push(GateSDG(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── S† @ q[0]

>>> GateSDG().power(2), GateSDG().inverse()
(S†**2, S)
>>> GateSDG().decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, (-1/2)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateSWAP[source]#

Bases: Gate

Two qubit SWAP gate.

See Also GateISWAP()

Matrix representation:

\[\begin{split}\operatorname{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSWAP()
SWAP
>>> GateSWAP().matrix()
[1.0, 0, 0, 0]
[0, 0, 1.0, 0]
[0, 1.0, 0, 0]
[0, 0, 0, 1.0]

>>> c = Circuit().push(GateSWAP(), 0, 1)
>>> GateSWAP().power(2), GateSWAP().inverse()
(⨷ ² ID, SWAP)
>>> GateSWAP().decompose()
2-qubit circuit with 3 instructions:
├── CX @ q[0], q[1]
├── CX @ q[1], q[0]
└── CX @ q[0], q[1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateSX[source]#

Bases: Power

Single qubit \(\sqrt{X}\) gate.

Matrix representation:

\[\begin{split}\sqrt{\operatorname{X}} = \frac{1}{2} \begin{pmatrix} 1+i & 1-i \\ 1-i & 1+i\\ \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSX()
SX
>>> GateSX().matrix()
[0.5 + 0.5*I, 0.5 - 0.5*I]
[0.5 - 0.5*I, 0.5 + 0.5*I]

>>> c = Circuit().push(GateSX(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── SX @ q[0]

>>> GateSX().power(2), GateSX().inverse()
(X, SX†)
>>> GateSX().decompose()
1-qubit circuit with 4 instructions:
├── S† @ q[0]
├── H @ q[0]
├── S† @ q[0]
└── U(0, 0, 0, (1/4)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
name = 'SX'#
class mimiqcircuits.GateSXDG[source]#

Bases: Inverse

Single qubit \(\sqrt{X}^\dagger\) gate (conjugate transpose of the \(\sqrt{X}\) gate).

Matrix representation:

\[\begin{split}\sqrt{\operatorname{X}}^\dagger = \frac{1}{2} \begin{pmatrix} 1-i & 1+i \\ 1+i & 1-i\\ \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSXDG()
SX†
>>> GateSXDG().matrix()
[0.5 - 0.5*I, 0.5 + 0.5*I]
[0.5 + 0.5*I, 0.5 - 0.5*I]

>>> c = Circuit().push(GateSXDG(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── SX† @ q[0]

>>> GateSXDG().power(2), GateSXDG().inverse()
(SX†**2, SX)
>>> GateSXDG().decompose()
1-qubit circuit with 4 instructions:
├── S @ q[0]
├── H @ q[0]
├── S @ q[0]
└── U(0, 0, 0, (-1/4)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateSY[source]#

Bases: Power

Single qubit \(\sqrt{Y}\) gate.

See also

GateSYDG, GateY, Power

Matrix Representation

\[\begin{split}\operatorname{SY} = \sqrt{\operatorname{Y}} = \frac{1}{2} \begin{pmatrix} 1+i & -1-i \\ 1+i & 1+i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSY()
SY
>>> GateSY().matrix()
[0.5 + 0.5*I, -0.5 - 0.5*I]
[0.5 + 0.5*I, 0.5 + 0.5*I]
>>> c = Circuit()
>>> c.push(GateSY(), 1)
2-qubit circuit with 1 instructions:
└── SY @ q[1]
>>> c.push(GateSY(), 2)
3-qubit circuit with 2 instructions:
├── SY @ q[1]
└── SY @ q[2]
>>> power(GateSY(), 2)
Y**1.0

Decomposition

>>> GateSY().decompose()
1-qubit circuit with 4 instructions:
├── S @ q[0]
├── S @ q[0]
├── H @ q[0]
└── U(0, 0, 0, (1/4)*pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateSYDG[source]#

Bases: Inverse

Single qubit \(\sqrt{Y}^\dagger\) gate (conjugate transpose of the \(\sqrt{Y}\) gate).

See also

GateSY, GateY, Power, Inverse

Matrix Representation

\[\begin{split}\operatorname{SYDG} = \sqrt{\operatorname{Y}}^\dagger = \frac{1}{2} \begin{pmatrix} 1-i & 1-i \\ -1+i & 1-i \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateSYDG()
SY†
>>> GateSYDG().matrix()
[0.5 - 0.5*I, 0.5 - 0.5*I]
[-0.5 + 0.5*I, 0.5 - 0.5*I]
>>> c = Circuit()
>>> c.push(GateSYDG(), 1)
2-qubit circuit with 1 instructions:
└── SY† @ q[1]
>>> c.push(GateSYDG(), 2)
3-qubit circuit with 2 instructions:
├── SY† @ q[1]
└── SY† @ q[2]
>>> power(GateSYDG(), 2)
SY†**2
>>> inverse(GateSYDG())
SY
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateT[source]#

Bases: Power

Single qubit T gate.

Matrix representation:

\[\begin{split}\operatorname{T} = \begin{pmatrix} 1 & 0 \\ 0 & \exp\left(\frac{i\pi}{4}\right) \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateT()
T
>>> GateT().matrix()
[1.0, 0]
[0, 0.707106781186548 + 0.707106781186548*I]

>>> c = Circuit().push(GateT(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── T @ q[0]

>>> GateT().power(2), GateT().inverse()
(S, T†)
>>> GateT().decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, (1/4)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateTDG[source]#

Bases: Inverse

Single qubit T-dagger gate (conjugate transpose of the T gate).

Matrix representation:

\[\begin{split}\operatorname{T}^\dagger = \begin{pmatrix} 1 & 0 \\ 0 & \exp\left(\frac{-i\pi}{4}\right) \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateTDG()
T†
>>> GateTDG().matrix()
[1.0, 0]
[0, 0.707106781186547 - 0.707106781186547*I]

>>> c = Circuit().push(GateTDG(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── T† @ q[0]

>>> GateTDG().power(2), GateTDG().inverse()
(T†**2, T)
>>> GateTDG().decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, (-1/4)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

isopalias()[source]#
class mimiqcircuits.GateU(theta, phi, lmbda, gamma=0.0)[source]#

Bases: Gate

Single qubit generic unitary phase gate.

Matrix representation:

\[\begin{split}\operatorname{U}(\theta, \phi, \lambda, \gamma) = \mathrm{e}^{i\gamma} \begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -\mathrm{e}^{i\lambda}\sin\left(\frac{\theta}{2}\right)\\ \mathrm{e}^{i\phi}\sin\left(\frac{\theta}{2}\right) & \mathrm{e}^{i(\phi+\lambda)}\cos\left (\frac{\theta}{2}\right) \end{pmatrix}\end{split}\]
Parameters:
  • theta (float) – Euler angle 1 in radians.

  • phi (float) – Euler angle 2 in radians.

  • lambda (float) – Euler angle 3 in radians.

  • gamma (float, optional) – Euler angle 4 in radians (default is 0).

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, phi, lmbda, gamma = symbols('theta phi lambda gamma')
>>> GateU(theta, phi, lmbda, gamma)
U(theta, phi, lambda, gamma)
>>> GateU(theta, phi, lmbda, gamma).matrix()
[exp(I*gamma)*cos((1/2)*theta), -exp(I*(gamma + lambda))*sin((1/2)*theta)]
[exp(I*(gamma + phi))*sin((1/2)*theta), exp(I*(gamma + lambda + phi))*cos((1/2)*theta)]

>>> c = Circuit().push(GateU(theta, phi, lmbda, gamma), 0)
>>> c
1-qubit circuit with 1 instructions:
└── U(theta, phi, lambda, gamma) @ q[0]

>>> GateU(theta, phi, lmbda, gamma).power(2), GateU(theta, phi, lmbda, gamma).inverse()
(U(theta, phi, lambda, gamma)**2, U(-theta, -lambda, -phi, -gamma))
>>> GateU(theta, phi, lmbda, gamma).decompose()
1-qubit circuit with 1 instructions:
└── U(theta, phi, lambda, gamma) @ q[0]

>>> c = Circuit().push(GateU(theta, phi, lmbda, gamma), 0)
>>> c
1-qubit circuit with 1 instructions:
└── U(theta, phi, lambda, gamma) @ q[0]

>>> GateU(theta, phi, lmbda, gamma).power(2), GateU(theta, phi, lmbda, gamma).inverse()
(U(theta, phi, lambda, gamma)**2, U(-theta, -lambda, -phi, -gamma))
>>> GateU(theta, phi, lmbda, gamma).decompose()
1-qubit circuit with 1 instructions:
└── U(theta, phi, lambda, gamma) @ q[0]
convert_to_numeric(matrix)[source]#

Convert a symbolic matrix to a numeric numpy array.

inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateU1(lmbda)[source]#

Bases: Gate

Single qubit generic unitary gate \({U_1}\).

Equivalent to GateP()

Matrix representation:

\[\begin{split}\operatorname{U1}(\lambda) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i\lambda} \end{pmatrix}\end{split}\]
Parameters:

lambda (float) – Euler angle 3 in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> lmbda = Symbol('lambda')
>>> GateU1(lmbda)
U1(lambda)
>>> GateU1(lmbda).matrix()
[1.0, 0]
[0, exp(I*lambda)]

>>> c = Circuit().push(GateU1(lmbda), 0)
>>> c
1-qubit circuit with 1 instructions:
└── U1(lambda) @ q[0]

>>> GateU1(lmbda).power(2), GateU1(lmbda).inverse()
(U1(2*lambda), U1(-lambda))
>>> GateU1(lmbda).decompose()
1-qubit circuit with 1 instructions:
└── U(0, 0, lambda, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateU2(phi, lmbda)[source]#

Bases: Gate

Single qubit generic unitary gate \({U_2}\).

Matrix representation:

\[\begin{split}\operatorname{U2}(\phi,\lambda) = \frac{1}{\sqrt{2}}e^{-(\phi+\lambda)/2}\begin{pmatrix} 1 & -e^{i\lambda} \\ e^{i\phi} & e^{i(\phi+\lambda)} \end{pmatrix}\end{split}\]
Parameters:
  • phi – Euler angle in radians.

  • lambda – Euler angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> phi, lmbda = symbols('phi lambda')
>>> GateU2(phi, lmbda)
U2(phi, lambda)
>>> GateU2(phi, lmbda).matrix()
[0.707106781186548, -0.707106781186548*exp(I*lambda)]
[0.707106781186548*exp(I*phi), 0.707106781186548*exp(I*(lambda + phi))]

>>> c = Circuit().push(GateU2(phi, lmbda), 0)
>>> c
1-qubit circuit with 1 instructions:
└── U2(phi, lambda) @ q[0]

>>> GateU2(phi, lmbda).power(2), GateU2(phi, lmbda).inverse()
(U2(phi, lambda)**2, U2(-lambda - pi, -phi + pi))
>>> GateU2(phi, lmbda).decompose()
1-qubit circuit with 1 instructions:
└── U((1/2)*pi, phi, lambda, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateU3(theta, phi, lmbda)[source]#

Bases: Gate

Single qubit generic unitary gate \({U_3}\).

This gate is equivalent to GateU() up to a global phase, \(\operatorname{U3}(\theta,\phi,\lambda) = e^{-i(\phi + \lambda + \theta)/2} \operatorname{U}(\theta,\phi,\lambda)\)

Matrix representation:

\[\begin{split}\operatorname{U3}(\theta,\phi,\lambda) = \frac{1}{2}e^{-i(\phi + \lambda + \theta)/2} \begin{pmatrix} 1 + e^{i\theta} & -i e^{i\lambda}(1 - e^{i\theta}) \\ i e^{i\phi}(1 - e^{i\theta}) & e^{i(\phi + \lambda)}(1 + e^{i\theta}) \end{pmatrix}\end{split}\]
Parameters:
  • theta – Euler angle 1 in radians.

  • phi – Euler angle 2 in radians.

  • lambda – Euler angle 3 in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, phi, lmbda = symbols('theta phi lambda')
>>> GateU3(theta, phi, lmbda)
U3(theta, phi, lambda)
>>> GateU3(theta, phi, lmbda).matrix()
[1.0*cos((1/2)*theta), -exp(I*lambda)*sin((1/2)*theta)]
[exp(I*phi)*sin((1/2)*theta), exp(I*(lambda + phi))*cos((1/2)*theta)]

>>> c = Circuit().push(GateU3(theta, phi, lmbda), 0)
>>> GateU3(theta, phi, lmbda).power(2), GateU3(theta, phi, lmbda).inverse()
(U3(theta, phi, lambda)**2, U3(-theta, -lambda, -phi))
>>> GateU3(theta, phi, lmbda).decompose()
1-qubit circuit with 1 instructions:
└── U(theta, phi, lambda, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateX[source]#

Bases: Gate

Single qubit Pauli-X gate.

Matrix representation:

\[\begin{split}\operatorname{X} = \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateX()
X
>>> GateX().matrix()
[0, 1.0]
[1.0, 0]

>>> c = Circuit().push(GateX(), 0)
>>> c
1-qubit circuit with 1 instructions:
└── X @ q[0]

>>> GateX().power(2), GateX().inverse()
(ID, X)
>>> GateX().decompose()
1-qubit circuit with 1 instructions:
└── U(pi, 0, pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateXXminusYY(theta, beta)[source]#

Bases: Gate

Two qubit parametric GateXXminusYY gate.

Its action is to induce a coherent rotation by some angle between \(\ket{00}\) and \(\ket{11}\)

Matrix representation:

\[\begin{split}\operatorname{(XX-YY)}(\theta, \beta)=\begin{pmatrix} \cos(\frac{\theta}{2}) & 0 & 0 & -i\sin(\frac{\theta}{2})e^{-i\beta} \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ -i\sin(\frac{\theta}{2})e^{i\beta} & 0 & 0 & \cos(\frac{\theta}{2}) \end{pmatrix}\end{split}\]
Parameters:
  • theta (float) – The angle in radians.

  • beta (float) – The angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, beta = symbols('theta beta')
>>> GateXXminusYY(theta, beta)
XXminusYY(theta, beta)
>>> GateXXminusYY(theta, beta).matrix()
[cos((1/2)*theta), 0, 0, I*(I*sin(beta) - cos(beta))*sin((1/2)*theta)]
[0, 1.0, 0, 0]
[0, 0, 1.0, 0]
[(sin(beta) - I*cos(beta))*sin((1/2)*theta), 0, 0, cos((1/2)*theta)]

>>> c = Circuit().push(GateXXminusYY(theta, beta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── XXminusYY(theta, beta) @ q[0,1]

>>> GateXXminusYY(theta, beta).power(2), GateXXminusYY(theta, beta).inverse()
(XXminusYY(theta, beta)**2, XXminusYY(-theta, beta))
>>> GateXXminusYY(theta, beta).decompose()
2-qubit circuit with 14 instructions:
├── RZ(-beta) @ q[1]
├── RZ((-1/2)*pi) @ q[0]
├── SX @ q[0]
├── RZ((1/2)*pi) @ q[0]
├── S @ q[1]
├── CX @ q[0], q[1]
├── RY((1/2)*theta) @ q[0]
├── RY((-1/2)*theta) @ q[1]
├── CX @ q[0], q[1]
├── S† @ q[1]
├── RZ((-1/2)*pi) @ q[0]
├── SX† @ q[0]
├── RZ((1/2)*pi) @ q[0]
└── RZ(beta) @ q[1]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateXXplusYY(theta, beta)[source]#

Bases: Gate

Two qubit parametric XXplusYY gate.

Also known as an XY gate. Its action is to induce a coherent rotation by some angle between \(\ket{10}\) and \(\ket{01}\).

Matrix representation:

\[\begin{split}\operatorname{(XX+YY)}(\theta, \beta)= \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\frac{\theta}{2}) & -i\sin(\frac{\theta}{2})e^{i\beta} & 0\\ 0 & -i\sin(\frac{\theta}{2})e^{-i\beta} & \cos(\frac{\theta}{2}) & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}\end{split}\]
Parameters:
  • theta – The angle in radians.

  • beta – The phase angle in radians.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> theta, beta = symbols('theta beta')
>>> GateXXplusYY(theta, beta)
XXplusYY(theta, beta)
>>> GateXXplusYY(theta, beta).matrix()
[1.0, 0, 0, 0]
[0, cos((1/2)*theta), (sin(beta) - I*cos(beta))*sin((1/2)*theta), 0]
[0, I*(I*sin(beta) - cos(beta))*sin((1/2)*theta), cos((1/2)*theta), 0]
[0, 0, 0, 1.0]

>>> c = Circuit().push(GateXXplusYY(theta, beta), 0, 1)
>>> c
2-qubit circuit with 1 instructions:
└── XXplusYY(theta, beta) @ q[0,1]

>>> GateXXplusYY(theta, beta).power(2), GateXXplusYY(theta, beta).inverse()
(XXplusYY(theta, beta)**2, XXplusYY(-theta, beta))
>>> GateXXplusYY(theta, beta).decompose()
2-qubit circuit with 14 instructions:
├── RZ(beta) @ q[0]
├── RZ((-1/2)*pi) @ q[1]
├── SX @ q[1]
├── RZ((1/2)*pi) @ q[1]
├── S @ q[0]
├── CX @ q[1], q[0]
├── RY((-1/2)*theta) @ q[1]
├── RY((-1/2)*theta) @ q[0]
├── CX @ q[1], q[0]
├── S† @ q[0]
├── RZ((-1/2)*pi) @ q[1]
├── SX† @ q[1]
├── RZ((1/2)*pi) @ q[1]
└── RZ(-beta) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateY[source]#

Bases: Gate

Single qubit Pauli-Y gate.

Matrix representation:

\[\begin{split}\operatorname{Y} = \begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateY()
Y
>>> GateY().matrix()
[0, -0.0 - 1.0*I]
[0.0 + 1.0*I, 0]

>>> c = Circuit().push(GateY(), 0)
>>> GateY().power(2), GateY().inverse()
(ID, Y)
>>> GateY().decompose()
1-qubit circuit with 1 instructions:
└── U(pi, (1/2)*pi, (1/2)*pi, 0.0) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GateZ[source]#

Bases: Gate

Single qubit Pauli-Z gate.

Matrix representation:

\[\begin{split}\operatorname{Z} = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}\end{split}\]

Examples

>>> from mimiqcircuits import *
>>> GateZ()
Z
>>> GateZ().matrix()
[1.0, 0]
[0, -1.0]

>>> c = Circuit().push(GateZ(), 0)
>>> GateZ().power(2), GateZ().inverse()
(ID, Z)
>>> GateZ().decompose()
1-qubit circuit with 1 instructions:
└── P(pi) @ q[0]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.GeneralizedAmplitudeDamping(p, gamma)[source]#

Bases: krauschannel

One-qubit generalized amplitude damping noise channel.

The amplitude generalized damping channel is defined by the Kraus operators.

Kraus Matrices representation:

\[\begin{split}\operatorname E_1 = \sqrt{p} \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1-\gamma} \end{pmatrix} ,\quad \operatorname E_2 = \sqrt{p} \begin{pmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \end{pmatrix} ,\quad \operatorname E_3 = \sqrt{1-p} \begin{pmatrix} \sqrt{1-\gamma} & 0 \\ 0 & 1 \end{pmatrix} ,\quad \operatorname E_4 = \sqrt{1-p} \begin{pmatrix} 0 & 0 \\ \sqrt{\gamma} & 0 \end{pmatrix},\end{split}\]
Parameters:

gammagamma in [0,1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GeneralizedAmplitudeDamping(0.1,1), 0)
1-qubit circuit with 1 instructions:
└── GeneralizedAmplitudeDamping((0.1, 1)) @ q[0]
evaluate(d={})[source]#
iswrapper()[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

property num_qubits#
property parnames#
class mimiqcircuits.IfStatement(operation, bitstring: BitString)[source]#

Bases: Operation

Conditional operation

This operation applies a specific operation if a given classical bit condition is met.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(IfStatement(GateX(), BitString('1')), 0, 0)
1-qubit circuit with 1 instructions:
└── IF (c==1) X @ q[0], c[0]
asciiwidth(qubits, bits, zvars)[source]#
property bitstring#
control(num_controls)[source]#
evaluate(d)[source]#
get_bitstring()[source]#
get_operation()[source]#
getparams()[source]#
inverse()[source]#
iswrapper()[source]#
property op#
power(power)[source]#
class mimiqcircuits.Instruction(operation, qubits=None, bits=None, zvars=None)[source]#

Bases: object

Initializes an instruction of a quantum circuit.

Parameters:
  • operation (Operation) – The operation applied by the instruction.

  • qubits (tuple of int) – The qubits to apply the quantum operation to.

  • bits (tuple of int) – The classical bits to apply the quantum operation to.

Raises:
  • TypeError – If operation is not a subclass of Gate or qubits is not a tuple.

  • ValueError – If qubits contains less than 1 or more than 2 elements.

Examples

>>> from mimiqcircuits import *
>>> Instruction(GateX(),(0,),())
X @ q[0]
>>> Instruction(Barrier(4),(0,1,2,3),())
Barrier @ q[0,1,2,3]
asciiwidth()[source]#
property bits#
copy()[source]#
decompose()[source]#
deepcopy()[source]#
evaluate(d)[source]#
get_bits()[source]#
get_operation()[source]#
get_qubits()[source]#
get_zvars()[source]#
inverse()[source]#
num_bits()[source]#
num_qubits()[source]#
num_zvars()[source]#
property operation#
property qubits#
property zvars#
class mimiqcircuits.Inverse(operation, *args, **kwargs)[source]#

Bases: Gate

Inverse of the wrapped quantum operation.

The inversion is not performed right away, but only when the circuit is cached or executed.

Warning

Users should not use Inverse directly but rather the inverse method, which performs all the necessary simplifications (e.g., op.inverse().inverse() == op)

Examples

>>> from mimiqcircuits import *
>>> Inverse(GateP(1)).matrix()
[1.0, 0]
[0, 0.54030230586814 - 0.841470984807897*I]

>>> c = Circuit()
>>> c.push(Inverse(GateP(1)), 1)
2-qubit circuit with 1 instructions:
└── P(1)† @ q[1]
control(*args)[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
getparams()[source]#
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

parallel(*args)[source]#
power(*args)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.Kraus(E: List[MutableDenseMatrix | ndarray | MutableDenseMatrix | AbstractOperator], rtol=1e-12)[source]#

Bases: krauschannel

Kraus(kmatrices).

Custom N-qubit Kraus channel specified by a list of 2^N x 2^N Kraus matrices.

A Kraus channel is defined by:

\[\mathcal{E}(\rho) = \sum_k E_k \rho E_k^\dagger,\]

where \(E_k\) are Kraus matrices that need to fulfill \(\sum_k E_k^\dagger E_k = I\).

If the Kraus matrices are all proportional to unitaries, use MixedUnitary() instead.

The Kraus matrices are defined in the computational basis in the usual textbook order (the first qubit corresponds to the left-most qubit). For 1 qubit, we have \(|0\rangle\), \(|1\rangle\). For 2 qubits, we have \(|00\rangle\), \(|01\rangle\), \(|10\rangle\), \(|11\rangle\).

Note: Currently, only 1 and 2-qubit custom Kraus channels are supported.

Parameters:

kmatrices (list) – List of \(2^N \times 2^N\) complex matrices. The number of qubits is equal to \(N\).

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> c = Circuit()
>>> c.push(Kraus([Matrix([[1, 0], [0, sqrt(0.9)]]), Matrix([[0, sqrt(0.1)], [0, 0]])]), 0)
1-qubit circuit with 1 instructions:
└── Kraus(Operator([[1, 0], [0, 0.948683298050514]]), Operator([[0, 0.316227766016838], [0, 0]])) @ q[0]

Kraus Matrices

>>> g=Kraus([Matrix([[1, 0], [0, sqrt(0.9)]]), Matrix([[0, sqrt(0.1)], [0, 0]])])
>>> g.krausmatrices()
[[1.0, 0]
[0, 0.948683298050514]
, [0, 0.316227766016838]
[0, 0]
]

# Example 2: Kraus with Projector0 and Projector1

>>> c = Circuit()
>>> c.push(Kraus([Projector0(), Projector1()]), 0)
1-qubit circuit with 1 instructions:
└── Kraus(P₀(1), P₁(1)) @ q[0]

# Example 3: Kraus with a matrix and Projector1

>>> c = Circuit()
>>> c.push(Kraus([Matrix([[1, 0], [0, 0]]), Projector1()]), 0)
1-qubit circuit with 1 instructions:
└── Kraus(Operator([[1, 0], [0, 0]]), P₁(1)) @ q[0]
evaluate(values: dict)[source]#

Evaluates symbolic parameters in Kraus matrices using a dictionary of values.

Parameters:

values (dict) – A dictionary where keys are symbolic variables and values are the corresponding numerical values.

Returns:

A new Kraus instance with evaluated matrices.

Return type:

Kraus

krausoperators()[source]#

Returns the Kraus operators.

unwrappedkrausmatrices()[source]#

Returns the unwrapped Kraus matrices.

class mimiqcircuits.LazyArg[source]#

Bases: object

class mimiqcircuits.LazyExpr(obj, *args)[source]#

Bases: object

control(*args)[source]#
inverse()[source]#
parallel(*args)[source]#
power(*args)[source]#
class mimiqcircuits.Measure[source]#

Bases: AbstractMeasurement

Measure operation.

This operation performs a measurement in the computational basis (Z-basis) and stores the result in a classical register.

The measurement projects the quantum state onto either the \(|0⟩\) or \(|1⟩\) state, corresponding to the classical bit values of 0 and 1, respectively.

Warning

Measure is non-reversible.

Examples

Adding Measure operation to the Circuit (The qubits (first arg) and the bits (second arg) can be: range, list, tuple, set or int)

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Measure(),0,0)
1-qubit circuit with 1 instructions:
└── M @ q[0], c[0]
>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Measure(), range(0,3), range(0,3))
3-qubit circuit with 3 instructions:
├── M @ q[0], c[0]
├── M @ q[1], c[1]
└── M @ q[2], c[2]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureReset[source]#

Bases: AbstractMeasurement

MeasureReset operation.

This operation measures a qubit q, stores the value in a classical bit c, then applies a X operation to the qubit if the measured value is 1, effectively resetting the qubit to the \(\ket{0}\) state.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureReset(), 1, 0)
2-qubit circuit with 1 instructions:
└── MR @ q[1], c[0]

>>> c.decompose()
2-qubit circuit with 2 instructions:
├── M @ q[1], c[0]
└── IF (c==1) X @ q[1], c[0]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureResetX[source]#

Bases: AbstractMeasurement

MeasureResetX operation.

The MeasureResetX operation first applies a Hadamard gate (H) to the qubit, performs a measurement and reset operation similar to the MeasureReset operation, and then applies another Hadamard gate. This sequence effectively measures the qubit in the X-basis and resets it to the \(|+>\) state.

See also

MeasureReset: The standard measurement and reset operation in the Z-basis. MeasureResetY: Similar operation that measures and resets the qubit in the Y-basis.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureResetX(), 1, 0)
2-qubit circuit with 1 instructions:
└── MRX @ q[1], c[0]

>>> c.decompose()
2-qubit circuit with 3 instructions:
├── H @ q[1]
├── MR @ q[1], c[0]
└── H @ q[1]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureResetY[source]#

Bases: AbstractMeasurement

MeasureResetY operation.

The MeasureResetY operation applies (HYZ) gate to the qubit, performs a MeasureReset operation, and then applies another HYZ gate. This sequence effectively measures the qubit in the Y-basis.

See also

MeasureReset: The standard measurement and reset operation in the Z-basis. MeasureResetX: Similar operation that measures and resets the qubit in the X-basis.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureResetY(), 1, 0)
2-qubit circuit with 1 instructions:
└── MRY @ q[1], c[0]

>>> c.decompose()
2-qubit circuit with 3 instructions:
├── HYZ @ q[1]
├── MR @ q[1], c[0]
└── HYZ @ q[1]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureResetZ[source]#

Bases: AbstractMeasurement

This class acting as an alias for MeasureReset.

class mimiqcircuits.MeasureX[source]#

Bases: AbstractMeasurement

Single qubit measurement operation in the X-basis.

This operation projects the quantum state onto the X-basis and stores the result of such measurement in a classical register. The measurement is performed by first applying a Hadamard gate to rotate the qubit’s state into the computational basis, performing a standard Z-basis measurement, and then applying another Hadamard gate to return the qubit to its original basis.

Warning

MeasureX is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> MeasureX()
MX
>>> c = Circuit()
>>> c.push(MeasureX(), 2, 1)
3-qubit circuit with 1 instructions:
└── MX @ q[2], c[1]
>>> c.push(MeasureX(), 3, 4)
4-qubit circuit with 2 instructions:
├── MX @ q[2], c[1]
└── MX @ q[3], c[4]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureXX[source]#

Bases: Operation

MeasureXX operation.

The MeasureXX operation measures the joint parity of two qubits in the X-basis, determining whether the qubits are in the same or different states within this basis. The operation begins by applying a controlled-X (CX) gate between the two qubits to entangle them. Following this, a Hadamard (H) gate is applied to the first qubit, rotating it into the X-basis. The second qubit, designated as the target, is then measured to extract the parity information. After the measurement, the Hadamard gate is applied again to the first qubit to reverse the rotation, and a second controlled-X (CX) gate is applied to disentangle the qubits, restoring the system to its original state. Through this sequence, the MeasureXX operation efficiently captures the parity relationship of the qubits in the X-basis.

A result of 0 indicates that the qubits are in the same state, while a result of 1 indicates that they are in different states.

See also

MeasureZZ: Measure the joint parity of two qubits in the Z-basis. MeasureYY: Measure the joint parity of two qubits in the Y-basis.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureXX(), 0, 1, 0)
2-qubit circuit with 1 instructions:
└── MXX @ q[0,1], c[0]

>>> c.decompose()
2-qubit circuit with 5 instructions:
├── CX @ q[0], q[1]
├── H @ q[0]
├── M @ q[1], c[0]
├── H @ q[0]
└── CX @ q[0], q[1]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureY[source]#

Bases: AbstractMeasurement

Single qubit measurement operation in the Y-basis.

This operation projects the quantum state onto the Y-basis and stores the result of such measurement in a classical register. The measurement is performed by first applying a \(R_y(\pi/2)\) rotation gate to rotate the qubit’s state into the computational basis, performing a standard Z-basis measurement, and then applying another \(R_y(-\pi/2)\) rotation to return the qubit to its original basis.

Warning

MeasureY is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> MeasureY()
MY
>>> c = Circuit()
>>> c.push(MeasureY(), 2, 1)
3-qubit circuit with 1 instructions:
└── MY @ q[2], c[1]
>>> c.push(MeasureY(), 3, 4)
4-qubit circuit with 2 instructions:
├── MY @ q[2], c[1]
└── MY @ q[3], c[4]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureYY[source]#

Bases: Operation

MeasureYY operation.

The MeasureYY operation measures the joint parity of two qubits in the Y-basis, determining whether they are in the same or different states in this basis. This is achieved by first applying an S gate (a π/2 phase shift) to both qubits, followed by a controlled-X (CX) gate. A Hadamard gate (H) is then applied to the first qubit, and the second qubit is measured. To restore the system, a Z gate is applied to the first qubit, followed by another Hadamard gate, another CX gate, and finally another S gate to both qubits. The measurement result reflects whether the qubits are in the same or different states in the Y-basis.

See Also:

MeasureZZ: Measure the joint parity of two qubits in the Z-basis. MeasureXX: Measure the joint parity of two qubits in the X-basis.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureYY(), 0, 1, 0)
2-qubit circuit with 1 instructions:
└── MYY @ q[0,1], c[0]

>>> c.decompose()
2-qubit circuit with 10 instructions:
├── S @ q[0]
├── S @ q[1]
├── CX @ q[0], q[1]
├── H @ q[0]
├── M @ q[0], c[0]
├── Z @ q[0]
├── H @ q[0]
├── CX @ q[0], q[1]
├── S @ q[0]
└── S @ q[1]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MeasureZ[source]#

Bases: AbstractMeasurement

Single qubit measurement operation in the Z-basis.

This class returns an instance of the Measure operation, effectively acting as an alias for Z-basis measurement.

Warning

MeasureZ is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> MeasureZ()
M
>>> c = Circuit()
>>> c.push(MeasureZ(), 0, 0)
1-qubit circuit with 1 instructions:
└── M @ q[0], c[0]
>>> c.push(MeasureZ(), 1, 2)
2-qubit circuit with 2 instructions:
├── M @ q[0], c[0]
└── M @ q[1], c[2]
class mimiqcircuits.MeasureZZ[source]#

Bases: Operation

MeasureZZ operation.

The MeasureZZ operation measures the joint parity of two qubits in the Z-basis. This is achieved by applying a controlled-X (CX) gate, measuring the target qubit, and then applying another CX gate to undo the entanglement. The measurement result indicates whether the qubits are in the same or different states in the Z-basis.

See also

MeasureXX: Measure the joint parity of two qubits in the X-basis. MeasureYY: Measure the joint parity of two qubits in the Y-basis.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(MeasureZZ(), 0, 1, 0)
2-qubit circuit with 1 instructions:
└── MZZ @ q[0,1], c[0]

>>> c.decompose()
2-qubit circuit with 3 instructions:
├── CX @ q[0], q[1]
├── M @ q[1], c[0]
└── CX @ q[0], q[1]
control(num_qubits)[source]#
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
power(p)[source]#
class mimiqcircuits.MimiqConnection(url=None)[source]#

Bases: MimiqConnection

Represents a connection to the Mimiq Server.

Inherits from: mimiqlink.MimiqConnection python.

execute(circuits, label='pyapi_v0.15.3', algorithm='auto', nsamples=1000, bitstrings=None, timelimit=None, bonddim=None, entdim=None, seed=None, qasmincludes=None, force=False, **kwargs)[source]#

Execute a circuit or a list of quantum circuits on the Mimiq server.

Parameters:
  • circuits (Circuit or list of Circuits or str) – A single Circuit object, a list of Circuit objects, or QASM file paths representing the circuits to be executed.

  • label (str) – A label for the execution. Defaults to “pyapi_v” + __version__.

  • algorithm (str) – The algorithm to use. Defaults to “auto”.

  • nsamples (int) – The number of samples to collect. Defaults to DEFAULT_SAMPLES.

  • bitstrings (list of str, optional) – Specific bitstrings to measure. Defaults to None.

  • timelimit (int, optional) – The maximum execution time in minutes. Defaults to None.

  • bonddim (int, optional) – The bond dimension to use. Defaults to None.

  • entdim (int, optional) – The entanglement dimension to use. Defaults to None.

  • seed (int, optional) – A seed for random number generation. Defaults to None.

  • qasmincludes (list of str, optional) – Additional QASM includes. Defaults to None.

  • **kwargs – Additional keyword arguments.

Returns:

A handle to the execution, typically used to retrieve results.

Return type:

object

Raises:
  • ValueError – If nsamples exceeds MAX_SAMPLES, bond/entanglement dimensions are out of bounds, or if a circuit contains unevaluated symbolic parameters.

  • FileNotFoundError – If a QASM file is not found.

  • TypeError – If the circuits argument is not a Circuit object or a valid file path.

Note

You can also pass a single QASM file path as a string or a list of QASM file paths instead of Circuit objects. This allows for executing circuits defined in the OpenQASM format directly.

Examples

Connecting to server

>>> from mimiqcircuits import *
>>> import os
>>> conn = MimiqConnection(os.getenv("MIMIQCLOUD2"))
>>> conn.connect(os.getenv("MIMIQUSER"), os.getenv("MIMIQPASS"))
Connection:
├── url: https://mimiqfast.qperfect.io/api
├── Computing time: 597/10000 minutes
├── Executions: 452/10000
├── Max time limit per request: 180 minutes
└── status: open

>>> c = Circuit()
>>> c.push(GateH(), range(10))
10-qubit circuit with 10 instructions:
├── H @ q[0]
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── H @ q[4]
├── H @ q[5]
├── H @ q[6]
├── H @ q[7]
├── H @ q[8]
└── H @ q[9]

>>> job = conn.execute(c, algorithm="auto")
>>> res = conn.get_results(job)
>>> res
[QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 7.3677e-05s
│    ├── apply time: 2.4677e-05s
│    ├── total time: 0.000306075s
│    ├── compression time: 8.103e-06s
│    └── sample time: 0.000141276s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    ├── bs"1100000001" => 7
│    ├── bs"1010110100" => 5
│    ├── bs"0010110110" => 4
│    ├── bs"1001010110" => 4
│    └── bs"1001100111" => 4
├── 1 executions
├── 0 amplitudes
└── 1000 samples]

Prepare List of Circuits for execution (Batch-Mode)

>>> c1 = Circuit()
>>> c1.push(Control(2, GateH()), 0, 1, 3)
4-qubit circuit with 1 instructions:
└── C₂H @ q[0,1], q[3]

>>> job = conn.execute([c,c1], algorithm="auto")

List of Results for all Circuits

>>> res = conn.get_results(job)
>>> res
[QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 0.00011103s
│    ├── apply time: 2.7497e-05s
│    ├── total time: 0.000326494s
│    ├── compression time: 8.657e-06s
│    └── sample time: 0.000110076s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    ├── bs"1011011100" => 6
│    ├── bs"0110001101" => 4
│    ├── bs"0001011001" => 4
│    ├── bs"1011010010" => 4
│    └── bs"1000000001" => 4
├── 1 executions
├── 0 amplitudes
└── 1000 samples, QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 0.081798891s
│    ├── apply time: 0.277081657s
│    ├── total time: 0.43127283099999997s
│    ├── amplitudes time: 1.08e-07s
│    ├── compression time: 0.072055493s
│    └── sample time: 5.2623e-05s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    └── bs"0000" => 1000
├── 1 executions
├── 0 amplitudes
└── 1000 samples]

Result of the first circuit

>>> res = conn.get_result(job)
Warning: Multiple results found. Returning the first one.
>>> res
QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 0.00011103s
│    ├── apply time: 2.7497e-05s
│    ├── total time: 0.000326494s
│    ├── compression time: 8.657e-06s
│    └── sample time: 0.000110076s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    ├── bs"1011011100" => 6
│    ├── bs"0110001101" => 4
│    ├── bs"0001011001" => 4
│    ├── bs"1011010010" => 4
│    └── bs"1000000001" => 4
├── 1 executions
├── 0 amplitudes
└── 1000 samples

Input parameters and List of the input Circuits

>>> circs, parameters = conn.get_inputs(job)
Downloaded files: ['circuit1.pb', 'circuit2.pb', 'circuits.json', 'request.json']
>>> circs
[10-qubit circuit with 10 instructions:
├── H @ q[0]
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── H @ q[4]
├── H @ q[5]
├── H @ q[6]
├── H @ q[7]
├── H @ q[8]
└── H @ q[9]
, 4-qubit circuit with 1 instructions:
└── C₂H @ q[0,1], q[3]
]
>>> parameters
{'algorithm': 'auto', 'bitstrings': [], 'samples': 1000, 'seed': 1550300089630762344, 'circuits': [{'file': 'circuit1.pb', 'type': 'proto'}, {'file': 'circuit2.pb', 'type': 'proto'}], 'bondDimension': 256, 'entDimension': 16}

Input parameters and first input Circuit

>>> circ, parameters = conn.get_input(job)
Downloaded files: ['circuit1.pb', 'circuit2.pb', 'circuits.json', 'request.json']
Warning: Multiple results found. Returning the first one.
>>> circ
10-qubit circuit with 10 instructions:
├── H @ q[0]
├── H @ q[1]
├── H @ q[2]
├── H @ q[3]
├── H @ q[4]
├── H @ q[5]
├── H @ q[6]
├── H @ q[7]
├── H @ q[8]
└── H @ q[9]

>>> parameters
{'algorithm': 'auto', 'bitstrings': [], 'samples': 1000, 'seed': 1550300089630762344, 'circuits': [{'file': 'circuit1.pb', 'type': 'proto'}, {'file': 'circuit2.pb', 'type': 'proto'}], 'bondDimension': 256, 'entDimension': 16}

Connecting Using Credentials#

conn = MimiqConnection(url="https://mimiq.qperfect.io/api")
conn.connect("Email_address", "Password")

Saving and Loading Tokens#

conn.savetoken("qperfect.json")
conn.loadtoken("qperfect.json")

Closing a Connection and Checking Connection Status#

conn.close()

conn.isOpen()
get_input(execution, **kwargs)[source]#

Retrieve the first circuit and parameters of the execution.

Parameters:

execution (str) – The execution identifier.

Returns:

A tuple containing the first Circuit object and parameters (dict).

Return type:

tuple

Raises:

RuntimeError – If required files are not found in the inputs.

get_inputs(execution)[source]#

Retrieve the inputs (circuits and parameters) of the execution.

Parameters:

execution (str) – The execution identifier.

Returns:

A tuple containing a list of Circuit objects and parameters (dict).

Return type:

tuple

Raises:

RuntimeError – If required files are not found in the inputs.

get_result(execution, **kwargs)[source]#

Retrieve the first result if multiple are found.

Parameters:
  • execution (str) – The execution identifier.

  • **kwargs – Additional keyword arguments for result retrieval.

Returns:

The first result found.

Return type:

QCSResults

Raises:

RuntimeWarning – If multiple results are found.

get_results(execution, interval=1)[source]#

Retrieve the results of a completed execution.

Parameters:
  • execution (str) – The execution identifier.

  • interval (int) – The interval (in seconds) for checking job status (default: 1).

Returns:

A list of QCSResults instances.

Return type:

List[QCSResults]

Raises:

RuntimeError – If the remote job encounters an error.

class mimiqcircuits.MixedUnitary(*args)[source]#

Bases: krauschannel

MixedUnitary(p,umatrices).

Custom N-qubit mixed unitary channel specified by a list of \(2^N \times 2^N\) unitary matrices and a list of probabilities that add up to 1.

A mixed unitary noise channel is defined by:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(0 \leq p_k \leq 1\) and \(U_k\) are unitary matrices. The probabilities must fulfill \(\sum_k p_k = 1\).

If your Kraus matrices are not all proportional to unitaries, use KrausChannel() instead.

The unitary matrices are defined in the computational basis in the usual textbook order (the first qubit corresponds to the left-most qubit). For 1 qubit, we have \(|0\rangle\), \(|1\rangle\). For 2 qubits, we have \(|00\rangle\), \(|01\rangle\), \(|10\rangle\), \(|11\rangle\).

Note: Currently, only 1 and 2-qubit custom MixedUnitary channels are supported.

Parameters:
  • p (list) – List of probabilities, must be positive real numbers and add up to 1.

  • umatrices (list) – List of complex-valued \(2^N \times 2^N\) matrices. The number of qubits is equal to \(N\).

The length of the lists p and umatrices must be equal.

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> c = Circuit()
>>> c.push(MixedUnitary([0.9, 0.1], [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [1, 0]])]), 0)
1-qubit circuit with 1 instructions:
└── MixedUnitary((0.9, "Custom([1 0; 0 1])"),(0.1, "Custom([0 1; 1 0])")) @ q[0]
>>> c.push(MixedUnitary([0.8, 0.2], [Matrix(GateID().matrix()), Matrix(GateRX(0.2).matrix())]), 1)
2-qubit circuit with 2 instructions:
├── MixedUnitary((0.9, "Custom([1 0; 0 1])"),(0.1, "Custom([0 1; 1 0])")) @ q[0]
└── MixedUnitary((0.8, "Custom([1.0 0; 0 1.0])"),(0.2, "Custom([0.995004165278026 -0.0 - 0.0998334166468282*I; -0.0 - 0.0998334166468282*I 0.995004165278026])")) @ q[1]

RescaleGate

>>> p1= 0.2
>>> p2 = 0.8
>>> U1 = Matrix([[1, 0], [0, 1]])  # Identity matrix
>>> U2 = Matrix([[0, 1], [1, 0]])  # Pauli-X matrix
>>> gate1 = GateCustom(U1)
>>> gate2 = GateCustom(U2)
>>> rescaled_gate1 = RescaledGate(gate1, sqrt(p1))
>>> rescaled_gate2 = RescaledGate(gate2, sqrt(p2))
>>> op = MixedUnitary([rescaled_gate1, rescaled_gate2])
>>> op
MixedUnitary((0.2, "Custom([1 0; 0 1])"), (0.8, "Custom([0 1; 1 0])"))
>>> c.push(op,1)
2-qubit circuit with 3 instructions:
├── MixedUnitary((0.9, "Custom([1 0; 0 1])"),(0.1, "Custom([0 1; 1 0])")) @ q[0]
├── MixedUnitary((0.8, "Custom([1.0 0; 0 1.0])"),(0.2, "Custom([0.995004165278026 -0.0 - 0.0998334166468282*I; -0.0 - 0.0998334166468282*I 0.995004165278026])")) @ q[1]
└── MixedUnitary((0.2, "Custom([1 0; 0 1])"),(0.8, "Custom([0 1; 1 0])")) @ q[1]
evaluate(values: dict)[source]#

Evaluates symbolic parameters in the MixedUnitary using a dictionary of values.

Parameters:

values (dict) – A dictionary where keys are symbolic variables and values are the corresponding numerical values.

Returns:

A new MixedUnitary instance with evaluated parameters.

Return type:

MixedUnitary

classmethod ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
unwrappedkrausmatrices()[source]#

Returns the unitary Kraus matrices associated to the mixed unitary Kraus channel without symbolic wrapper.

Example

>>> from mimiqcircuits import *
>>> op = PauliX(0.2)
>>> op.unwrappedkrausmatrices()
[[0.894427190999916, 0]
[0, 0.894427190999916]
, [0, 0.447213595499958]
[0.447213595499958, 0]
]
class mimiqcircuits.Not[source]#

Bases: Operation

Not operation.

Represents a NOT operation that can be added to quantum circuits. This operation inverts a classical bit.

Examples

>>> from mimiqcircuits import *
>>> not_op = Not()
>>> not_op.name
'!'
>>> c = Circuit()
>>> c.push(Not(), 1)
0-qubit circuit with 1 instructions:
└── ! @ c[1]
get_operation()[source]#
inverse()[source]#
iswrapper()[source]#
class mimiqcircuits.ObservableInclude(*args)[source]#

Bases: AbstractAnnotation

ObservableInclude operation for including observable index.

An annotation class for adding measurement records to a specified logical observable within a quantum circuit. Observables are sets of measurements expected to produce a deterministic result, used to track specific logical qubit states across operations.

The ObservableInclude class tags a group of measurement records as a logical observable, representing a consistent, predictable result under noiseless conditions. This grouping allows for tracking the state of logical qubits across circuit operations, which is crucial for error correction. Logical observables monitor encoded qubit states by combining multiple measurements, providing robustness against noise and helping to identify any deviations that indicate potential errors.

Examples

Adding ObservableInclude to a circuit:

>>> from mimiqcircuits import *
>>> ObservableInclude(2)
ObservableInclude()
>>> c= Circuit()
>>> c.push(ObservableInclude(1), 0)
0-qubit circuit with 1 instructions:
└── ObservableInclude() @ c[0]
get_notes()[source]#
iswrapper()[source]#
static opname()[source]#
class mimiqcircuits.Operation[source]#

Bases: ABC

Abstract base class for quantum operations.

asciiwidth(qubits, bits, zvars)[source]#
copy()[source]#
property cregsizes#
decompose()[source]#
deepcopy()[source]#
evaluate(d)[source]#
get_operation()[source]#
getparam(pn)[source]#
getparams()[source]#
is_symbolic()[source]#
isidentity()[source]#
isopalias()[source]#
abstract iswrapper()[source]#
property name#
property num_bits#
property num_cregs#
property num_qregs#
property num_qubits#
property num_zvars#
numparams()[source]#
property parnames#
property qregsizes#
property zregsizes#
class mimiqcircuits.Operator(mat)[source]#

Bases: AbstractOperator

N-qubit operator specified by a \(2^N \times 2^N\) matrix.

Note

Only one and two qubit operators are supported.

This operator does not have to be unitary.

See also

AbstractOperator, ExpectationValue, KrausChannel

Parameters:

matrix (list or np.ndarray) – The \(2^N \times 2^N\) matrix representing the operator.

Examples

>>> from mimiqcircuits import *
>>> from symengine import Matrix
>>> op = Operator(Matrix([[1, 2], [3, 4]]))
>>> op
1-qubit Operator:
├── 1 2
└── 3 4
>>> op = Operator(Matrix([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 1]]))
>>> op
2-qubit Operator:
├── 1 0 0 1
├── 0 0 0 0
├── 0 0 0 0
└── 1 0 0 1

Operators can be used for expectation values:

>>> c = Circuit()
>>> c.push(ExpectationValue(Operator(Matrix([[0, 1], [0, 0]]))), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨Operator([[0, 1], [0, 0]])⟩ @ q[1], z[1]
static is_valid_power_of_2(n)[source]#

Check if a number is a power of 2.

iswrapper()[source]#

Placeholder method.

opname()[source]#

Return the operator name.

opsquared()[source]#

Return the operator squared (O’ * O).

property parnames#

Return the parameter names.

pretty_print()[source]#

Return a detailed string representation of the operator.

rescale(scale)[source]#

Return a new rescaled Operator by scaling the matrix.

rescale_inplace(scale)[source]#

In-place rescaling of the operator matrix.

unwrappedmatrix()[source]#

Return the matrix unwrapped (if needed).

class mimiqcircuits.Parallel(num_repeats, op: Gate)[source]#

Bases: Gate

Parallel operation

This is a composite operation that applies multiple gates in parallel to the circuit at once.

Examples

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Parallel(3,GateX()),1,2,3)
4-qubit circuit with 1 instructions:
└── ⨷ ³ X @ q[1], q[2], q[3]
control(*args)[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
get_operation()[source]#
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

property num_repeats#
property op#
parallel(*args)[source]#
power(*args)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.PauliNoise(p: List[float | int], paulistr: List[str])[source]#

Bases: krauschannel

N-qubit Pauli noise channel specified by a list of probabilities and Pauli gates.

A Pauli channel is defined by:

\[\mathcal{E}(\rho) = \sum_k p_k P_k \rho P_k,\]

where \(0 \leq p_k \leq 1\) and \(P_k\) are Pauli string operators, defined as tensor products of one-qubit Pauli operators. The probabilities must fulfill \(\sum_k p_k = 1\).

This channel is a mixed unitary channel (see ismixedunitary()).

See also

Depolarizing, PauliX, PauliY, PauliZ, which are special cases of PauliNoise.

Parameters:
  • p (list) – List of probabilities that must add up to 1.

  • paulistrings (list) – List of strings, each of length \(N\), with each character being either “I”, “X”, “Y”, or “Z”. The number of qubits is equal to \(N\).

The lengths of p and paulistrings must be the same.

Examples

PauliNoise channels can be defined for any number of qubits, and for any number of Pauli strings:

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PauliNoise([0.8, 0.1, 0.1], ["I", "X", "Y"]), 1)
2-qubit circuit with 1 instructions:
└── PauliNoise((0.8, pauli"I"), (0.1, pauli"X"), (0.1, pauli"Y")) @ q[1]
>>> c.push(PauliNoise([0.9, 0.1], ["XY", "II"]), 1, 2)
3-qubit circuit with 2 instructions:
├── PauliNoise((0.8, pauli"I"), (0.1, pauli"X"), (0.1, pauli"Y")) @ q[1]
└── PauliNoise((0.9, pauli"XY"), (0.1, pauli"II")) @ q[1]
>>> c.push(PauliNoise([0.5, 0.2, 0.2, 0.1], ["IXIX", "XYXY", "ZZZZ", "IXYZ"]), 1, 2, 3, 4)
5-qubit circuit with 3 instructions:
├── PauliNoise((0.8, pauli"I"), (0.1, pauli"X"), (0.1, pauli"Y")) @ q[1]
├── PauliNoise((0.9, pauli"XY"), (0.1, pauli"II")) @ q[1]
└── PauliNoise((0.5, pauli"IXIX"), (0.2, pauli"XYXY"), (0.2, pauli"ZZZZ"), (0.1, pauli"IXYZ")) @ q[1]
evaluate(d={})[source]#
classmethod ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
iswrapper()[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

property num_qubits#
property parnames#
probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
unwrappedkrausmatrices()[source]#

Returns the unitary Kraus matrices associated to the mixed unitary Kraus channel without symbolic wrapper.

Example

>>> from mimiqcircuits import *
>>> op = PauliX(0.2)
>>> op.unwrappedkrausmatrices()
[[0.894427190999916, 0]
[0, 0.894427190999916]
, [0, 0.447213595499958]
[0.447213595499958, 0]
]
class mimiqcircuits.PauliString(pauli: str | chr)[source]#

Bases: Gate

PauliString.

N-qubit tensor product of Pauli operators.

The PauliString gate can represent any N-qubit tensor product of operators of the form P_1 ⊗ P_2 ⊗ P_3 ⊗ … ⊗ P_N, where each P_i ∈ { I, X, Y, Z } is a Pauli operator, including the identity.

This gate can be initialized by passing as argument a string of length N where each element is either ‘I’, ‘X’, ‘Y’, or ‘Z’. For example, PauliString(“IXXYZ”)

Examples

>>> from mimiqcircuits import *
>>> g= PauliString("XYZ")
>>> g.matrix()
[0, 0, 0, 0, 0, 0, -0.0 - 1.0*I, 0]
[0, 0, 0, 0, 0, 0, 0, 0.0 + 1.0*I]
[0, 0, 0, 0, 0.0 + 1.0*I, 0, 0, 0]
[0, 0, 0, 0, 0, -0.0 - 1.0*I, 0, 0]
[0, 0, -0.0 - 1.0*I, 0, 0, 0, 0, 0]
[0, 0, 0, 0.0 + 1.0*I, 0, 0, 0, 0]
[0.0 + 1.0*I, 0, 0, 0, 0, 0, 0, 0]
[0, -0.0 - 1.0*I, 0, 0, 0, 0, 0, 0]

>>> c= Circuit()
>>> c.push(PauliString("XYZ"),0,1,2)
3-qubit circuit with 1 instructions:
└── XYZ @ q[0,1,2]

>>> c.decompose()
3-qubit circuit with 3 instructions:
├── X @ q[0]
├── Y @ q[1]
└── Z @ q[2]
asciiwidth(qubits, bits, zvars)[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
classmethod from_string(pauli_expr: str)[source]#
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

is_identity()[source]#
isidentity()[source]#
property num_qubits#
property parnames#
unwrapped_matrix()[source]#
class mimiqcircuits.PauliX(p: float | int)[source]#

Bases: krauschannel

One-qubit Pauli X noise channel (bit flip error).

This channel is defined by the Kraus operators:

\[E_1 = \sqrt{1-p}\,I, \quad E_2 = \sqrt{p}\,X,\]

where \(0 \leq p \leq 1\).

This channel is a mixed unitary channel (see ismixedunitary()), and is a special case of PauliNoise.

PauliX(p) is equivalent to PauliNoise([1-p, p], [“I”, “X”]).

Parameters:

p (float) – Probability of a bit flip error, must be in the range [0, 1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PauliX(0.1), 1)
2-qubit circuit with 1 instructions:
└── PauliX(0.1) @ q[1]
evaluate(d={})[source]#
static ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
class mimiqcircuits.PauliY(p: float | int)[source]#

Bases: krauschannel

One-qubit Pauli Y noise channel (bit-phase flip error).

This channel is determined by the Kraus operators:

\[E_1 = \sqrt{1-p}\,I, \quad E_2 = \sqrt{p}\,Y,\]

where \(0 \leq p \leq 1\).

This channel is a mixed unitary channel (see ismixedunitary()), and is a special case of PauliNoise.

PauliY(p) is equivalent to PauliNoise([1-p, p], [“I”, “Y”]).

Parameters:

p (float) – Probability of a bit-phase flip error, must be in the range [0, 1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PauliY(0.1), 1)
2-qubit circuit with 1 instructions:
└── PauliY(0.1) @ q[1]
evaluate(d={})[source]#
static ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
class mimiqcircuits.PauliZ(p: float | int)[source]#

Bases: krauschannel

One-qubit Pauli Z noise channel (phase flip error).

This channel is determined by the Kraus operators:

\[E_1 = \sqrt{1-p}\,I, \quad E_2 = \sqrt{p}\,Z,\]

where \(0 \leq p \leq 1\).

This channel is a mixed unitary channel (see ismixedunitary()), and is a special case of PauliNoise.

PauliZ(p) is equivalent to PauliNoise([1-p, p], [“I”, “Z”]).

Parameters:

p (float) – Probability of a phase flip error, must be in the range [0, 1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PauliZ(0.1), 1)
2-qubit circuit with 1 instructions:
└── PauliZ(0.1) @ q[1]
evaluate(d={})[source]#
static ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
class mimiqcircuits.PhaseAmplitudeDamping(p: float | int, gamma: float | int, beta: float | int)[source]#

Bases: krauschannel

One-qubit phase amplitude damping noise channel.

This channel is defined by:

\[\begin{split}\mathcal{E}(\rho) = \begin{pmatrix} (1-\gamma)\rho_{00} + \gamma p & (1-2\beta)\sqrt{1-\gamma}\rho_{01} \\ (1-2\beta)\sqrt{1-\gamma}\rho_{10} & (1-\gamma)\rho_{11} + (1-p)\gamma \end{pmatrix}\end{split}\]

Here, \(p, \gamma, \beta \in [0,1]\).

This channel is equivalent to a GeneralizedAmplitudeDamping(p, gamma) channel (see GeneralizedAmplitudeDamping), followed by a PauliZ(beta) channel (see PauliZ).

Use krausmatrices() to see a Kraus matrix representation of the channel.

Parameters:
  • p (float) – Probability parameter, must be in the range [0, 1].

  • γ (float) – Damping parameter, must be in the range [0, 1].

  • β (float) – Phase flip parameter, must be in the range [0, 1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PhaseAmplitudeDamping(0.1, 0.2, 0.3), 1)
2-qubit circuit with 1 instructions:
└── PhaseAmplitudeDamping(0.1, 0.2, 0.3) @ q[1]
evaluate(d={})[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

property parnames#
class mimiqcircuits.PhaseGradient(*args)[source]#

Bases: Gate

Phase Gradient gate

A phase gradient gate applies a phase shift to a quantum register of n qubits, where each computational basis state |k⟩ experiences a phase proportional to its integer value k

Parameters:

n (int) – The number of qubits in the quantum register.

Returns:

The PhaseGradient gate.

Return type:

PhaseGradient

name#

The name of the operation.

Type:

str

num_qubits#

The number of qubits in the quantum register.

Type:

int

qregsizes#

The sizes of the quantum registers.

Type:

list of int

Examples

>>> from mimiqcircuits import *
>>> c=Circuit()
>>> c.push(PhaseGradient(2),9,8)
10-qubit circuit with 1 instructions:
└── PhaseGradient @ q[9,8]
class mimiqcircuits.PolynomialOracle(*args)[source]#

Bases: Gate

Polynomial Oracle.

Parameters:
  • a (Num) – Coefficient a in the polynomial.

  • b (Num) – Coefficient b in the polynomial.

  • c (Num) – Coefficient c in the polynomial.

  • d (Num) – Coefficient d in the polynomial.

Raises:

ValueError – If the input parameters do not satisfy the required conditions.

Returns:

The Polynomial Oracle.

Return type:

PolynomialOracle

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(PolynomialOracle(5,5,1, 2, 3, 4), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
11-qubit circuit with 1 instructions:
└── PolynomialOracle(1, 2, 3, 4) @ q[1,2,3,4,5], q[6,7,8,9,10]
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.Power(operation, exponent, *args, **kwargs)[source]#

Bases: Gate

Power operation.

Represents a Power operation raised to a specified exponent.

Examples

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Power(GateX(),1/2),1)
2-qubit circuit with 1 instructions:
└── SX @ q[1]

>>> c.push(Power(GateX(),5),1)
2-qubit circuit with 2 instructions:
├── SX @ q[1]
└── X**5 @ q[1]

>>> c.decompose()
2-qubit circuit with 6 instructions:
├── U(1.5707963267948966, -1.570796326794897, 1.5707963267948961, 0.7853981633974482) @ q[1]
├── X @ q[1]
├── X @ q[1]
├── X @ q[1]
├── X @ q[1]
└── X @ q[1]
control(*args)[source]#
decompose()[source]#
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
property exponent#
getparams()[source]#
inverse()[source]#

Raise an error, as non-unitary operators cannot be inverted.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Raises:

NotImplementedError – If the method is called.

iswrapper()[source]#

Check if the operator is a wrapper around another operator.

This method should be overridden in subclasses to return True if the operator is acting as a wrapper around another operation or object, and False otherwise.

Returns:

Always returns False in the base class. Subclasses should override this method to provide the appropriate logic.

Return type:

bool

property op#
parallel(*args)[source]#
power(*args)[source]#

Raise an error, as powers of non-unitary operators are not supported.

This method is not implemented for non-unitary operators and will raise a NotImplementedError if called.

Parameters:

n (int) – The exponent to which the operator would be raised.

Raises:

NotImplementedError – If the method is called.

class mimiqcircuits.ProjectiveNoise(basis)[source]#

Bases: krauschannel

Single qubit projection noise onto a Pauli basis.

This channel is defined by the Kraus operators:

\[E_1 = |\alpha\rangle \langle\alpha|, \quad E_2 = |\beta\rangle \langle\beta|,\]

where the states \(|\alpha\rangle\) and \(|\beta\rangle\) are the +1 and -1 eigenstates of a Pauli operator. Specifically, they correspond to \(\{ |0\rangle, |1\rangle \}\) (Z basis), \(\{ |+\rangle, |-\rangle \}\) (X basis), or \(\{ |y+\rangle, |y-\rangle \}\) (Y basis).

This operation is similar to measuring in the corresponding basis (X, Y, or Z), except that the outcome of the measurement is not stored, i.e., there is a loss of information.

Parameters:

basis (str) – String or character that selects the Pauli basis, “X”, “Y”, or “Z”.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(ProjectiveNoise("Z"), 1)
2-qubit circuit with 1 instructions:
└── ProjectiveNoiseZ @ q[1]

The Kraus matrices are given by:

>>> ProjectiveNoise("X").krausmatrices()
[[0.5, 0.5]
[0.5, 0.5]
, [0.5, -0.5]
[-0.5, 0.5]
]
>>> ProjectiveNoise("Y").krausmatrices()
[[0.5, -0.0 - 0.5*I]
[0.0 + 0.5*I, 0.5]
, [0.5, 0.0 + 0.5*I]
[-0.0 - 0.5*I, 0.5]
]
>>> ProjectiveNoise("Z").krausmatrices()
[[1.0, 0]
[0, 0]
, [0, 0]
[0, 1.0]
]
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

class mimiqcircuits.ProjectiveNoiseX[source]#

Bases: krauschannel

Single qubit projection noise onto an X Pauli basis.

This channel is defined by the Kraus operators:

\[E_1 = |-\rangle \langle-|, \quad E_2 = |+\rangle \langle+|,\]

where \(\ket{+}\) and \(\ket{-}\) are the eigenstates of Pauli X.

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

opname()[source]#
class mimiqcircuits.ProjectiveNoiseY[source]#

Bases: krauschannel

Single qubit projection noise onto a Y Pauli basis.

This channel is defined by the Kraus operators:

\[E_1 = |Y0\rangle \langle Y0|, \quad E_2 = |Y1\rangle \langle Y1|,\]

where \(\ket{Y0}\) and \(\ket{Y1}\) are the eigenstates of Pauli Y.

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

opname()[source]#
class mimiqcircuits.ProjectiveNoiseZ[source]#

Bases: krauschannel

Single qubit projection noise onto a Z Pauli basis.

This channel is defined by the Kraus operators:

\[E_1 = |0\rangle \langle Z0|, \quad E_2 = |1\rangle \langle Z1|,\]

where \(\ket{0}\) and \(\ket{1}\) are the eigenstates of Pauli Z.

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

opname()[source]#
class mimiqcircuits.Projector0(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|0\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} a & 0 \\ 0 & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling. Equivalent to DiagonalOp(a, 0).

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – The top-left entry of the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector0()
P₀(1)
>>> Projector0(0.5)
P₀(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector0()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨P₀(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.Projector00(a=1)[source]#

Bases: AbstractOperator

Two-qubit operator corresponding to a projection onto \(|00\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} a & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector00()
P₀₀(1)
>>> Projector00(0.5)
P₀₀(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector00()), 1, 2, 1)
3-qubit circuit with 1 instructions:
└── ⟨P₀₀(1)⟩ @ q[1,2], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.Projector01(a=1)[source]#

Bases: AbstractOperator

Two-qubit operator corresponding to a projection onto \(|01\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & a & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector01()
P₀₁(1)
>>> Projector01(0.5)
P₀₁(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector01()), 1, 2, 1)
3-qubit circuit with 1 instructions:
└── ⟨P₀₁(1)⟩ @ q[1,2], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.Projector1(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|1\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & 0 \\ 0 & a \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling. Equivalent to DiagonalOp(0, a).

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – The bottom-right entry of the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector1()
P₁(1)
>>> Projector1(0.5)
P₁(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector1()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨P₁(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.Projector10(a=1)[source]#

Bases: AbstractOperator

Two-qubit operator corresponding to a projection onto \(|10\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & a & 0\\ 0 & 0 & 0 & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector10()
P₁₀(1)
>>> Projector10(0.5)
P₁₀(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector10()), 1, 2, 1)
3-qubit circuit with 1 instructions:
└── ⟨P₁₀(1)⟩ @ q[1,2], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.Projector11(a=1)[source]#

Bases: AbstractOperator

Two-qubit operator corresponding to a projection onto \(|11\rangle\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0\\ 0 & 0 & 0 & a \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> Projector11()
P₁₁(1)
>>> Projector11(0.5)
P₁₁(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(Projector11()), 1, 2, 1)
3-qubit circuit with 1 instructions:
└── ⟨P₁₁(1)⟩ @ q[1,2], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.ProjectorX0(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|+\rangle\).

Matrix Representation

\[\begin{split}\frac{a}{2} \begin{pmatrix} 1 & 1 \\ 1 & 1 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

ProjectorX1

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> ProjectorX0()
PX₀(1)
>>> ProjectorX0(0.5)
PX₀(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(ProjectorX0()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨PX₀(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.ProjectorX1(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|-\rangle\).

Matrix Representation

\[\begin{split}\frac{a}{2} \begin{pmatrix} 1 & -1 \\ -1 & 1 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

ProjectorX0

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> ProjectorX1()
PX₁(1)
>>> ProjectorX1(0.5)
PX₁(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(ProjectorX1()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨PX₁(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.ProjectorY0(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|y+\rangle\).

Matrix Representation

\[\begin{split}\frac{a}{2} \begin{pmatrix} 1 & -i \\ i & 1 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

ProjectorY1

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> ProjectorY0()
PY₀(1)
>>> ProjectorY0(0.5)
PY₀(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(ProjectorY0()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨PY₀(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.ProjectorY1(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to a projection onto \(|y-\rangle\).

Matrix Representation

\[\begin{split}\frac{a}{2} \begin{pmatrix} 1 & i \\ -i & 1 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

ProjectorY0

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> ProjectorY1()
PY₁(1)
>>> ProjectorY1(0.5)
PY₁(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(ProjectorY1()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨PY₁(1)⟩ @ q[1], z[1]
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.ProjectorZ0[source]#

Bases: AbstractOperator

Alias for Projector0

class mimiqcircuits.ProjectorZ1[source]#

Bases: AbstractOperator

Alias for Projector1

class mimiqcircuits.QCSResults(simulator=None, version=None, fidelities=None, avggateerrors=None, cstates=None, zstates=None, amplitudes=None, timings=None)[source]#

Bases: object

Represents the results of quantum computations obtained from quantum cloud services (QCS).

Parameters:
  • simulator (str) – The name of the quantum simulator.

  • version (str) – The version of the quantum simulator.

  • fidelities (list) – List of fidelity estimates from different executions.

  • avggateerrors (list) – List of average multi-qubit gate errors from different executions.

  • cstates (list) – List of classical states obtained from executions.

  • zstates (list) – Not used in the current implementation.

  • amplitudes (dict) – Dictionary of statevector amplitudes for different quantum states.

  • timings (dict) – Dictionary of timing information for different phases of the computation.

histogram()[source]#

Histogram of the obtained classical states’ occurrences.

Returns:

A dictionary of classical states (bitarray) and their occurrences (float).

Raises:

TypeError – If a non QCSResults object is passed.

histzvars()[source]#

Histogram of the obtained zstates’ occurrences.

static loadproto(file)[source]#

Load QCSResults object from a Protocol Buffers file.

The loadproto() method is a static method and should be called on the class, not on an instance of the class.

Note

Look for example in QCSResults.saveproto()

saveproto(file)[source]#

Save QCSResults object to a Protocol Buffers file.

Examples

>>> from mimiqcircuits import *
>>> import os
>>> from symengine import *
>>> import tempfile
>>> x, y = symbols("x y")
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instructions:
└── H @ q[0]

>>> conn = MimiqConnection(os.getenv("MIMIQCLOUD2"))
>>> conn.connect(os.getenv("MIMIQUSER"), os.getenv("MIMIQPASS"))
Connection:
├── url: https://mimiqfast.qperfect.io/api
├── Computing time: 597/10000 minutes
├── Executions: 451/10000
├── Max time limit per request: 180 minutes
└── status: open

>>> job = conn.execute(c)
>>> res = conn.get_result(job)
>>> res
QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 5.9552e-05s
│    ├── apply time: 1.6682e-05s
│    ├── total time: 0.00019081399999999998s
│    ├── compression time: 4.575e-06s
│    └── sample time: 5.299e-05s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    ├── bs"1" => 513
│    └── bs"0" => 487
├── 1 executions
├── 0 amplitudes
└── 1000 samples
>>> tmpfile = tempfile.NamedTemporaryFile(suffix=".pb", delete=True)
>>> res.saveproto(tmpfile.name)
7169
>>> res.loadproto(tmpfile.name)
QCSResults:
├── simulator: MIMIQ-StateVector 0.18.0
├── timings:
│    ├── parse time: 5.9552e-05s
│    ├── apply time: 1.6682e-05s
│    ├── total time: 0.00019081399999999998s
│    ├── compression time: 4.575e-06s
│    └── sample time: 5.299e-05s
├── fidelity estimate: 1
├── average multi-qubit gate error estimate: 0
├── most sampled:
│    ├── bs"1" => 513
│    └── bs"0" => 487
├── 1 executions
├── 0 amplitudes
└── 1000 samples
Note:

This example uses a temporary file to demonstrate the save and load functionality. You can save your file with any name at any location using:

res.saveproto("example.pb")
res.loadproto("example.pb")
class mimiqcircuits.QFT(*args)[source]#

Bases: Gate

Quantum Fourier transform.

Performs the quantum Fourier transform on a register of n qubits.

Parameters:

n (int) – The number of qubits in the quantum register.

Raises:

ValueError – If the number of qubits is less than 1.

Returns:

The Quantum Fourier Transform operation.

Return type:

QFT

name#

The name of the operation.

Type:

str

num_qubits#

The number of qubits in the quantum register.

Type:

int

qregsizes#

The sizes of the quantum registers.

Type:

list of int

Examples

>>> from mimiqcircuits import *
>>> c=Circuit()
>>> c.push(QFT(2),1,2)
3-qubit circuit with 1 instructions:
└── QFT @ q[1,2]
class mimiqcircuits.QubitCoordinates(*args)[source]#

Bases: AbstractAnnotation

QubitCoordinates operation for specifying qubit positions.

An annotation class used to specify the spatial location of a qubit in a quantum circuit. Coordinates do not affect simulation results but are useful for visualizing and organizing qubit layouts within the circuit.

Examples

Adding QubitCoordinates to a circuit:

>>> from mimiqcircuits import *
>>> QubitCoordinates([0.5, 0.75, 1.0])
QubitCoordinates(0.5, 0.75, 1.0)
>>> op = QubitCoordinates([0.5, 0.75, 1.0])
>>> op.get_notes()
[0.5, 0.75, 1.0]
>>> c= Circuit()
>>> c.push(QubitCoordinates([0.2, 0.3]), 0)
1-qubit circuit with 1 instructions:
└── QubitCoordinates(0.2, 0.3) @ q[0]
get_notes()[source]#
iswrapper()[source]#
static opname()[source]#
class mimiqcircuits.RescaledGate(gate: T, p)[source]#

Bases: AbstractOperator, Generic[T]

RescaledGate operation.

The RescaledGate represents an operation where a quantum gate is rescaled by a factor p, typically between 0 and 1. This rescaling modifies the action of the gate, multiplying its matrix representation by p. The gate to be rescaled must be an instance of the Gate class, and p must be a valid scalar (real or symbolic) in the range [0, 1].

Parameters:
  • gate (-) – The quantum gate to be rescaled.

  • p (-) – The rescaling factor, must be between 0 and 1.

Raises:
  • - TypeError – If the provided gate is not an instance of Gate.

  • - ValueError – If the rescaling factor p is not between 0 and 1.

- get_operation()

Returns the underlying gate.

- get_param(name)

Retrieves the value of a parameter (such as p).

- _matrix(*args)

Returns the matrix representation of the rescaled gate.

- get_scale()

Returns the scaling factor p.

- rescale(p)

Returns a new RescaledGate with a different scaling factor.

- rescale_in_place(p)

Rescales the gate in place by multiplying the current p with the new value.

- evaluate(d)

Substitutes values in the parameters (useful when symbolic values are used).

Examples

Creating and rescaling a gate:

>>> from mimiqcircuits import *
>>> g = GateH()
>>> rg = RescaledGate(g, 0.5)
>>> rg.get_scale()
0.5
>>> rg.rescale(0.8)
0.4*H
evaluate(d)[source]#

Substitute the symbolic parameters of the operator with numerical values.

This method evaluates the operator’s symbolic parameters using the values provided in the dictionary d. If the operator has no parameters, it returns the same instance. Otherwise, it creates a new instance of the operator with updated numerical parameters.

Parameters:

d (dict) – A dictionary where keys are symbolic parameter names and values are values for substitution.

Example

>>> from symengine import *
>>> from mimiqcircuits import *
>>> theta = symbols('theta')
>>> op = GateRX(theta)
>>> evaluated_op = op.evaluate({'theta': 0.5})
>>> print(evaluated_op)
RX(0.5)
get_operation()[source]#
get_param(name)[source]#
get_scale()[source]#
rescale(p)[source]#
rescale_in_place(p)[source]#
class mimiqcircuits.Reset[source]#

Bases: krauschannel

Reset operation.

Quantum operation that resets the status of one qubit to the \(\ket{0}\) state.

Warning

This operation is non-reversible.

Examples

Adding Reset operation to the Circuit (The args can be: range, list, tuple, set or int)

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Reset(), 0)
1-qubit circuit with 1 instructions:
└── Reset @ q[0]

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Reset(),(0,1,2))
3-qubit circuit with 3 instructions:
├── Reset @ q[0]
├── Reset @ q[1]
└── Reset @ q[2]

See also

ResetX, ResetY, ResetZ - Variants of the Reset operation along different axes.

asciiwidth(qubits, bits, zvars)[source]#
control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
krausoperators(normaliz=None)[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

power(pwr)[source]#
class mimiqcircuits.ResetX[source]#

Bases: krauschannel

ResetX operation.

This operation is performed by applying a Hadamard gate (H) to the qubit, which transforms the qubit from the computational basis (\(\ket{0}\) or \(\ket{1}\)) to the superposition basis (\(\ket{+}\) or \(\ket{-}\)). After applying the Hadamard gate, a standard Reset operation is performed, resetting the qubit to the \(\ket{0}\) state. Finally, another Hadamard gate is applied to convert the \(\ket{0}\) state back into the \(\ket{+}\) state.

Warning

This operation is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(ResetX(), 0)
1-qubit circuit with 1 instructions:
└── ResetX @ q[0]

>>> c.decompose()
1-qubit circuit with 3 instructions:
├── H @ q[0]
├── Reset @ q[0]
└── H @ q[0]

See also

Reset, ResetY, ResetZ - Variants of the Reset operation along different axes.

control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
power(pwr)[source]#
class mimiqcircuits.ResetY[source]#

Bases: krauschannel

ResetY operation.

The ResetY operation works by first rotating the qubit’s state so that the Y-axis aligns with the Z-axis, applying a standard reset (which resets the qubit to the \(\ket{0}\) state), and then rotating the qubit back to its original basis.

Warning

This operation is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(ResetY(), 0)
1-qubit circuit with 1 instructions:
└── ResetY @ q[0]

>>> c.decompose()
1-qubit circuit with 3 instructions:
├── HYZ @ q[0]
├── Reset @ q[0]
└── HYZ @ q[0]

See also

Reset, ResetX, ResetZ - Variants of the Reset operation along different axes.

control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
power(pwr)[source]#
class mimiqcircuits.ResetZ[source]#

Bases: krauschannel

ResetZ operation.

Quantum operation that resets the status of one qubit to the \(\ket{0}\) state along the Z-axis. This operation is an alias of the Reset operation.

Warning

This operation is non-reversible.

Examples

>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(ResetZ(), 0)
1-qubit circuit with 1 instructions:
└── Reset @ q[0]

>>> c.decompose()
1-qubit circuit with 1 instructions:
└── Reset @ q[0]

See also

Reset, ResetX, ResetY - Variants of the Reset operation along different axes.

class mimiqcircuits.SchmidtRank[source]#

Bases: Operation

Operation to get the Schmidt rank of a bipartition and store it in a z-register.

A Schmidt decomposition for a bipartition into subsystems \(A\) and \(B\) is defined for a pure state as:

Schmidt Decomposition

\[|\psi\rangle = \sum_{i=1}^{r} s_i |\alpha_i\rangle \otimes |\beta_i\rangle,\]

where \(|\alpha_i\rangle\) (\(|\beta_i\rangle\)) are orthonormal states acting on \(A\) (\(B\)). The Schmidt rank is the number of terms \(r\) in the sum. A product state gives \(r=1\), and \(r>1\) signals entanglement.

We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\), for some \(k\) and where \(N\) is the total number of qubits.

In MPS (Matrix Product States), when the state is optimally compressed, the Schmidt rank should be equal to the bond dimension (see BondDim).

Examples

When pushing to a circuit, the qubit index k takes the role of the above bipartition into A and B. For k=1, A is empty and the Schmidt rank will always return 1.

>>> from mimiqcircuits import *
>>> k = 5
>>> c = Circuit()
>>> c.push(SchmidtRank(), k, 1)
6-qubit circuit with 1 instructions:
└── SchmidtRank @ q[5], z[1]
control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
property num_qubits#
property parnames#
power(p)[source]#
class mimiqcircuits.ShiftCoordinates(*args)[source]#

Bases: AbstractAnnotation

ShiftCoordinates operation for shifting the coordinates of qubits.

An annotation class used to apply a shift to the spatial coordinates of subsequent qubit or detector annotations in a quantum circuit. ShiftCoordinates accumulates offsets that adjust the position of related circuit components, aiding in visualization without affecting the simulation.

Examples

Adding ShiftCoordinates to represent spatial shifts in a circuit:

>>> from mimiqcircuits import *
>>> ShiftCoordinates(0.1, 0.2, 0.3)
ShiftCoordinates(0.1, 0.2, 0.3)
>>> ShiftCoordinates([0.4, 0.5])
ShiftCoordinates(0.4, 0.5)
>>> ShiftCoordinates(0.4, 0.5)
ShiftCoordinates(0.4, 0.5)
>>> op = ShiftCoordinates(0.1, 0.9)
>>> op.get_notes()
[0.1, 0.9]
>>> c = Circuit()
>>> c.push(ShiftCoordinates(0.4, 0.2))
0-qubit circuit with 1 instructions:
└── ShiftCoordinates(0.4, 0.2)
get_notes()[source]#
iswrapper()[source]#
static opname()[source]#
class mimiqcircuits.SigmaMinus(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to \(|0 \rangle\langle 1|\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & a\\ 0 & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

SigmaPlus

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> SigmaMinus()
SigmaMinus(1)
>>> SigmaMinus(0.5)
SigmaMinus(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(SigmaMinus()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨SigmaMinus(1)⟩ @ q[1], z[1]
property num_qubits#
opsquared()[source]#
property parnames#
rescale(scale)[source]#
rescale_inplace(scale)[source]#
class mimiqcircuits.SigmaPlus(a=1)[source]#

Bases: AbstractOperator

One-qubit operator corresponding to \(|1 \rangle\langle 0|\).

Matrix Representation

\[\begin{split}\begin{pmatrix} 0 & 0\\ a & 0 \end{pmatrix}\end{split}\]

This matrix is parametrized by a to allow for phases/rescaling.

The parameter a is optional and is set to 1 by default.

See also

SigmaMinus

Parameters:

a (complex, optional) – Scaling factor for the matrix. Defaults to 1.

Examples

>>> from mimiqcircuits import *
>>> SigmaPlus()
SigmaPlus(1)
>>> SigmaPlus(0.5)
SigmaPlus(0.5)
>>> c = Circuit()
>>> c.push(ExpectationValue(SigmaPlus()), 1, 1)
2-qubit circuit with 1 instructions:
└── ⟨SigmaPlus(1)⟩ @ q[1], z[1]
property num_qubits#
opsquared()[source]#
property parnames#
rescale(scale)[source]#
class mimiqcircuits.ThermalNoise(T1: float | int, T2: float | int, time: float | int, ne: float | int)[source]#

Bases: krauschannel

One-qubit thermal noise channel.

The thermal noise channel is equivalent to the PhaseAmplitudeDamping channel, but it is parametrized instead as:

\[\begin{split}\mathcal{E}(\rho) = \begin{pmatrix} e^{-\Gamma_1 t}\rho_{00} + (1-n_e)(1-e^{-\Gamma_1 t}) & e^{-\Gamma_2 t}\rho_{01} \\ e^{-\Gamma_2 t}\rho_{10} & e^{-\Gamma_1 t}\rho_{11} + n_e(1-e^{-\Gamma_1 t}) \end{pmatrix}\end{split}\]

where \(\Gamma_1 = 1/T_1\) and \(\Gamma_2 = 1/T_2\), and the parameters must fulfill \(T_1 \geq 0\), \(T_2 \leq 2 T_1\), \(t \geq 0\), and \(0 \leq n_e \leq 1\).

These parameters can be related to the ones used to define the PhaseAmplitudeDamping channel through \(p = 1-n_e\), \(\gamma = 1-e^{-\Gamma_1 t}\), and \(\beta = \frac{1}{2}(1-e^{-(\Gamma_2-\Gamma_1/2)t})\).

Parameters:
  • T₁ (float) – Longitudinal relaxation rate, must be greater than or equal to 0.

  • T₂ (float) – Transversal relaxation rate, must be less than or equal to 2 * T₁.

  • t (float) – Time duration of the gate, must be greater than or equal to 0.

  • nₑ (float) – Excitation fraction when in thermal equilibrium with the environment, must be in the range [0, 1].

Examples

>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(ThermalNoise(0.5, 0.6, 1.2, 0.3), 1)
2-qubit circuit with 1 instructions:
└── ThermalNoise(0.5, 0.6, 1.2, 0.3) @ q[1]
evaluate(d={})[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

static matrix(gate)[source]#
property parnames#
class mimiqcircuits.Tick[source]#

Bases: AbstractAnnotation

An annotation class representing a timing marker or layer boundary in a quantum circuit. Tick does not affect simulation but provides structure by separating operations into distinct time steps, which is useful for visualization and analysis.

Examples

>>> from mimiqcircuits import *
>>> tick = Tick()
>>> print(tick)
Tick
>>> c = Circuit()
>>> c.push(tick)
0-qubit circuit with 1 instructions:
└── Tick
get_notes()[source]#
iswrapper()[source]#
static opname()[source]#
class mimiqcircuits.VonNeumannEntropy[source]#

Bases: Operation

Operation to get the bipartite Von Neumann entanglement entropy and store it in a z-register.

The entanglement entropy for a bipartition into subsystems \(A\) and \(B\) is defined for a pure state \(\rho = | \psi \rangle\langle \psi |\) as:

Entanglement Entropy

\[\mathcal{S}(\rho_A) = - \mathrm{Tr}(\rho_A \log_2 \rho_A) = - \mathrm{Tr}(\rho_B \log_2 \rho_B) = \mathcal{S}(\rho_A)\]

where \(\rho_A = \mathrm{Tr}_B(\rho)\) is the reduced density matrix. A product state has \(\mathcal{S}(\rho_A)=0\) and a maximally entangled state between \(A\) and \(B\) gives \(\mathcal{S}(\rho_A)=1\).

We only consider bipartitions where \(A=\{1,\ldots,k-1\}\) and \(B=\{k,\ldots,N\}\), for some \(k\) and where \(N\) is the total number of qubits.

When the system is open (i.e., with noise) and we are using quantum trajectories, the entanglement entropy of each trajectory is returned during execution.

See also

BondDim, SchmidtRank

Examples

When pushing to a circuit, the qubit index k takes the role of the above bipartition into A and B. For k=1, A is empty and the entanglement entropy will always return 0.

>>> from mimiqcircuits import *
>>> k = 5
>>> c = Circuit()
>>> c.push(VonNeumannEntropy(), k, 1)
6-qubit circuit with 1 instructions:
└── VonNeumannEntropy @ q[5], z[1]
control(num_qubits)[source]#
inverse()[source]#
iswrapper()[source]#
property num_qubits#
property parnames#
power(p)[source]#
mimiqcircuits.control(*args)[source]#
mimiqcircuits.gatedecl(name)[source]#
mimiqcircuits.inverse(op)[source]#
class mimiqcircuits.krauschannel[source]#

Bases: Operation

Supertype for all N-qubit Kraus channels.

A Kraus channel is a quantum operation on a density matrix \(\\rho\) of the form:

\[\mathcal{E}(\rho) = \sum_k E_k \rho E_k^\dagger,\]

where \(E_k\) are Kraus operators that need to fulfill \(\sum_k E_k^\dagger E_k \leq I\).

Special Properties:

  • isCPTP(): A Kraus channel is a completely positive and trace-preserving (CPTP) operation when \(\\sum_k E_k^\dagger E_k = I\). Currently, all noise channels are CPTP.

  • ismixedunitary(): A Kraus channel is called a mixed unitary channel when the Kraus operators \(E_k\) are each proportional to a unitary matrix \(U_k\), i.e., when \(\\mathcal{E}(\\rho) = \\sum_k p_k U_k \\rho U_k^\dagger\) with some probabilities \(0 \leq p_k \leq 1\) that add up to 1 and \(U_k^\dagger U_k = I\).

cumprobabilities()[source]#

Returns the cumulative sum of probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

Cumulative probabilities for the Kraus operators.

Return type:

list

Examples

>>> from mimiqcircuits import *
>>> Depolarizing1(0.1).cumprobabilities()
[0.9, 0.933333333333333, 0.966666666666667, 1.0]
inverse()[source]#
classmethod isCPTP()[source]#

Determine whether the Kraus channel is Completely Positive and Trace Preserving (CPTP).

A quantum operation is CPTP if the Kraus operators fulfill the condition:

\[\sum_k E_k^\dagger E_k = I\]

If the condition is:

\[\sum_k E_k^\dagger E_k < I\]

then the operation is not CPTP.

Note

Currently, all noise channels are considered CPTP.

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is CPTP, False otherwise.

Return type:

bool

classmethod ismixedunitary()[source]#

Determine whether the quantum operation is a mixed unitary channel.

A channel is considered mixed unitary if all the Kraus operators \(E_k\) are proportional to a unitary matrix \(U_k\), i.e.,

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

with some probabilities \(0 \leq p_k \leq 1\) that add up to 1, and \(U_k^\dagger U_k = I\).

Parameters:

krauschannel – The Kraus channel to check.

Returns:

True if the channel is a mixed unitary channel, False otherwise.

Return type:

bool

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.1).ismixedunitary()
True
>>> AmplitudeDamping(0.1).ismixedunitary()
False
iswrapper()[source]#
krausmatrices()[source]#

Returns the Kraus matrices associated with the given Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices returned by this function.

If the Kraus channel is parametric, the matrix elements are wrapped in a symengine or sympy object.

Returns:

A list of symengine matrices representing the Kraus operators.

Return type:

list

krausoperators()[source]#

Returns the Kraus operators associated with the given Kraus channel.

This should be implemented for each specific channel.

Returns:

A list of matrices representing the Kraus operators.

Return type:

list

numparams()[source]#
power(n)[source]#
probabilities()[source]#

Returns the probabilities for each Kraus operator in a mixed unitary channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(p_k\) are the probabilities.

This method is valid only for mixed unitary channels.

Returns:

A list of probabilities for each Kraus operator.

Return type:

list

squaredkrausoperators()[source]#

Returns the square of the Kraus operators (\(E_k^\dagger E_k\)). This computes the Hermitian conjugate (dagger) of each Kraus operator and multiplies it by the operator itself.

Returns:

A list of squared Kraus operators.

Return type:

list

unitarygates()[source]#

Returns the unitary gates associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary operators.

This method is valid only for mixed unitary channels.

unitarymatrices()[source]#

Unitary matrices associated with the given mixed unitary Kraus channel.

A mixed unitary channel is written as:

\[\mathcal{E}(\rho) = \sum_k p_k U_k \rho U_k^\dagger,\]

where \(U_k\) are the unitary matrices.

An error is raised if the channel is not mixed unitary (i.e., ismixedunitary(self)==False).

Note

If the Kraus channel is parametric, the matrix elements are wrapped in a symbolic object (e.g., from sympy or symengine). To manipulate expressions, use the appropriate symbolic manipulation libraries.

Examples

>>> from mimiqcircuits import *
>>> PauliX(0.2).unitarymatrices()
[[1.0, 0]
[0, 1.0]
, [0, 1.0]
[1.0, 0]
]
unwrappedcumprobabilities()[source]#

Returns the cumulative sum of probabilities for the mixed unitary channel without symbolic wrapping.

Returns:

A list of cumulative probabilities as float values.

Return type:

list

unwrappedkrausmatrices()[source]#

Returns the unitary Kraus matrices associated to the mixed unitary Kraus channel without symbolic wrapper.

Example

>>> from mimiqcircuits import *
>>> op = PauliX(0.2)
>>> op.unwrappedkrausmatrices()
[[0.894427190999916, 0]
[0, 0.894427190999916]
, [0, 0.447213595499958]
[0.447213595499958, 0]
]
unwrappedunitarymatrices()[source]#

Returns the unitary matrices associated with the mixed unitary channel without symbolic wrapping.

Returns:

A list of unitary matrices as numerical values.

Return type:

list

mimiqcircuits.parallel(*args)[source]#
mimiqcircuits.power(*args)[source]#