cunqa.circuit

This module defines CunqaCircuit. Many circuit design models already exist, the best-known being Qiskit’s QuantumCircuit. The motivation behind the design of CunqaCircuit does not stem from identifying problems in existing models, but from the lack of communication directives, which are so vital in CUNQA as a DQC platform. In this way, CunqaCircuit class allows users to describe quantum circuits that include not only local quantum operations, but also classical and quantum communication directives between distributed circuits. A CunqaCircuit can, therefore, represent both computation and communication in DQC scenarios.

class CunqaCircuit(num_qubits, num_clbits=None, id=None)

Quantum circuit abstraction for the CUNQA API. This class allows the design of quantum circuits to be executed in the vQPUs. Upon initialization, the circuit is defined by its number of qubits and, optionally, its number of classical bits and a user-defined identifier. If no identifier is provided, a unique one is generated automatically. The circuit identifier is later used to reference the circuit in communication-related instructions.

Once created, instructions can be appended to the circuit using the provided methods, including single- and multi-qubit gates, measurements, classically controlled operations, and remote communication primitives.

Supported operations

Group

Category

Operations

Unitary operations

Single-qubit gates with no parameters

id, x, y, z, h, s, sdg, sx, sxdg, sy, sydg, sz, szdg, t, tdg, p0, p1, v, vdg, k

Single-qubit gates with one parameter

u1, p, rx, ry, rz, rotinvx, rotinvy, rotinvz

Single-qubit gates with two parameters

u2, r

Single-qubit gates with three parameters

u3

Single-qubit gates with four parameters

u

Two-qubit gates with no parameters

swap, iswap, fusedswap, cx, cy, cz, ch, csx, csxdg, csy, csz, cs, csdg, ecr, ct, dcx

Two-qubit gates with one parameter

cu1, cp, crx, cry, crz, rxx, ryy, rzz, rzx

Two-qubit gates with two parameters

cu2, xxmyy, xxpyy

Two-qubit gates with three parameters

cu3

Two-qubit gates with four parameters

cu

Three-qubit gates with no parameters

ccx, ccy, ccz, cswap, cecr

Multicontrol gates with no parameters

mcx, mcy, mcz, mcsx, mcswap

Multicontrol gates with one parameter

mcp, mcrx, mcry, mcrz

Multicontrol gates with four parameters

mcu

Special gates

unitary, randomunitary, diagonal, multipauli, multipaulirotation, sparsematrix, amplitudedampingnoise, bitflipnoise, dephasingnoise, depolarizingnoise, independentxznoise, twoqubitdepolarizingnoise

Local non-unitary operations

Local non-unitary operations

cif, endcif, measure, measure_all, reset

Remote operations

Classical communication

send, recv

Quantum communication

gen_ent

Attributes

id : str

Returns circuit id.

info : dict

Information of the instance attributes, given in a dictionary.

num_qubits : tuple[int, int]

Returns a pair with the number of data qubits on the first spot and the number of the communication qubits on the second spot.

num_clbits : int

Number of classical bits of the circuit.

instructions: list[dict]

Set of operations applied to the circuit.

is_dynamic: bool

Whether the circuit has local non-unitary operations.

data_regs: dict

Dictionary of quantum registers with data qubits as {"name": [assigned qubits]}.

classical_regs: dict

Dictionary of classical registers of the circuit as {"name": [assigned clbits]}.

sending_to: set[str]

Set of circuit ids to which the current circuit is sending measurement outcomes or qubits.

Operations

Classical communication directives

Classical communication directives
send(clbits, recving_circuit)

Class method to send a bit (previously measured from a qubit) from the current circuit to a remote one.

Parameters:
  • clbits (int) – bits to be sent.

  • recving_circuit (str | CunqaCircuit) – id of the circuit or circuit object to which the bit is sent.

  • tag – unique identifier of the send operation.

Return type:

None

recv(clbits, sending_circuit)

Class method to receive a bit (previously measured from a qubit) from a remote circuit into a classical register of the receiving circuit.

Parameters:
  • clbits (int | list[int]) – indexes of the cl registers where the bits will be stored.

  • sending_circuit (str | CunqaCircuit) – id of the circuit or circuit object from which the bit is sent.

  • tag – unique identifier of the recv operation.

Return type:

None

Quantum communication directives

Quantum communication directives
gen_ent(comm_qubit, circuits, tag)

Low-level quantum-communication primitive that requests a shared entangled state (a Bell pair between two participants, or a GHZ state when more are involved) on the given comm qubit, linking this circuit with the circuits provided.

This is the building block on top of which the high-level teledata and telegate protocols are implemented. Most users should not call it directly but rely on the helpers in cunqa.qc_protocols (qsend, qrecv, cat_entangler and cat_disentangler).

Parameters:
  • comm_qubit (int) – communication qubit on which the entangled state is generated.

  • circuits (str | CunqaCircuit | list[str] | list[CunqaCircuit]) – id(s) or circuit object(s) that share the entangled state with this circuit.

  • tag (str) – identifier shared by every participant of the same entanglement-generation operation; it links the matching gen_ent directives across circuits.

The gen_ent method is the low-level primitive that requests a shared entangled (Bell/GHZ) state between the comm qubits of the participating circuits. The user-facing teledata and telegate protocols are built on top of it and provided as functions in the cunqa.qc_protocols subpackage (qsend, qrecv, cat_entangler and cat_disentangler).

Non-unitary operations

Non-unitary operations
save_state(pershot=False, label='_method_')

Instruction to save the state of the circuit simulation at the particular moment the instruction is executed.

Parameters:
  • pershot (bool) – determines wether the state is stored separatedly for each shot or averaged. Default: False

  • label (str) – key for the state in the result dict. Used to distinguish two states saved. Default: ‘_method_’, which appears in the result as the name of the simulation method selected.

Return type:

None

measure_all()

Class to apply a global measurement of all of the qubits of the circuit. An additional classcial register will be added and labeled as “measure”.

Return type:

None

measure(qubits, clbits, save=True)

Class method to add a measurement of a qubit or a list of qubits and to register that measurement in the given classical bits.

Parameters:
  • qubits (int | list[int]) – qubits to measure.

  • clbits (int | list[int]) – clasical bits where the measurement will be registered.

  • save (bool)

Return type:

None

reset(qubits)

Class method to add reset to zero instruction to a qubit or list of qubits (use after measure).

Parameters:

qubits (int, list[int]]) – qubits to which the reset operation is applied.

cif(clbits, condition=1, operation='and')

Method to open a classically controlled block. The instructions added to the circuit after this call, and up to the matching endcif call, are only executed when the measurement outcomes of the given clbits match condition under the selected operation. Every cif call must be closed with an endcif call.

In this example, the operations defined inside the cif block are executed only if the value of classical bit 0 is equal to 1. Currently, this construct does not support an explicit else branch. This design decision is based on the observation that none of the reviewed algorithms or protocols require such functionality. Support for an else branch may be added in future versions if needed.

Parameters:
  • clbits (int | list[int]) – clbits to match the condition.

  • condition (int) – can be 1 or 0. The clbits will have to match this condition for the gate to be applied. Default = 1

  • operation (str) – If multiple clbits are provided, their measures are operated on with the selected operation and the result is matched to condition. Operations supported are: “and”, “or”, “xor”, “nand”, “nor” and “xnor”.

Return type:

None

endcif()

Method to close a classically controlled block opened with cif.

Every cif call must be paired with a matching endcif call. All the instructions added to the circuit between cif and endcif are the ones that are executed only when the classical condition is met. This method takes no arguments.

Return type:

None

Unitary operations

Single-qubit gates
i(qubit)

Class method to apply id gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

x(qubit)

Class method to apply x gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

y(qubit)

Class method to apply y gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

z(qubit)

Class method to apply z gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

h(qubit)

Class method to apply h gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

s(qubit)

Class method to apply s gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sdg(qubit)

Class method to apply sdg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sx(qubit)

Class method to apply sx gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sxdg(qubit)

Class method to apply sxdg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sy(qubit)

Class method to apply sy gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sydg(qubit)

Class method to apply sydg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

sz(qubit)

Class method to apply sz gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

szdg(qubit)

Class method to apply szdg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

t(qubit)

Class method to apply t gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

tdg(qubit)

Class method to apply tdg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

p0(qubit)

Class method to apply P0 gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

p1(qubit)

Class method to apply P1 gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

v(qubit)

Class method to apply v gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

vdg(qubit)

Class method to apply vdg gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

k(qubit)

Class method to apply k gate to the given qubit.

Parameters:

qubit (int) – qubit in which the gate is applied.

Return type:

None

u1(param, qubit)

Class method to apply u1 gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

u2(theta, phi, qubit)

Class method to apply u2 gate to the given qubit.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

u3(theta, phi, lam, qubit)

Class method to apply u3 gate to the given qubit.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

u(theta, phi, lam, qubit)

Class method to apply u gate to the given qubit.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

p(param, qubit)

Class method to apply p gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

r(theta, phi, qubit)

Class method to apply r gate to the given qubit.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

rx(param, qubit)

Class method to apply rx gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

ry(param, qubit)

Class method to apply ry gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

rz(param, qubit)

Class method to apply rz gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

rotinvx(param, qubit)

Class method to apply rotinvx gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

rotinvy(param, qubit)

Class method to apply rotinvy gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

rotinvz(param, qubit)

Class method to apply rotinvz gate to the given qubit.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubit (int) – qubit in which the gate is applied.

Return type:

None

Two-qubit gates
swap(*qubits)

Class method to apply swap gate to the given qubits.

Parameters:

qubits (list[int]) – qubits in which the gate is applied.

Return type:

None

iswap(*qubits)

Class method to apply iswap gate to the given qubits.

Parameters:

qubits (list[int]) – qubits in which the gate is applied.

Return type:

None

fusedswap(block_size, *qubits)

Class method to apply fusedswap gate to the given qubits.

Parameters:
  • block_size (int) – size of the block to be swapped.

  • qubits (list[int]) – qubits in which the gate is applied.

Return type:

None

ecr(*qubits)

Class method to apply ecr gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied.

Return type:

None

cx(*qubits)

Class method to apply cx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

cy(*qubits)

Class method to apply cy gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

cz(*qubits)

Class method to apply cz gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

ch(*qubits)

Class method to apply ch gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

csx(*qubits)

Class method to apply csx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

csxdg(*qubits)

Class method to apply csxdg gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

cs(*qubits)

Class method to apply cs gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

csdg(*qubits)

Class method to apply csdg gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

ct(*qubits)

Class method to apply ct gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

dcx(*qubits)

Class method to apply dcx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

rxx(param, *qubits)

Class method to apply rxx gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied.

Return type:

None

ryy(param, *qubits)

Class method to apply ryy gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied.

Return type:

None

rzz(param, *qubits)

Class method to apply rzz gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied.

Return type:

None

rzx(param, *qubits)

Class method to apply rzx gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied.

Return type:

None

crx(param, *qubits)

Class method to apply crx gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cry(param, *qubits)

Class method to apply cry gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

crz(param, *qubits)

Class method to apply crz gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cp(param, *qubits)

Class method to apply cp gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cu1(param, *qubits)

Class method to apply cu1 gate to the given qubits.

Parameters:
  • param (float | int | str) – parameter for the parametric gate.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cu2(theta, phi, *qubits)

Class method to apply cu2 gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cu3(theta, phi, lam, *qubits)

Class method to apply cu3 gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

cu(theta, phi, lam, gamma, *qubits)

Class method to apply cu gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • gamma (float | int) – angle.

  • qubits (int | list[int]) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

xxmyy(theta, phi, *qubits)

Class method to apply XX - YY gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

xxpyy(theta, phi, *qubits)

Class method to apply XX + YY gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first one will be the control qubit and second one the target qubit.

Return type:

None

Three-qubit gates
ccx(*qubits)

Class method to apply ccx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

ccy(*qubits)

Class method to apply ccy gate to the given qubits. Gate is decomposed as follows as it is not commonly supported by simulators.

q_0: ──────────────■─────────────
                   │
q_1: ──────────────■─────────────
     ┌──────────┐┌─┴─┐┌─────────┐
q_2: ┤ Rz(-π/2) ├┤ X ├┤ Rz(π/2) ├
     └──────────┘└───┘└─────────┘
Parameters:

qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

ccz(*qubits)

Class method to apply ccz gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

cecr(*qubits)

Class method to apply cecr gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first one will be control qubit and second one target qubit.

Return type:

None

cswap(*qubits)

Class method to apply cswap gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

Multicontrol gates
mcx(*qubits)

Class method to apply mcx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, all will be control qubits except the last one, that is the target qubit.

Return type:

None

mcy(*qubits)

Class method to apply mcy gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, all will be control qubits except the last one, that is the target qubit.

Return type:

None

mcz(*qubits)

Class method to apply mcz gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, all will be control qubits except the last one, that is the target qubit.

Return type:

None

mcsx(*qubits)

Class method to apply mcsx gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, all will be control qubits except the last one, that is the target qubit.

Return type:

None

mcswap(*qubits)

Class method to apply mcswap gate to the given qubits.

Parameters:

qubits (int) – qubits in which the gate is applied, all will be control qubits except the last two, that are the target qubits.

Return type:

None

mcp(theta, *qubits)

Class method to apply mcp gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcrx(theta, *qubits)

Class method to apply mcrx gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcry(theta, *qubits)

Class method to apply mcry gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcrz(theta, *qubits)

Class method to apply mcrz gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcu1(theta, *qubits)

Class method to apply mcu1 gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcu2(theta, phi, *qubits)

Class method to apply mcu2 gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcu3(theta, phi, lam, *qubits)

Class method to apply mcu3 gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

mcu(theta, phi, lam, beta, *qubits)

Class method to apply mcu gate to the given qubits.

Parameters:
  • theta (float | int | str) – angle.

  • phi (float | int | str) – angle.

  • lam (float | int | str) – angle.

  • beta (float | int | str) – angle.

  • qubits (int) – qubits in which the gate is applied, first two will be control qubits and the following one will be target qubit.

Return type:

None

Special gates
unitary(matrix, *qubits)

Class method to apply a unitary gate created from an unitary matrix provided.

Parameters:
  • matrix (list | numpy.ndarray) – unitary operator in matrix form to be applied to the given qubits.

  • qubits (int) – qubits to which the unitary operator will be applied.

Return type:

None

randomunitary(*qubits, seed=None)

Class method to apply a randomunitary gate.

Parameters:
  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

diagonal(diagonal, *qubits)

Class method to apply a diagonal gate created from a complex vector.

Parameters:
  • diagonal (list | numpy.ndarray) – list or np.array with diagonal elements

  • qubits (int) – qubits to which the unitary operator will be applied.

Return type:

None

multipauli(pauli_id_list, *qubits)

Class method to apply a multipauli gate.

Parameters:
  • pauli_id_list (list[int]) – list of Pauli ids.

  • qubits (int) – qubits to which the unitary operator will be applied.

Return type:

None

multipaulirotation(param, pauli_id_list, *qubits)

Class method to apply a multipaulirotation gate.

Parameters:
  • param (float) – parameter

  • pauli_id_list (list[int]) – list of Pauli ids.

  • qubits (int) – qubits to which the unitary operator will be applied.

Return type:

None

amplitudedampingnoise(prob, *qubits, seed=None)

Class method to apply a amplitudedampingnoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

bitflipnoise(prob, *qubits, seed=None)

Class method to apply a bitflipnoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

dephasingnoise(prob, *qubits, seed=None)

Class method to apply a dephasingnoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

depolarizingnoise(prob, *qubits, seed=None)

Class method to apply a depolarizingnoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

independentxznoise(prob, *qubits, seed=None)

Class method to apply a independentxznoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

twoqubitdepolarizingnoise(prob, *qubits, seed=None)

Class method to apply a twoqubitdepolarizingnoise gate.

Parameters:
  • prob (float) – probability.

  • qubits (int) – qubits to which the unitary operator will be applied.

  • seed (None | int) – seed.

Return type:

None

Parameters:
  • num_qubits (Union[int, tuple[int, int]])

  • num_clbits (Optional[int])

  • id (Optional[str])

Transformations

Besides all quantum operations and communication directives, it is common for users to want to combine two circuits or, conversely, to create several circuits from a single one. This module exists to enable this capability, defining the following functions. See the following explanations of the add, union and hsplit functions to understand how they empower the study of DQC algorithms.

The function add takes an iterable of circuits as input and returns a circuit that has the instructions of each circuit of the iterable in order. Its purpose is to build circuits modularly from simple parts.

add(circuits)

This function concatenates the instructions of two circuits.

It appends the gates from a list of circuits into a final resulting circuit. The order of the list passed as an argument is important, since the instructions will be appended in that same order.

In this operation, if two circuits in the list contain communication directives between them, an exception will be raised to prevent potential deadlocks and undefined or incorrectly specified behavior.

This operation is the inverse of the vsplit.

Parameters:

circuits (list[CunqaCircuit])

Return type:

CunqaCircuit

Warning

As communications greatly depend on the order of execution, adding circuits does not mesh well with communications. In particular, if two circuits that communicate with eachother were added, execution would stall as the circuit would wait to communicate with the next subcircuit, which wouldn’t respond until execution progressed, waiting indefinitely.

For this reason, if two circuits on the iterable contain comunications between them, an exception would be raised.

The add function can be used to avoid redundant code, for example when creating a circuit for the Grover algorithm:

grover_circuit = add([oracle, diffusor] * repeat_times)

Full example of the add function:

import os, sys
# In order to import cunqa, we append to the search path the cunqa installation path
sys.path.append(os.getenv("HOME")) # HOME as install path is specific to CESGA

from cunqa.qpu import get_QPUs, qraise, qdrop, run
from cunqa.qjob import gather
from cunqa.circuit import CunqaCircuit
from cunqa.circuit.transformations import add
from pprint import pprint

# ---------------------------
# Acquiring the resources
# ---------------------------
family = qraise(1, "00:10:00", simulator="Aer", co_located=True)
[qpu] = get_QPUs(co_located=True, family=family)

# ---------------------------
# Original circuits are:
#
#    | circuit1 | circuit2 |
# q0:|   ─[H]─  |   ──●──  |
#    |          |     |    |
# q1:|          |   ─[X]─  |
# 
# And, then, added_circuit is a Bell pair:
#
# added_circuit.q0: ─[H]──●──[M]─
#                         |
# added_circuit.q1: ─────[X]─[M]─
# ---------------------------
circuit1 = CunqaCircuit(1, id = "circuit1") # adding ancilla
circuit1.h(0)

circuit2 = CunqaCircuit(2, id = "circuit2")
circuit2.cx(0,1)

added_circuit = add([circuit1, circuit2])
added_circuit.measure_all()

qjob = run(added_circuit, qpu, shots = 1024)# non-blocking call
results = qjob.result

print(f"\nResult after addition: {results.counts}\n")

# ---------------------------
# Relinquishing resources
# ---------------------------
qdrop(family)

Given two circuits with n and m qubits, their union returns a circuit with n + m qubits, where the operations of the former are applied to the first n qubits and those of the latter are applied to the last m qubits. If originally there were distributed instructions between the circuits, they would be replaced by local ones. In the following example we observe the union of two simple circuits.

union(circuits)

Union of circuits (addition of qubits).

This function joins the qubits of several CunqaCircuit objects into a single circuit. Circuits connected by quantum communication protocols have those protocols collapsed into their equivalent local operations:

  • Telegate (a cat_entangler / remote-controlled gate(s) / cat_disentangler block). The whole protocol is removed and the gate(s) the receiver applied controlled on its comm qubit are reissued as direct gate(s) controlled on the sender’s data qubit.

  • Teledata (a qsend / qrecv block). The whole protocol is removed and replaced by a swap between the sender’s and the receiver’s data qubits.

This operation is the inverse of the hsplit.

Parameters:

circuits (list[CunqaCircuit]) – circuits to join.

Return type:

CunqaCircuit

Basic usage:

c1 = CunqaCircuit(2, id="circuit1")
c1.h(0)
c1.cx(0,1)

c2 = CunqaCircuit(1, id="circuit2")
c2.x(0)

union_circuit = union([c1, c2])

Full example of the union function:

import os, sys
# In order to import cunqa, we append to the search path the cunqa installation path
sys.path.append(os.getenv("HOME")) # HOME as install path is specific to CESGA

from cunqa.qpu import get_QPUs, qraise, qdrop, run
from cunqa.qc_protocols import cat_entangler, cat_disentangler
from cunqa.circuit import CunqaCircuit
from cunqa.circuit.transformations import union
from cunqa.qjob import gather

# ---------------------------
# Acquiring resources
# ---------------------------
family_separated = qraise(2, "00:10:00", simulator="Aer", quantum_comm=True, co_located=True)
qpus_separated = get_QPUs(co_located=True, family=family_separated)

family_union = qraise(1, "00:10:00", simulator="Aer", co_located=True)
[qpu_union] = get_QPUs(co_located=True, family=family_union)

try:
    # ---------------------------
    # Communicated circuits created and executed.
    # The remote control of the gate (a remote CX) is implemented with the
    # cat-entangler / cat-disentangler telegate protocol.
    #
    # circuit1.data: ─[H]──●──[M]─
    #                      $
    # circuit2.data: ─────[X]─[M]─
    # Where $ represents the remote control of the gate
    # ---------------------------
    circuit1 = CunqaCircuit((1, 1), 1, id="circuit1") # (data qubits, comm qubits), clbits
    circuit2 = CunqaCircuit((1, 1), 1, id="circuit2")

    data_1, comm_1 = circuit1.get_qubits()
    data_2, comm_2 = circuit2.get_qubits()

    circuit1.h(data_1[0])

    cat_entangler(
        [circuit1, circuit2],
        data_1[0],
        [comm_1[0], comm_2[0]],
        [0, 0],
        tag="telegate"
    )

    circuit2.cx(comm_2[0], data_2[0])

    cat_disentangler(
        [circuit1, circuit2],
        data_1[0],
        [comm_2[0]],
        [0],
        [0]
    )

    circuit1.measure(data_1[0], 0)
    circuit2.measure(data_2[0], 0)

    qjobs = run([circuit1, circuit2], qpus_separated, shots=1024)
    results = gather(qjobs)

    for i, result in enumerate(results):
        print(f"\nResult before union (circuit{i + 1}): {result.counts}")

    # ---------------------------
    # Take the union of the circuits and execute it on a single QPU.
    # The communication directives (gen_ent/send/recv) are replaced by local
    # operations, yielding a single equivalent circuit.
    #
    # union_circuit.q0: ─[H]──●──[M]─
    #                         |
    # union_circuit.q1: ─────[X]─[M]─
    # ---------------------------
    union_circuit = union([circuit1, circuit2])

    qjob = run(union_circuit, qpu_union, shots=1024) # non-blocking call
    results = qjob.result

    print(f"\nResult after union: {results.counts}\n")

except Exception as error:
    raise error
finally:
    # ---------------------------
    # Relinquishing resources
    # ---------------------------
    qdrop(family_union)
    qdrop(family_separated)

The function hsplit divides the set of qubits of a circuit into subcircuits, preserving the instructions and substituing local 2-qubit gates by distributed gates if they involve qubits from different subcircuits. The name hspit stands for horizontal split, as in the conventional way to visually represent a circuit one would have to draw a horizontal line to separate the qubits of the circuit in two subsets.

To divide a circuit circuit_to_divide, one should provide an additional argument that determines how the circuits should be divided. This argument can be a list with the number of qubits for each subcircuit (the lenght of the list determines the number of subcircuits), or an int specifying the number of subcircuits, which would get an equal number of qubits except possibly the last one, which would get the remainder if the number of qubits is not cleanly divided by the int provided.

hsplit(circuit, qubits_or_sections)

Horizontal split of a quantum circuit.

This function splits a circuit into a given number of subcircuits. This number is determined by the qubits_or_sections argument. If it is a list, then it specifies the number of qubits each subcircuit will have; however, if it is an int, it specifies the number of subcircuits to be created (each having the same number of qubits, except for one in case the split is not exact, which will take the remainder as its number of qubits).

This operation is the inverse of the union.

Parameters:
  • circuit (CunqaCircuit) – circuit to be splited.

  • qubits_or_sections (list[int], int) – if is a list, qubits in which to split, if an int, number of subcircuits that result of the split.

Return type:

list[CunqaCircuit]

Basic syntax:

# List with the number of qubits per subcircuit
[c1, c2] = hsplit(circuit_to_divide, [2, 7])

# Int specifying the number of resulting subcircuits
[c1, c2] = hsplit(circuit_to_divide, 2)

In particular, it could be checked that union and hsplit are inverses of eachother:

# New circuit equivalent to circ is returned
union(hsplit(circ, 2))
# New circuits equal to circ1 and circ2 are returned
hsplit(union(circ1, circ2), [circ1.num_qubits, circ2.num_qubits])

Full example of the hsplit function:

import os, sys
# In order to import cunqa, we append to the search path the cunqa installation path
sys.path.append(os.getenv("HOME")) # HOME as install path is specific to CESGA

from cunqa.qpu import get_QPUs, qraise, qdrop, run
from cunqa.qjob import gather
from cunqa.circuit import CunqaCircuit
from cunqa.circuit.transformations import hsplit

# ---------------------------
# Acquiring resources
# ---------------------------

family_original = qraise(1, "00:10:00", simulator="Aer", co_located=True)
[qpu_original]  = get_QPUs(co_located=True, family=family_original)

family_separated = qraise(2, "00:10:00", simulator="Aer", quantum_comm=True, co_located=True)
qpus_separated = get_QPUs(co_located=True, family=family_separated)

try:
    # ---------------------------
    # Original circuit
    # union_circuit.q0: ─[H]──●──[M]─
    #                         |
    # union_circuit.q1: ─────[X]─[M]─
    # ---------------------------
    circuit = CunqaCircuit(2, id="circuit")
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure_all()

    qjob = run(circuit, qpu_original, shots=1024) # non-blocking call
    results = qjob.result

    print(f"\nResult before hsplit: {results.counts}")

    # ---------------------------
    # Split original circuit to create two communicated circuits, and execute them
    # circuit1.q0: ─[H]──●──[M]─
    #                    $
    # circuit2.q0: ─────[X]─[M]─
    # Where $ represents the remote control of the gate
    # ---------------------------
    [circuit1, circuit2] = hsplit(circuit, 2)

    data1, comm1 = circuit1.get_qubits()
    print(f"{data1} and {comm1}")
    
    data2, comm2 = circuit2.get_qubits()
    print(f"{data2} and {comm2}")

    qjobs = run([circuit1, circuit2], qpus_separated, shots=1024)
    results = gather(qjobs)

    for result in results:
        print(f"Result after split: {result.counts}")
except Exception as error:
    raise error
finally:
    # ---------------------------
    # Relinquishing resources
    # ---------------------------
    qdrop(family_original, family_separated, remove_logs=True)