Models#

Qibo provides models for both the circuit based and the adiabatic quantum computation paradigms. Circuit based models include Circuit models which allow defining arbitrary circuits and Quantum Fourier Transform (QFT) such as the Quantum Fourier Transform (qibo.models.QFT) and the Variational Quantum Eigensolver (qibo.models.VQE). Adiabatic quantum computation is simulated using the Time evolution of state vectors.

In order to perform calculations and apply gates to a state vector a backend has to be used. The backends are defined in qibo/backends. Circuit and gate objects are backend independent and can be executed with any of the available backends.

Qibo uses big-endian byte order, which means that the most significant qubit is the one with index 0, while the least significant qubit is the one with the highest index.

Circuit models#

Circuit#

class qibo.models.circuit.Circuit(nqubits: int | list | None = None, accelerators=None, density_matrix: bool = False, wire_names: list | None = None)[source]#

Circuit object which holds a list of gates.

This circuit is symbolic and cannot perform calculations. A specific backend has to be used for performing calculations.

Circuits can be created with a specific number of qubits and wire names. .. rubric:: Example


Parameters:
  • nqubits (int | list, optional) – Number of qubits in the circuit or a list of wire names.

  • wire_names (list, optional) – List of wire names. - Either nqubits or wire_names must be provided. - If only nqubits is provided, wire names will default to [0, 1, …, nqubits - 1]. - If only wire_names is provided, nqubits will be set to the length of wire_names. - nqubits and wire_names must be consistent with each other.

  • init_kwargs (dict) –

    a dictionary with the following keys

    • nqubits

    • accelerators

    • density_matrix

    • wire_names.

  • queue (_Queue) – List that holds the queue of gates of a circuit.

  • parametrized_gates (_ParametrizedGates) – List of parametric gates.

  • trainable_gates (_ParametrizedGates) – List of trainable gates.

  • measurements (list) – List of non-collapsible measurements.

  • _final_state – Final result after full simulation of the circuit.

  • compiled (CompiledExecutor) – Circuit executor. Defaults to None.

  • repeated_execution (bool) – If True, the circuit would be re-executed when sampling. Defaults to False.

  • density_matrix (bool, optional) – If True, the circuit would evolve density matrices. If False, defaults to statevector simulation. Defaults to False.

  • accelerators (dict, optional) – Dictionary that maps device names to the number of times each device will be used. Defaults to None.

  • ndevices (int) – Total number of devices. Defaults to None.

  • nglobal (int) – Base two logarithm of the number of devices. Defaults to None.

  • nlocal (int) – Total number of available qubits in each device. Defaults to None.

  • queues (DistributedQueues) – Gate queues for each accelerator device. Defaults to None.

on_qubits(*qubits)[source]#

Generator of gates contained in the circuit acting on specified qubits.

Useful for adding a circuit as a subroutine in a larger circuit.

Parameters:

qubits (int) – Qubit ids that the gates should act.

Example

from qibo import Circuit, gates

# create small circuit on 4 qubits
small_circuit = Circuit(4)
small_circuit.add(gates.RX(i, theta=0.1) for i in range(4))
small_circuit.add((gates.CNOT(0, 1), gates.CNOT(2, 3)))
# create large circuit on 8 qubits
large_circuit = Circuit(8)
large_circuit.add(gates.RY(i, theta=0.1) for i in range(8))
# add the small circuit to the even qubits of the large one
large_circuit.add(small_circuit.on_qubits(*range(0, 8, 2)))
light_cone(*qubits)[source]#

Reduces circuit to the qubits relevant for an observable.

Useful for calculating expectation values of local observables without requiring simulation of large circuits. Uses the light cone construction described in issue #571.

Parameters:

qubits (int) – Qubit ids that the observable has support on.

Returns:

Circuit that contains only

the qubits that are required for calculating expectation involving the given observable qubits.

qubit_map (dict): Dictionary mapping the qubit ids of the original

circuit to the ids in the new one.

Return type:

circuit (qibo.models.Circuit)

copy(deep: bool = False)[source]#

Creates a copy of the current circuit as a new Circuit model.

Parameters:

deep (bool) – If True copies of the gate objects will be created for the new circuit. If False, the same gate objects of circuit will be used.

Returns:

The copied circuit object.

invert()[source]#

Creates a new Circuit that is the inverse of the original.

Inversion is obtained by taking the dagger of all gates in reverse order. If the original circuit contains parametrized gates, dagger will change their parameters. This action is not persistent, so if the parameters are updated afterwards, for example using qibo.models.circuit.Circuit.set_parameters(), the action of dagger will be overwritten. If the original circuit contains measurement gates, these are included in the inverted circuit.

Returns:

The circuit inverse.

decompose(*free: int)[source]#

Decomposes circuit’s gates to gates supported by OpenQASM.

Parameters:

free – Ids of free (work) qubits to use for gate decomposition.

Returns:

Circuit that contains only gates that are supported by OpenQASM and has the same effect as the original circuit.

with_pauli_noise(noise_map: Tuple[int, int, int] | Dict[int, Tuple[int, int, int]])[source]#

Creates a copy of the circuit with Pauli noise gates after each gate.

If the original circuit uses state vectors then noise simulation will be done using sampling and repeated circuit execution. In order to use density matrices the original circuit should be created setting the flag density_matrix=True. For more information we refer to the How to perform noisy simulation? example.

Parameters:

noise_map (dict) – list of tuples \((P_{k}, p_{k})\), where \(P_{k}\) is a str representing the \(k\)-th \(n\)-qubit Pauli operator, and \(p_{k}\) is the associated probability.

Returns:

Circuit object that contains all the gates of the original circuit and additional noise channels on all qubits after every gate.

Example

from qibo import Circuit, gates
# use density matrices for noise simulation
circuit = Circuit(2, density_matrix=True)
circuit.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)])
noise_map = {
    0: list(zip(["X", "Z"], [0.1, 0.2])),
    1: list(zip(["Y", "Z"], [0.2, 0.1]))
}
noisy_circuit = circuit.with_pauli_noise(noise_map)
# ``noisy_circuit`` will be equivalent to the following circuit
circuit_2 = Circuit(2, density_matrix=True)
circuit_2.add(gates.H(0))
circuit_2.add(gates.PauliNoiseChannel(0, [("X", 0.1), ("Z", 0.2)]))
circuit_2.add(gates.H(1))
circuit_2.add(gates.PauliNoiseChannel(1, [("Y", 0.2), ("Z", 0.1)]))
circuit_2.add(gates.CNOT(0, 1))
circuit_2.add(gates.PauliNoiseChannel(0, [("X", 0.1), ("Z", 0.2)]))
circuit_2.add(gates.PauliNoiseChannel(1, [("Y", 0.2), ("Z", 0.1)]))
add(gate)[source]#

Add a gate to a given queue.

Parameters:

gate (qibo.gates.Gate) – the gate object to add. See Gates for a list of available gates. gate can also be an iterable or generator of gates. In this case all gates in the iterable will be added in the circuit.

Returns:

If the circuit contains measurement gates with collapse=True a sympy.Symbol that parametrizes the corresponding outcome.

property ngates: int#

Total number of gates/operations in the circuit.

property depth: int#

Circuit depth if each gate is placed at the earliest possible position.

property gate_types: Counter#

collections.Counter with the number of appearances of each gate type.

property gate_names: Counter#

collections.Counter with the number of appearances of each gate name.

gates_of_type(gate: str | type) List[Tuple[int, Gate]][source]#

Finds all gate objects of specific type or name.

This method can be affected by how qibo.gates.Gate.controlled_by() behaves with certain gates. To see how qibo.gates.Gate.controlled_by() affects gates, we refer to the documentation of qibo.gates.Gate.controlled_by().

Parameters:

gate (str or type) – The name of a gate or the corresponding gate class.

Returns:

gates that are in the circuit and have the same type as gate.

The list contains tuples (k, g) where k is the index of the gate g in the circuit’s gate queue.

Return type:

list

set_parameters(parameters)[source]#

Updates the parameters of the circuit’s parametrized gates.

For more information on how to use this method we refer to the How to use parametrized gates? example.

Parameters:

parameters – Container holding the new parameter values. It can have one of the following types: List with length equal to the number of parametrized gates and each of its elements compatible with the corresponding gate. Dictionary with keys that are references to the parametrized gates and values that correspond to the new parameters for each gate. Flat list with length equal to the total number of free parameters in the circuit. A backend supported tensor (for example np.ndarray or tf.Tensor) may also be given instead of a flat list.

Example

from qibo import Circuit, gates
# create a circuit with all parameters set to 0.
circuit = Circuit(3)
circuit.add(gates.RX(0, theta=0))
circuit.add(gates.RY(1, theta=0))
circuit.add(gates.CZ(1, 2))
circuit.add(gates.fSim(0, 2, theta=0, phi=0))
circuit.add(gates.H(2))

# set new values to the circuit's parameters using list
params = [0.123, 0.456, (0.789, 0.321)]
circuit.set_parameters(params)
# or using dictionary
params = {
    circuit.queue[0]: 0.123,
    circuit.queue[1]: 0.456,
    circuit.queue[3]: (0.789, 0.321)
}
circuit.set_parameters(params)
# or using flat list (or an equivalent `np.array`/`tf.Tensor`/`torch.Tensor`)
params = [0.123, 0.456, 0.789, 0.321]
circuit.set_parameters(params)
get_parameters(format: str = 'list', include_not_trainable: bool = False) List | Dict[source]#

Returns the parameters of all parametrized gates in the circuit.

Inverse method of qibo.models.circuit.Circuit.set_parameters().

Parameters:
  • format (str) – How to return the variational parameters. Available formats are 'list', 'dict' and 'flatlist'. See qibo.models.circuit.Circuit.set_parameters() for more details on each format. Default is 'list'.

  • include_not_trainable (bool) – If True it includes the parameters of non-trainable parametrized gates in the returned list or dictionary. Default is False.

associate_gates_with_parameters()[source]#

Associates to each parameter its gate.

Returns:

A nparams-long flatlist whose i-th element is the gate parameterized by the i-th parameter.

summary() str[source]#

Generates a summary of the circuit.

The summary contains the circuit depths, total number of qubits and the all gates sorted in decreasing number of appearance.

Example

from qibo import Circuit, gates

circuit = Circuit(3)
circuit.add(gates.H(0))
circuit.add(gates.H(1))
circuit.add(gates.CNOT(0, 2))
circuit.add(gates.CNOT(1, 2))
circuit.add(gates.H(2))
circuit.add(gates.TOFFOLI(0, 1, 2))

print(circuit.summary())
# Prints
'''
Circuit depth = 5
Total number of gates = 6
Number of qubits = 3
Most common gates:
h: 3
cx: 2
ccx: 1
'''
fuse(max_qubits=2)[source]#

Creates an equivalent circuit by fusing gates for increased simulation performance.

Parameters:

max_qubits (int) – Maximum number of qubits in the fused gates.

Returns:

A qibo.core.circuit.Circuit object containing qibo.gates.FusedGate gates, each of which corresponds to a group of some original gates. For more details on the fusion algorithm we refer to the Circuit fusion section.

Example

from qibo import Circuit, gates

circuit = Circuit(2)
circuit.add([gates.H(0), gates.H(1)])
circuit.add(gates.CNOT(0, 1))
circuit.add([gates.Y(0), gates.Y(1)])
# create circuit with fused gates
fused_circuit = circuit.fuse()
# now ``fused_circuit`` contains a single ``FusedGate`` that is
# equivalent to applying the five original gates
unitary(backend=None)[source]#

Creates the unitary matrix corresponding to all circuit gates.

This is a \(2^{n} \times 2^{n}`\) matrix obtained by multiplying all circuit gates, where \(n\) is nqubits.

property final_state#

Returns the final state after full simulation of the circuit.

If the circuit is executed more than once, only the last final state is returned.

execute(initial_state=None, nshots=1000)[source]#

Executes the circuit. Exact implementation depends on the backend.

Parameters:
  • initial_state (np.ndarray or qibo.models.circuit.Circuit) – Initial configuration. It can be specified by the setting the state vector using an array or a circuit. If None, the initial state is |000..00>.

  • nshots (int) – Number of shots.

Returns:

either a qibo.result.QuantumState, qibo.result.MeasurementOutcomes or qibo.result.CircuitResult depending on the circuit’s configuration.

property raw: dict#

Serialize to dictionary.

This is a thin wrapper over Gate.raw().

classmethod from_dict(raw)[source]#

Load from serialization.

Essentially the counter-part of raw().

to_qasm()[source]#

Convert circuit to QASM.

Note

This method does not support multi-controlled gates and gates with torch.Tensor as parameters.

Parameters:

filename (str) – The filename where the code is saved.

classmethod from_qasm(qasm_code, accelerators=None, density_matrix=False)[source]#

Constructs a circuit from QASM code.

Parameters:

qasm_code (str) – String with the QASM script.

Returns:

A qibo.models.circuit.Circuit that contains the gates specified by the given QASM script.

Example

from qibo import Circuit, gates
qasm_code = '''OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
h q[1];
cx q[0],q[1];'''
circuit = Circuit.from_qasm(qasm_code)
# is equivalent to creating the following circuit
circuit_2 = Circuit(2)
circuit_2.add(gates.H(0))
circuit_2.add(gates.H(1))
circuit_2.add(gates.CNOT(0, 1))
diagram(line_wrap: int = 70, legend: bool = False) str[source]#

Build the string representation of the circuit diagram.

draw(line_wrap: int = 70, legend: bool = False)[source]#

Draw text circuit using unicode symbols.

Parameters:
  • line_wrap (int, optional) – maximum number of characters per line. This option split the circuit text diagram in chunks of line_wrap characters. Defaults to \(70\).

  • legend (bool, optional) – If True prints a legend below the circuit for callbacks and channels. Defaults to False.

Returns:

String containing text circuit diagram.

Circuit addition#

qibo.models.circuit.Circuit objects support addition. For example

circuit_1 = QFT(4)

circuit_2 = Circuit(4)
circuit_2.add(gates.RZ(0, 0.1234))
circuit_2.add(gates.RZ(1, 0.1234))
circuit_2.add(gates.RZ(2, 0.1234))
circuit_2.add(gates.RZ(3, 0.1234))

circuit = circuit_1 + circuit_2

will create a circuit that performs the Quantum Fourier Transform on four qubits followed by Rotation-Z gates.

Circuit fusion#

The gates contained in a circuit can be fused up to two-qubits using the qibo.models.circuit.Circuit.fuse() method. This returns a new circuit for which the total number of gates is less than the gates in the original circuit as groups of gates have been fused to a single qibo.gates.special.FusedGate gate. Simulating the new circuit is equivalent to simulating the original one but in most cases more efficient since less gates need to be applied to the state vector.

The fusion algorithm works as follows: First all gates in the circuit are transformed to unmarked qibo.gates.special.FusedGate. The gates are then processed in the order they were added in the circuit. For each gate we identify the neighbors forth and back in time and attempt to fuse them to the gate. Two gates can be fused if their total number of target qubits is smaller than the fusion maximum qubits (specified by the user) and there are no other gates between acting on the same target qubits. Gates that are fused to others are marked. The new circuit queue contains the gates that remain unmarked after the above operations finish.

Gates are processed in the original order given by user. There are no additional simplifications performed such as commuting gates acting on the same qubit or canceling gates even when such simplifications are mathematically possible. The user can specify the maximum number of qubits in a fused gate using the max_qubits flag in qibo.models.circuit.Circuit.fuse().

For example the following:

from qibo import Circuit, gates

circuit = Circuit(2)
circuit.add([gates.H(0), gates.H(1)])
circuit.add(gates.CZ(0, 1))
circuit.add([gates.X(0), gates.Y(1)])
fused_circuit = circuit.fuse()

will create a new circuit with a single qibo.gates.special.FusedGate acting on (0, 1), while the following:

from qibo import Circuit, gates

circuit = Circuit(3)
circuit.add([gates.H(0), gates.H(1), gates.H(2)])
circuit.add(gates.CZ(0, 1))
circuit.add([gates.X(0), gates.Y(1), gates.Z(2)])
circuit.add(gates.CNOT(1, 2))
circuit.add([gates.H(0), gates.H(1), gates.H(2)])
fused_circuit = circuit.fuse()

will give a circuit with two fused gates, the first of which will act on (0, 1) corresponding to

[H(0), H(1), CZ(0, 1), X(0), H(0)]

and the second will act to (1, 2) corresponding to

[Y(1), Z(2), CNOT(1, 2), H(1), H(2)]

Quantum Fourier Transform (QFT)#

class qibo.models.qft.QFT(nqubits: int, with_swaps: bool = True, accelerators=None, **kwargs)[source]#

Creates a circuit that implements the Quantum Fourier Transform.

Parameters:
  • nqubits (int) – number of qubits in the circuit.

  • with_swaps (bool, optional) – If True, uses qibo.gates.SWAP gates at the end of the circuit so that the qubit order in the final state is the same as the initial state. Defauts to True.

  • accelerators (dict, optional) – Accelerator device dictionary in order to use a distributed circuit. If None, a simple (non-distributed) circuit will be used.

  • kwargs (dict, optional) – Additional arguments used to initialize qibo.models.Circuit. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

implementation of the Quantum Fourier Transform.

Return type:

qibo.models.Circuit

Example

import numpy as np
from qibo.models import QFT
nqubits = 6
circuit = QFT(nqubits)
# Random normalized initial state vector
init_state = np.random.random(2 ** nqubits) + 1j * np.random.random(2 ** nqubits)
init_state = init_state / np.sqrt((np.abs(init_state)**2).sum())
# Execute the circuit
final_state = circuit(init_state)

Variational Quantum Eigensolver (VQE)#

class qibo.models.variational.VQE(circuit, hamiltonian)[source]#

This class implements the variational quantum eigensolver algorithm.

Parameters:

Example

import numpy as np

from qibo import Circuit, gates
from qibo.hamiltonians import XXZ
from qibo.models import VQE

# create circuit ansatz for two qubits
circuit = Circuit(2)
circuit.add(gates.RY(0, theta=0))
# create XXZ Hamiltonian for two qubits
hamiltonian = XXZ(2)
# create VQE model for the circuit and Hamiltonian
vqe = VQE(circuit, hamiltonian)
# optimize using random initial variational parameters
initial_parameters = np.random.uniform(0, 2, 1)
vqe.minimize(initial_parameters)
minimize(initial_state, method='Powell', loss_func=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, compile=False, processes=None)[source]#

Search for parameters which minimizes the hamiltonian expectation.

Parameters:
  • initial_state (array) – a initial guess for the parameters of the variational circuit.

  • method (str) – the desired minimization method. See qibo.optimizers.optimize() for available optimization methods.

  • loss (callable) – loss function, the default one is qibo.models.utils.vqe_loss().

  • jac (dict) – Method for computing the gradient vector for scipy optimizers.

  • hess (dict) – Method for computing the hessian matrix for scipy optimizers.

  • hessp (callable) – Hessian of objective function times an arbitrary vector for scipy optimizers.

  • bounds (sequence or Bounds) – Bounds on variables for scipy optimizers.

  • constraints (dict) – Constraints definition for scipy optimizers.

  • tol (float) – Tolerance of termination for scipy optimizers.

  • callback (callable) – Called after each iteration for scipy optimizers.

  • options (dict) – a dictionary with options for the different optimizers.

  • compile (bool) – whether the TensorFlow graph should be compiled.

  • processes (int) – number of processes when using the paralle BFGS method.

Returns:

The final expectation value. The corresponding best parameters. The optimization result object. For scipy methods it returns the OptimizeResult, for 'cma' the CMAEvolutionStrategy.result, and for 'sgd' the options used during the optimization.

energy_fluctuation(state)[source]#

Evaluate energy fluctuation

\[\Xi_{k}(\mu) = \sqrt{\langle\mu|\hat{H}^2|\mu\rangle - \langle\mu|\hat{H}|\mu\rangle^2} \,\]

for a given state \(|\mu\rangle\).

Parameters:

state (np.ndarray) – quantum state to be used to compute the energy fluctuation with H.

Adiabatically Assisted Variational Quantum Eigensolver (AAVQE)#

class qibo.models.variational.AAVQE(circuit, easy_hamiltonian, problem_hamiltonian, s, nsteps=10, t_max=1, bounds_tolerance=1e-07, time_tolerance=1e-07)[source]#

This class implements the Adiabatically Assisted Variational Quantum Eigensolver algorithm. See https://arxiv.org/abs/1806.02287.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – variational ansatz.

  • easy_hamiltonian (qibo.hamiltonians.Hamiltonian) – initial Hamiltonian object.

  • problem_hamiltonian (qibo.hamiltonians.Hamiltonian) – problem Hamiltonian object.

  • s (callable) – scheduling function of time that defines the adiabatic evolution. It must verify boundary conditions: s(0) = 0 and s(1) = 1.

  • nsteps (float) – number of steps of the adiabatic evolution.

  • t_max (float) – total time evolution.

  • bounds_tolerance (float) – tolerance for checking s(0) = 0 and s(1) = 1.

  • time_tolerance (float) – tolerance for checking if time is greater than t_max.

Example

import numpy as np

from qibo import Circuit, gates
from qibo.hamiltonians import X, XXZ
from qibo.models import AAVQE

# create circuit ansatz for two qubits
circuit = Circuit(2)
circuit.add(gates.RY(0, theta=0))
circuit.add(gates.RY(1, theta=0))
# define the easy and the problem Hamiltonians.
easy_hamiltonian = X(2)
problem_hamiltonian = XXZ(2)
# define a scheduling function with only one parameter
# and boundary conditions s(0) = 0, s(1) = 1
s = lambda t: t
# create AAVQE model
aavqe = AAVQE(
    circuit,
    easy_hamiltonian,
    problem_hamiltonian,
    s,
    nsteps=10,
    t_max=1
)
# optimize using random initial variational parameters
np.random.seed(0)
initial_parameters = np.random.uniform(0, 2*np.pi, 2)
ground_energy, params = aavqe.minimize(initial_parameters)
set_schedule(func)[source]#

Set scheduling function s(t) as func.

schedule(t)[source]#

Returns scheduling function evaluated at time t: s(t/Tmax).

hamiltonian(t)[source]#

Returns the adiabatic evolution Hamiltonian at a given time.

minimize(params, method='BFGS', jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, options=None, compile=False, processes=None)[source]#

Performs minimization to find the ground state of the problem Hamiltonian.

Parameters:
  • params (np.ndarray or list) – initial guess for the parameters of the variational circuit.

  • method (str) – optimizer to employ.

  • jac (dict) – Method for computing the gradient vector for scipy optimizers.

  • hess (dict) – Method for computing the hessian matrix for scipy optimizers.

  • hessp (callable) – Hessian of objective function times an arbitrary vector for scipy optimizers.

  • bounds (sequence or Bounds) – Bounds on variables for scipy optimizers.

  • constraints (dict) – Constraints definition for scipy optimizers.

  • tol (float) – Tolerance of termination for scipy optimizers.

  • options (dict) – a dictionary with options for the different optimizers.

  • compile (bool) – whether the TensorFlow graph should be compiled.

  • processes (int) – number of processes when using the parallel BFGS method.

Quantum Approximate Optimization Algorithm (QAOA)#

class qibo.models.variational.QAOA(hamiltonian, mixer=None, solver='exp', callbacks=[], accelerators=None)[source]#

Quantum Approximate Optimization Algorithm (QAOA) model.

The QAOA is introduced in arXiv:1411.4028.

Parameters:

Example

import numpy as np
from qibo import models, hamiltonians
# create XXZ Hamiltonian for four qubits
hamiltonian = hamiltonians.XXZ(4)
# create QAOA model for this Hamiltonian
qaoa = models.QAOA(hamiltonian)
# optimize using random initial variational parameters
# and default options and initial state
initial_parameters = 0.01 * np.random.random(4)
best_energy, final_parameters, extra = qaoa.minimize(initial_parameters, method="BFGS")
set_parameters(p)[source]#

Sets the variational parameters.

Parameters:

p (np.ndarray) – 1D-array holding the new values for the variational parameters. Length should be an even number.

execute(initial_state=None)[source]#

Applies the QAOA exponential operators to a state.

Parameters:

initial_state (np.ndarray) – Initial state vector.

Returns:

State vector after applying the QAOA exponential gates.

minimize(initial_p, initial_state=None, method='Powell', loss_func=None, loss_func_param={}, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, compile=False, processes=None)[source]#

Optimizes the variational parameters of the QAOA. A few loss functions are provided for QAOA optimizations such as expected value (default), CVar which is introduced in Quantum 4, 256, and Gibbs loss function which is introduced in PRR 2, 023074 (2020).

Parameters:
  • initial_p (np.ndarray) – initial guess for the parameters.

  • initial_state (np.ndarray) – initial state vector of the QAOA.

  • method (str) – the desired minimization method. See qibo.optimizers.optimize() for available optimization methods.

  • loss_func (function) – the desired loss function. If it is None, the expectation is used.

  • loss_func_param (dict) – a dictionary to pass in the loss function parameters.

  • jac (dict) – Method for computing the gradient vector for scipy optimizers.

  • hess (dict) – Method for computing the hessian matrix for scipy optimizers.

  • hessp (callable) – Hessian of objective function times an arbitrary vector for scipy optimizers.

  • bounds (sequence or Bounds) – Bounds on variables for scipy optimizers.

  • constraints (dict) – Constraints definition for scipy optimizers.

  • tol (float) – Tolerance of termination for scipy optimizers.

  • callback (callable) – Called after each iteration for scipy optimizers.

  • options (dict) – a dictionary with options for the different optimizers.

  • compile (bool) – whether the TensorFlow graph should be compiled.

  • processes (int) – number of processes when using the paralle BFGS method.

Returns:

The final energy (expectation value of the hamiltonian). The corresponding best parameters. The optimization result object. For scipy methods it returns the OptimizeResult, for 'cma' the CMAEvolutionStrategy.result, and for 'sgd' the options used during the optimization.

Example

from qibo import hamiltonians
from qibo.models.utils import cvar, gibbs

h = hamiltonians.XXZ(3)
qaoa = models.QAOA(h)
initial_p = [0.314, 0.22, 0.05, 0.59]
best, params, _ = qaoa.minimize(initial_p)
best, params, _ = qaoa.minimize(initial_p, loss_func=cvar, loss_func_param={'alpha':0.1})
best, params, _ = qaoa.minimize(initial_p, loss_func=gibbs, loss_func_param={'eta':0.1})

Feedback-based Algorithm for Quantum Optimization (FALQON)#

class qibo.models.variational.FALQON(hamiltonian, mixer=None, solver='exp', callbacks=[], accelerators=None)[source]#

Feedback-based ALgorithm for Quantum OptimizatioN (FALQON) model.

The FALQON is introduced in arXiv:2103.08619. It inherits the QAOA class.

Parameters:

Example

import numpy as np
from qibo import models, hamiltonians
# create XXZ Hamiltonian for four qubits
hamiltonian = hamiltonians.XXZ(4)
# create FALQON model for this Hamiltonian
falqon = models.FALQON(hamiltonian)
# optimize using random initial variational parameters
# and default options and initial state
delta_t = 0.01
max_layers = 3
best_energy, final_parameters, extra = falqon.minimize(delta_t, max_layers)
minimize(delta_t, max_layers, initial_state=None, tol=None, callback=None)[source]#

Optimizes the variational parameters of the FALQON.

Parameters:
  • delta_t (float) – initial guess for the time step. A too large delta_t will make the algorithm fail.

  • max_layers (int) – maximum number of layers allowed for the FALQON.

  • initial_state (np.ndarray) – initial state vector of the FALQON.

  • tol (float) – Tolerance of energy change. If not specified, no check is done.

  • callback (callable) – Called after each iteration for scipy optimizers.

  • options (dict) – a dictionary with options for the different optimizers.

Returns:

The final energy (expectation value of the hamiltonian). The corresponding best parameters. extra: variable with historical data for the energy and callbacks.

Grover’s Algorithm#

class qibo.models.grover.Grover(oracle, superposition_circuit=None, initial_state_circuit=None, superposition_qubits=None, superposition_size=None, number_solutions=None, target_amplitude=None, check=None, check_args=(), iterative=False)[source]#

Model that performs Grover’s algorithm.

For Grover’s original search algorithm: arXiv:quant-ph/9605043 For the iterative version with unknown solutions:arXiv:quant-ph/9605034 For the Grover algorithm with any superposition:arXiv:quant-ph/9712011

Parameters:
  • oracle (qibo.core.circuit.Circuit) – quantum circuit that flips the sign using a Grover ancilla initialized with -X-H-. Grover ancilla expected to be last qubit of oracle circuit.

  • superposition_circuit (qibo.core.circuit.Circuit) – quantum circuit that takes an initial state to a superposition. Expected to use the first set of qubits to store the relevant superposition.

  • initial_state_circuit (qibo.core.circuit.Circuit) – quantum circuit that initializes the state. If empty defaults to |000..00>

  • superposition_qubits (int) – number of qubits that store the relevant superposition. Leave empty if superposition does not use ancillas.

  • superposition_size (int) – how many states are in a superposition. Leave empty if its an equal superposition of quantum states.

  • number_solutions (int) – number of expected solutions. Needed for normal Grover. Leave empty for iterative version.

  • target_amplitude (float) – absolute value of the amplitude of the target state. Only for advanced use and known systems.

  • check (function) – function that returns True if the solution has been found. Required of iterative approach. First argument should be the bitstring to check.

  • check_args (tuple) – arguments needed for the check function. The found bitstring not included.

  • iterative (bool) – force the use of the iterative Grover

Example

import numpy as np

from qibo import Circuit, gates
from qibo.models.grover import Grover

# Create an oracle. Ex: Oracle that detects state |11111>
oracle = Circuit(5 + 1)
oracle.add(gates.X(5).controlled_by(*range(5)))

# Create superoposition circuit. Ex: Full superposition over 5 qubits.
superposition = Circuit(5)
superposition.add([gates.H(i) for i in range(5)])

# Generate and execute Grover class
grover = Grover(oracle, superposition_circuit=superposition, number_solutions=1)
solution, iterations = grover()
initialize()[source]#

Initialize the Grover algorithm with the superposition and Grover ancilla.

diffusion()[source]#

Construct the diffusion operator out of the superposition circuit.

step()[source]#

Combine oracle and diffusion for a Grover step.

circuit(iterations)[source]#

Creates circuit that performs Grover’s algorithm with a set amount of iterations.

Parameters:

iterations (int) – number of times to repeat the Grover step.

Returns:

qibo.core.circuit.Circuit that performs Grover’s algorithm.

iterative_grover(lamda_value=1.2, backend=None)[source]#

Iterative approach of Grover for when the number of solutions is not known.

Parameters:
  • lamda_value (real) – parameter that controls the evolution of the iterative method. Must be between 1 and 4/3.

  • backend (qibo.backends.abstract.Backend) – Backend to use for circuit execution.

Returns:

bitstring measured and checked as a valid solution. total_iterations (int): number of times the oracle has been called.

Return type:

measured (str)

execute(nshots=100, freq=False, logs=False, backend=None)[source]#

Execute Grover’s algorithm.

If the number of solutions is given, calculates iterations, otherwise it uses an iterative approach.

Parameters:
  • nshots (int) – number of shots in order to get the frequencies.

  • freq (bool) – print the full frequencies after the exact Grover algorithm.

  • backend (qibo.backends.abstract.Backend) – Backend to use for circuit execution.

Returns:

bitstring (or list of bitstrings) measured as solution of the search. iterations (int): number of oracle calls done to reach a solution.

Return type:

solution (str)

Travelling Salesman Problem#

class qibo.models.tsp.TSP(distance_matrix, backend=None)[source]#

The travelling salesman problem (also called the travelling salesperson problem or TSP) asks the following question: “Given a list of cities and the distances between each pair of cities, what is the shortest possible route for a salesman to visit each city exactly once and return to the origin city?” It is an NP-hard problem in combinatorial optimization. It is also important in theoretical computer science and operations research.

This is a TSP class that enables us to implement TSP according to arxiv:1709.03489 by Hadfield (2017).

Parameters:
  • distance_matrix – a numpy matrix encoding the distance matrix.

  • backend – Backend to use for calculations. If not given the global backend will be used.

Example

from qibo.models.tsp import TSP
import numpy as np
from collections import defaultdict
from qibo import gates
from qibo.models import QAOA
from qibo.result import CircuitResult


def convert_to_standard_Cauchy(config):
    m = int(np.sqrt(len(config)))
    cauchy = [-1] * m  # Cauchy's notation for permutation, e.g. (1,2,0) or (2,0,1)
    for i in range(m):
        for j in range(m):
            if config[m * i + j] == '1':
                cauchy[j] = i  # citi i is in slot j
    for i in range(m):
        if cauchy[i] == 0:
            cauchy = cauchy[i:] + cauchy[:i]
            return tuple(cauchy)  # now, the cauchy notation for permutation begins with 0


def evaluate_dist(cauchy):
    '''
    Given a permutation of 0 to n-1, we compute the distance of the tour

    '''
    m = len(cauchy)
    return sum(distance_matrix[cauchy[i]][cauchy[(i+1)%m]] for i in range(m))


def qaoa_function_of_layer(layer, distance_matrix):
    '''
    This is a function to study the impact of the number of layers on QAOA, it takes
    in the number of layers and compute the distance of the mode of the histogram obtained
    from QAOA

    '''
    small_tsp = TSP(distance_matrix)
    obj_hamil, mixer = small_tsp.hamiltonians()
    qaoa = QAOA(obj_hamil, mixer=mixer)
    best_energy, final_parameters, extra = qaoa.minimize(initial_p=[0.1] * layer,
                                         initial_state=initial_state, method='BFGS')
    qaoa.set_parameters(final_parameters)
    quantum_state = qaoa.execute(initial_state)
    circuit = Circuit(9)
    circuit.add(gates.M(*range(9)))
    result = CircuitResult(quantum_state, circuit.measurements, small_tsp.backend, nshots=1000)
    freq_counter = result.frequencies()
    # let's combine freq_counter here, first convert each key and sum up the frequency
    cauchy_dict = defaultdict(int)
    for freq_key in freq_counter:
        standard_cauchy_key = convert_to_standard_Cauchy(freq_key)
        cauchy_dict[standard_cauchy_key] += freq_counter[freq_key]
    max_key = max(cauchy_dict, key=cauchy_dict.get)
    return evaluate_dist(max_key)

np.random.seed(42)
num_cities = 3
distance_matrix = np.array([[0, 0.9, 0.8], [0.4, 0, 0.1],[0, 0.7, 0]])
distance_matrix = distance_matrix.round(1)
small_tsp = TSP(distance_matrix)
initial_parameters = np.random.uniform(0, 1, 2)
initial_state = small_tsp.prepare_initial_state([i for i in range(num_cities)])
qaoa_function_of_layer(2, distance_matrix)
hamiltonians()[source]#
Returns:

The pair of Hamiltonian describes the phaser hamiltonian and the mixer hamiltonian.

prepare_initial_state(ordering)[source]#

To run QAOA by Hadsfield, we need to start from a valid permutation function to ensure feasibility.

Parameters:

ordering (array) – A list describing permutation from 0 to n-1

Returns:

An initial state that is used to start TSP QAOA.

Iterative Quantum Amplitude Estimation (IQAE)#

class qibo.models.iqae.IQAE(circuit_a, circuit_q, alpha=0.05, epsilon=0.005, n_shots=1024, method='chernoff')[source]#

Model that performs the Iterative Quantum Amplitude Estimation algorithm.

The implemented class in this code utilizes the Iterative Quantum Amplitude Estimation (IQAE) algorithm, which was proposed in arxiv:1912.05559. The algorithm provides an estimated output that, with a probability alpha, differs from the target value by epsilon. Both alpha and epsilon can be specified.

Unlike Brassard’s original QAE algorithm arxiv:quant-ph/0005055, this implementation does not rely on Quantum Phase Estimation but instead is based solely on Grover’s algorithm. The IQAE algorithm employs a series of carefully selected Grover iterations to determine an estimate for the target amplitude.

Parameters:
  • circuit_a (qibo.models.circuit.Circuit) – quantum circuit that specifies the QAE problem.

  • circuit_q (qibo.models.circuit.Circuit) – quantum circuit of the Grover/Amplification operator.

  • alpha (float) – confidence level, the target probability is 1 - alpha, has values between 0 and 1.

  • epsilon (float) – target precision for estimation target a, has values between 0 and 0.5.

  • method (str) – statistical method used to estimate the confidence intervals in each iteration, can be either chernoff (default) for the Chernoff intervals or beta for the Clopper-Pearson intervals.

  • n_shots (int) – number of shots.

Raises:
  • ValueError – If epsilon is not in (0, 0.5].

  • ValueError – If alpha is not in (0, 1).

  • ValueError – If method is not supported.

  • ValueError – If the number of qubits in circuit_a is greater than in circuit_q.

Example

from qibo import Circuit, gates
from qibo.models.iqae import IQAE

# Defining circuit A to integrate sin(x)^2 from [0,1]
a_circuit = Circuit(2)
a_circuit.add(gates.H(0))
a_circuit.add(gates.RY(q = 1, theta = 1 / 2))
a_circuit.add(gates.CU3(0, 1, 1, 0, 0))
# Defining circuit Q = -A S_0 A^-1 S_X
q_circuit = Circuit(2)
# S_X
q_circuit.add(gates.Z(q = 1))
# A^-1
q_circuit = q_circuit + a_circuit.invert()
# S_0
q_circuit.add(gates.X(0))
q_circuit.add(gates.X(1))
q_circuit.add(gates.CZ(0, 1))
# A
q_circuit = q_circuit + a_circuit

# Executing IQAE and obtaining the result
iae = IQAE(a_circuit, q_circuit)
results = iae.execute()
integral_value = results.estimation
integral_error = results.epsilon_estimated
construct_qae_circuit(k)[source]#

Generates quantum circuit for QAE.

Parameters:

k (int) – number of times the amplification operator circuit_q is applied.

Returns:

The quantum circuit of the QAE algorithm.

clopper_pearson(count, n, alpha)[source]#

Calculates the confidence interval for the quantity to estimate a.

Parameters:
  • count (int) – number of successes.

  • n (int) – total number of trials.

  • alpha (float) – significance level. Must be in (0, 0.5).

Returns:

The confidence interval [a_min, a_max].

h_calc_CP(n_successes, n_total_shots, upper_bound_t)[source]#

Calculates the h function.

Parameters:
  • n_successes (int) – number of successes.

  • n_total_shots (int) – total number of trials.

  • upper_bound_t (int) – maximum number of rounds to achieve the desired absolute error.

Returns:

The h function for the given inputs.

calc_L_range_CP(n_shots, upper_bound_t)[source]#

Calculate the confidence interval for the Clopper-Pearson method.

Parameters:
  • n_shots (int) – number of shots.

  • upper_bound_t (int) – maximum number of rounds to achieve the desired absolute error.

Returns:

The maximum and minimum possible error which could be returned on a given iteration.

Return type:

max_L, min_L (float, float)

calc_L_range_CH(n_shots, upper_bound_t)[source]#

Calculate the confidence interval for the Chernoff method.

Parameters:
  • n_shots (int) – number of shots.

  • upper_bound_t (int) – maximum number of rounds to achieve the desired absolute error.

Returns:

The maximum and minimum possible error which could be returned on a given iteration.

Return type:

max_L, min_L (float, float)

find_next_k(uppercase_k_i, up_i, theta_l, theta_u, r=2)[source]#

Find the largest integer uppercase_k such that the interval uppercase_k * [ theta_l , theta_u ] lies completely in [0, pi] or [pi, 2 pi].

Parameters:
  • uppercase_k_i (int) – the current uppercase_k such uppercase_k = 4 k + 2, where k is the power of the operator circuit_q.

  • up_i (bool) – boolean flag of whether theta_interval lies in the upper half-circle [0, pi] or in the lower one [pi, 2 pi].

  • theta_l (float) – the current lower limit of the confidence interval for the angle theta.

  • theta_u (float) – the current upper limit of the confidence interval for the angle theta.

  • r (int) – lower bound for uppercase_k.

Returns:

The next power K_i, and boolean flag for the extrapolated interval.

execute(backend=None)[source]#

Execute IQAE algorithm.

Parameters:

backend – the qibo backend.

Returns:

A qibo.models.iqae.IterativeAmplitudeEstimationResult results object.

class qibo.models.iqae.IterativeAmplitudeEstimationResult[source]#

The IterativeAmplitudeEstimationResult result object.

Double Bracket Iteration algorithm for Diagonalization#

The Double Bracket Flow (DBF) has been presented here as a novel strategy for preparing eigenstates of a quantum system. We implement in Qibo a discretized version of the algorithm, which executes sequential Double Bracket Iterations.

class qibo.models.dbi.double_bracket.DoubleBracketGeneratorType(value)[source]#

Define DBF evolution.

canonical = 1#

Use canonical commutator.

single_commutator = 2#

Use single commutator.

group_commutator = 3#

Use group commutator approximation

group_commutator_third_order = 4#

Implements Eq. (8) of Ref. [1], i.e.

\[e^{\frac{\sqrt{5}-1}{2}isH} \, e^{\frac{\sqrt{5}-1}{2}isD} \, e^{-isH} \, e^{isD} \, e^{\frac{3-\sqrt{5}}{2}isH} \, e^{isD} \approx e^{-s^2[H,D]} + O(s^4) \, .\]

\(s\) must be taken as \(\sqrt{s}\) to approximate the flow using the commutator.

class qibo.models.dbi.double_bracket.DoubleBracketIteration(hamiltonian, mode=DoubleBracketGeneratorType.canonical, scheduling=<function grid_search_step>, cost=DoubleBracketCostFunction.off_diagonal_norm, ref_state=None)[source]#

Class implementing the Double Bracket iteration algorithm. For more details, see Ref. [1].

Parameters:
  • hamiltonian (qibo.hamiltonians.Hamiltonian) – starting Hamiltonian.

  • mode (qibo.models.dbi.double_bracket.DoubleBracketGeneratorType) – type of generator of the evolution.

  • scheduling (qibo.models.dbi.double_bracket.DoubleBracketScheduling) – type of scheduling strategy.

  • cost (qibo.models.dbi.double_bracket.DoubleBracketCostFunction) – type of cost function.

  • ref_state (ndarray) – reference state for computing the energy fluctuation.

Example

from qibo.models.dbi.double_bracket import DoubleBracketIteration, DoubleBracketGeneratorType
from qibo.quantum_info import random_hermitian
from qibo.hamiltonians import Hamiltonian

nqubits = 4
h0 = random_hermitian(2**nqubits, seed=2)
dbf = DoubleBracketIteration(Hamiltonian(nqubits=nqubits, matrix=h0))

# diagonalized matrix
dbf.h

References

1. M. Gluza, Double-bracket quantum algorithms for diagonalization. arXiv:2206.11772 [quant-ph].

eval_dbr_unitary(step: float, mode=None, d=None)[source]#

In qibo.models.dbi.double_bracket.DoubleBracketIteration.__call__(), we are working in the following convention:

\[H^{'} = U^{\dagger} \, H \, U \, ,\]

where \(U = e^{-s\,W}\), and \(W = [D, H]\) (or an approximation of that by a group commutator). That is convenient because if we switch from the DBI in the Heisenberg picture for the Hamiltonian, we get that the transformation of the state is \(|\psi'\rangle = U \, |\psi\rangle\), so that

\[\langle H\rangle_{\psi'} = \langle H' \rangle_\psi \, ,\]

i.e. when writing the unitary acting on the state dagger notation is avoided). The group commutator must approximate \(U = e^{-s\, [D,H]}\). This is achieved by setting \(r = \sqrt{s}\) so that

\[V = e^{-i\,r\,H} \, e^{i\,r\,D} \, e^{i\,r\,H} \, e^{-i\,r\,D}\]

because

\[e^{-i\,r\,H} \, D \, e^{i\,r\,H} = D + i\,r\,[D, H] +O(r^2)\]

so

\[V \approx \exp\left(i\,r\,D + i^2 \, r^2 \, [D, H] + O(r^2) -i\,r\,D\right) \approx U \, .\]

See the Appendix in Ref. [1] for the complete derivation.

References

1. M. Gluza, Double-bracket quantum algorithms for diagonalization. arXiv:2206.11772 [quant-ph].

static commutator(a, b)[source]#

Compute commutator between two arrays.

property diagonal_h_matrix#

Diagonal H matrix.

property off_diag_h#

Off-diagonal H matrix.

property off_diagonal_norm#

Hilbert Schmidt norm of off-diagonal part of H matrix, namely \(\text{Tr}(\sqrt{A^{\dagger} A})\).

property backend#

Get Hamiltonian’s backend.

property nqubits#

Number of qubits.

least_squares(d: array)[source]#

Least squares cost function.

choose_step(d: array | None = None, scheduling: DoubleBracketScheduling | None = None, **kwargs)[source]#

Calculate the optimal step using respective the scheduling methods.

loss(step: float, d: array | None = None, look_ahead: int = 1)[source]#

Compute loss function distance between look_ahead steps.

Parameters:
  • step (float) – iteration step.

  • d (np.array) – diagonal operator, use canonical by default.

  • look_ahead (int) – number of iteration steps to compute the loss function;

energy_fluctuation(state)[source]#

Evaluate energy fluctuation.

\[\Xi(\mu) = \sqrt{\langle\mu|\hat{H}^2|\mu\rangle - \langle\mu|\hat{H}|\mu\rangle^2} \,\]

for a given state \(|\mu\rangle\).

Parameters:

state (np.ndarray) – quantum state to be used to compute the energy fluctuation with H.

sigma(h: array)[source]#

Returns the off-diagonal restriction of matrix h.

generate_gamma_list(n: int, d: array)[source]#

Computes the n-nested Gamma functions, where $Gamma_k=[W,…,[W,[W,H]]…]$, where we take k nested commutators with $W = [D, H]$

Time evolution#

State evolution#

class qibo.models.evolution.StateEvolution(hamiltonian, dt, solver='exp', callbacks=[], accelerators=None)[source]#

Unitary time evolution of a state vector under a Hamiltonian.

Parameters:
  • hamiltonian (qibo.hamiltonians.abstract.AbstractHamiltonian) – Hamiltonian to evolve under.

  • dt (float) – Time step to use for the numerical integration of Schrondiger’s equation.

  • solver (str) – Solver to use for integrating Schrodinger’s equation. Available solvers are ‘exp’ which uses the exact unitary evolution operator and ‘rk4’ or ‘rk45’ which use Runge-Kutta methods to integrate the Schordinger’s time-dependent equation in time. When the ‘exp’ solver is used to evolve a qibo.hamiltonians.hamiltonians.SymbolicHamiltonian then the Trotter decomposition of the evolution operator will be calculated and used automatically. If the ‘exp’ is used on a dense qibo.core.hamiltonians.hamiltonians.Hamiltonian the full Hamiltonian matrix will be exponentiated to obtain the exact evolution operator. Runge-Kutta solvers use simple matrix multiplications of the Hamiltonian to the state and no exponentiation is involved.

  • callbacks (list) – List of callbacks to calculate during evolution.

  • accelerators (dict) – Dictionary of devices to use for distributed execution. This option is available only when the Trotter decomposition is used for the time evolution.

Example

import numpy as np
from qibo import models, hamiltonians
# create critical (h=1.0) TFIM Hamiltonian for three qubits
hamiltonian = hamiltonians.TFIM(3, h=1.0)
# initialize evolution model with step dt=1e-2
evolve = models.StateEvolution(hamiltonian, dt=1e-2)
# initialize state to |+++>
initial_state = np.ones(8) / np.sqrt(8)
# execute evolution for total time T=2
final_state2 = evolve(final_time=2, initial_state=initial_state)
execute(final_time, start_time=0.0, initial_state=None)[source]#

Runs unitary evolution for a given total time.

Parameters:
  • final_time (float) – Final time of evolution.

  • start_time (float) – Initial time of evolution. Defaults to t=0.

  • initial_state (np.ndarray) – Initial state of the evolution.

Returns:

Final state vector a tf.Tensor or a qibo.core.distutils.DistributedState when a distributed execution is used.

Adiabatic evolution#

class qibo.models.evolution.AdiabaticEvolution(h0, h1, s, dt, solver='exp', callbacks=[], accelerators=None)[source]#

Adiabatic evolution of a state vector under the following Hamiltonian:

\[H(t) = (1 - s(t)) H_0 + s(t) H_1\]
Parameters:
  • h0 (qibo.hamiltonians.abstract.AbstractHamiltonian) – Easy Hamiltonian.

  • h1 (qibo.hamiltonians.abstract.AbstractHamiltonian) – Problem Hamiltonian. These Hamiltonians should be time-independent.

  • s (callable) – Function of time that defines the scheduling of the adiabatic evolution. Can be either a function of time s(t) or a function with two arguments s(t, p) where p corresponds to a vector of parameters to be optimized.

  • dt (float) – Time step to use for the numerical integration of Schrondiger’s equation.

  • solver (str) – Solver to use for integrating Schrodinger’s equation. Available solvers are ‘exp’ which uses the exact unitary evolution operator and ‘rk4’ or ‘rk45’ which use Runge-Kutta methods to integrate the Schordinger’s time-dependent equation in time. When the ‘exp’ solver is used to evolve a qibo.hamiltonians.hamiltonians.SymbolicHamiltonian then the Trotter decomposition of the evolution operator will be calculated and used automatically. If the ‘exp’ is used on a dense qibo.hamiltonians.hamiltonians.Hamiltonian the full Hamiltonian matrix will be exponentiated to obtain the exact evolution operator. Runge-Kutta solvers use simple matrix multiplications of the Hamiltonian to the state and no exponentiation is involved.

  • callbacks (list) – List of callbacks to calculate during evolution.

  • accelerators (dict) – Dictionary of devices to use for distributed execution. This option is available only when the Trotter decomposition is used for the time evolution.

property schedule#

Returns scheduling as a function of time.

set_parameters(params)[source]#

Sets the variational parameters of the scheduling function.

minimize(initial_parameters, method='BFGS', options=None, messages=False)[source]#

Optimize the free parameters of the scheduling function.

Parameters:
  • initial_parameters (np.ndarray) – Initial guess for the variational parameters that are optimized. The last element of the given array should correspond to the guess for the total evolution time T.

  • method (str) – The desired minimization method. One of "cma" (genetic optimizer), "sgd" (gradient descent) or any of the methods supported by scipy.optimize.minimize.

  • options (dict) – a dictionary with options for the different optimizers.

  • messages (bool) – If True the loss evolution is shown during optimization.

Data Encoders#

We provide a family of algorithms that encode classical data into quantum circuits.

Computational Basis Encoder#

Given a bitstring \(b\) of length \(n\), this encoder generates a layer of Pauli-\(X\) gates that creates the quantum state \(|\,b\,\rangle\).

For instance, the following two circuit generations are equivalent:

b = "101"
circuit_1 = comp_basis_encoder(b)

circuit_2 = Circuit(3)
circuit_2.add(gates.X(0))
circuit_2.add(gates.X(2))
../_images/comp_basis_encoder.png
qibo.models.encodings.comp_basis_encoder(basis_element: int | str | list | tuple, nqubits: int | None = None, **kwargs)[source]#

Creates circuit that performs encoding of bitstrings into computational basis states.

Parameters:
  • basis_element (int or str or list or tuple) – bitstring to be encoded. If int, nqubits must be specified. If str, must be composed of only \(0`s and :math:`1`s. If ``list`\) or tuple, must be composed of \(0`s and :math:`1`s as ``int`\) or str.

  • nqubits (int, optional) – total number of qubits in the circuit. If basis_element is int, nqubits must be specified. If nqubits is None, nqubits defaults to length of basis_element. Defaults to None.

  • kwargs (dict, optional) – Additional arguments used to initialize a Circuit object. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

circuit encoding computational basis element.

Return type:

qibo.models.circuit.Circuit

Phase Encoder#

Encodes data of length \(n\) into the phases of \(n\) qubits.

For instance, the following two circuit generations are equivalent:

nqubits = 3
phases = np.random.rand(nqubits)

circuit_1 = phase_encoder(phases, rotation="RX")

circuit_2 = Circuit(3)
circuit_2.add(gates.RX(qubit, phases[qubit]) for qubit in range(nqubits))
../_images/phase_encoder.png
qibo.models.encodings.phase_encoder(data, rotation: str = 'RY', **kwargs)[source]#

Creates circuit that performs the phase encoding of data.

Parameters:
Returns:

circuit that loads data in phase encoding.

Return type:

qibo.models.circuit.Circuit

Unary Encoder#

Given a classical data array \(\mathbf{x} \in \mathbb{R}^{d}\) such that

\[\mathbf{x} = (x_{1}, x_{2}, \dots, x_{d}) \, ,\]

this function generate the circuit that prepares the following quantum state \(\ket{\psi} \in \mathcal{H}\):

\[\ket{\psi} = \frac{1}{\|\mathbf{x}\|_{\textup{HS}}} \, \sum_{k=1}^{d} \, x_{k} \, \ket{k} \, ,\]

with \(\mathcal{H} \cong \mathbb{C}^{d}\) being a \(d\)-qubit Hilbert space, and \(\|\cdot\|_{\textup{HS}}\) being the Hilbert-Schmidt norm.

Here, \(\ket{k}\) is a unary representation of the number \(k\). For instance, for \(d = 3\), the final state would be

\[\ket{\psi} = \frac{1}{\|\mathbf{x}\|_{\textup{HS}}} \, \left( x_{1} \ket{001} + x_{2} \ket{010} + x_{3} \ket{100} \right) \, .\]

There are multiple circuit architechtures that lead to unary encoding of classical data. For example, to encode a \(8\)-dimensional data, one could use the so-called tree architechture below:

../_images/unary_encoder_tree.png

where the first gate is the qibo.gates.X and the parametrized gates are the qibo.gates.RBS. To know how the angles \(\{\theta_{k}\}_{[k]}\) are calculated for this architecture, please refer to S. Johri et al., Nearest Centroid Classification on a Trapped Ion Quantum Computer, arXiv:2012.04145v2 [quant-ph].

On the other hand, the same encoding could be performed using the so-called diagonal (also known as ladder) architecture below:

../_images/unary_encoder_ladder.png

This architecture leads to a choice of angles based on spherical coordinates in a d-dimensional hypersphere.

qibo.models.encodings.unary_encoder(data, architecture: str = 'tree', **kwargs)[source]#

Creates circuit that performs the (deterministic) unary encoding of data.

Parameters:
  • data (ndarray) – \(1\)-dimensional array of data to be loaded.

  • architecture (str, optional) – circuit architecture used for the unary loader. If diagonal, uses a ladder-like structure. If tree, uses a binary-tree-based structure. Defaults to tree.

  • kwargs (dict, optional) – Additional arguments used to initialize a Circuit object. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

circuit that loads data in unary representation.

Return type:

qibo.models.circuit.Circuit

Unary Encoder for Random Gaussian States#

Performs the same unary encoder as qibo.models.encodings.unary_encoder using the tree architecture , with the difference being that now each entry of the \(d\)-dimensional array is sampled from a Gaussian distribution \(\mathcal{N}(0, 1)\).

qibo.models.encodings.unary_encoder_random_gaussian(nqubits: int, architecture: str = 'tree', seed=None, **kwargs)[source]#

Creates a circuit that performs the unary encoding of a random Gaussian state.

At depth \(h\) of the tree architecture, the angles \(\theta_{k} \in [0, 2\pi]\) of the the gates \(RBS(\theta_{k})\) are sampled from the following probability density function:

\[p_{h}(\theta) = \frac{1}{2} \, \frac{\Gamma(2^{h-1})}{\Gamma^{2}(2^{h-2})} \, \left|\sin(\theta) \, \cos(\theta)\right|^{2^{h-1} - 1} \, ,\]

where \(\Gamma(\cdot)\) is the Gamma function.

Parameters:
  • nqubits (int) – number of qubits.

  • architecture (str, optional) – circuit architecture used for the unary loader. If tree, uses a binary-tree-based structure. Defaults to tree.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Defaults to None.

  • kwargs (dict, optional) – Additional arguments used to initialize a Circuit object. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

circuit that loads a random Gaussian array in unary representation.

Return type:

qibo.models.circuit.Circuit

References

1. A. Bouland, A. Dandapani, and A. Prakash, A quantum spectral method for simulating stochastic processes, with applications to Monte Carlo. arXiv:2303.06719v1 [quant-ph]

Entangling layer#

Generates a layer of nearest-neighbour two-qubit gates, assuming 1-dimensional connectivity. With the exception of qibo.gates.gates.GeneralizedfSim, any of the two-qubit gates implemented in qibo can be selected to customize the entangling layer. If the chosen gate is parametrized, all phases are set to \(0.0\). Note that these phases can be updated a posterior by using qibo.models.Circuit.set_parameters(). There are four possible choices of layer architecture: diagonal, shifted, even-layer, and odd-layer. For instance, we show below an example of each architecture for nqubits = 6.

../_images/entangling_layer.png

If closed_boundary is set to True, then an extra gate is added connecting the last and the first qubit, with the last qubit as the control qubit and the first qubit as a target qubit.

qibo.models.encodings.entangling_layer(nqubits: int, architecture: str = 'diagonal', entangling_gate: str | Gate = 'CNOT', closed_boundary: bool = False, **kwargs)[source]#

Creates a layer of two-qubit, entangling gates.

If the chosen gate is a parametrized gate, all phases are set to \(0.0\).

Parameters:
  • nqubits (int) – Total number of qubits in the circuit.

  • architecture (str, optional) – Architecture of the entangling layer. Options are diagonal, shifted, even-layer, and odd-layer. Defaults to "diagonal".

  • entangling_gate (str or qibo.gates.Gate, optional) – Two-qubit gate to be used in the entangling layer. If entangling_gate is a parametrized gate, all phases are initialized as \(0.0\). Defaults to "CNOT".

  • closed_boundary (bool, optional) – If True adds a closed-boundary condition to the entangling layer. Defaults to False.

  • kwargs (dict, optional) – Additional arguments used to initialize a Circuit object. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

Circuit containing layer of two-qubit gates.

Return type:

qibo.models.circuit.Circuit

Greenberger-Horne-Zeilinger (GHZ) state#

qibo.models.encodings.ghz_state(nqubits: int, **kwargs)[source]#

Generates an \(n\)-qubit Greenberger-Horne-Zeilinger (GHZ) state that takes the form

\[\ket{\text{GHZ}} = \frac{\ket{0}^{\otimes n} + \ket{1}^{\otimes n}}{\sqrt{2}}\]

where \(n\) is the number of qubits.

Parameters:
  • nqubits (int) – number of qubits \(n >= 2\).

  • kwargs (dict, optional) – additional arguments used to initialize a Circuit object. For details, see the documentation of qibo.models.circuit.Circuit.

Returns:

Circuit that prepares the GHZ state.

Return type:

qibo.models.circuit.Circuit

Error Mitigation#

Qibo allows for mitigating noise in circuits via error mitigation methods. Unlike error correction, error mitigation does not aim to correct qubit errors, but rather it provides the means to estimate the noise-free expected value of an observable measured at the end of a noisy circuit.

Readout Mitigation#

A common kind of error happening in quantum circuits is readout error, i.e. the error in the measurement of the qubits at the end of the computation. In Qibo there are currently two methods implemented for mitigating readout errors, and both can be used as standalone functions or in combination with the other general mitigation methods by setting the paramter readout.

Response Matrix#

Given \(n\) qubits, all the possible \(2^n\) states are constructed via the application of the corresponding sequence of \(X\) gates \(X_0\otimes I_1\otimes\cdot\cdot\cdot\otimes X_{n-1}\). In the presence of readout errors, we will measure for each state \(i\) some noisy frequencies \(F_i^{noisy}\) different from the ideal ones \(F_i^{ideal}=\delta_{i,j}\).

The effect of the error is modeled by the response matrix composed of the noisy frequencies as columns \(M=\big(F_0^{noisy},...,F_{n-1}^{noisy}\big)\). We have indeed that:

\[F_i^{noisy} = M \cdot F_i^{ideal}\]

and, therefore, the calibration matrix obtained as \(M_{\text{cal}}=M^{-1}\) can be used to recover the noise-free frequencies.

The calibration matrix \(M_{\text{cal}}\) lacks stochasticity, resulting in a ‘negative probability’ issue. The distributions that arise after applying \(M_{\text{cal}}\) are quasiprobabilities; the individual elements can be negative surpass 1, provided they sum to 1. It is posible to use Iterative Bayesian Unfolding (IBU) to preserve non-negativity. See Nachman et al for more details.

qibo.models.error_mitigation.get_response_matrix(nqubits, qubit_map=None, noise_model=None, nshots: int = 10000, backend=None)[source]#

Computes the response matrix for readout mitigation.

Parameters:
  • nqubits (int) – Total number of qubits.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • noise_model (qibo.noise.NoiseModel, optional) – noise model used for simulating noisy computation. This matrix can be used to mitigate the effect of qibo.noise.ReadoutError.

  • nshots (int, optional) – number of shots. Defaults to \(10000\).

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

the computed (nqubits, nqubits) response matrix for

readout mitigation.

Return type:

numpy.ndarray

qibo.models.error_mitigation.iterative_bayesian_unfolding(probabilities, response_matrix, iterations=10)[source]#

Iterative Bayesian Unfolding (IBU) method for readout mitigation.

Parameters:
  • probabilities (numpy.ndarray) – the input probabilities to be unfolded.

  • response_matrix (numpy.ndarray) – the response matrix.

  • iterations (int, optional) – the number of iterations to perform. Defaults to 10.

Returns:

the unfolded probabilities.

Return type:

numpy.ndarray

Reference:
  1. B. Nachman, M. Urbanek et al, Unfolding Quantum Computer Readout Noise. arXiv:1910.01969 [quant-ph].

  2. S. Srinivasan, B. Pokharel et al, Scalable Measurement Error Mitigation via Iterative Bayesian Unfolding. arXiv:2210.12284 [quant-ph].

qibo.models.error_mitigation.apply_resp_mat_readout_mitigation(state, response_matrix, iterations=None)[source]#

Applies readout error mitigation to the given state using the provided response matrix.

Parameters:
  • state (qibo.measurements.CircuitResult) – the input state to be updated. This state should contain the frequencies that need to be mitigated.

  • response_matrix (numpy.ndarray) – the response matrix for readout mitigation.

  • iterations (int, optional) – the number of iterations to use for the Iterative Bayesian Unfolding method. If None the ‘inverse’ method is used. Defaults to None.

Returns:

the input state with the updated (mitigated) frequencies.

Return type:

qibo.measurements.CircuitResult

qibo.models.error_mitigation.apply_randomized_readout_mitigation(circuit, noise_model=None, nshots: int = 10000, ncircuits: int = 10, qubit_map=None, seed=None, backend=None)[source]#

Readout mitigation method that transforms the bias in an expectation value into a measurable multiplicative factor.

This factor can be eliminated at the expense of increased sampling complexity for the observable.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit.

  • noise_model (qibo.noise.NoiseModel, optional) – noise model used for simulating noisy computation. Defaults to None.

  • nshots (int, optional) – number of shots. Defaults to \(10000\).

  • ncircuits (int, optional) – number of randomized circuits. Each of them uses int(nshots / ncircuits) shots. Defaults to 10.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

the state of the input circuit with

mitigated frequencies.

Return type:

qibo.measurements.CircuitResult

References

1. Ewout van den Berg, Zlatko K. Minev et al, Model-free readout-error mitigation for quantum expectation values. arXiv:2012.09738 [quant-ph].

qibo.models.error_mitigation.get_expectation_val_with_readout_mitigation(circuit, observable, noise_model=None, nshots: int = 10000, readout=None, qubit_map=None, seed=None, backend=None)[source]#

Applies readout error mitigation to the given circuit and observable.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit.

  • observable (qibo.hamiltonians.Hamiltonian/:class:`qibo.hamiltonians.SymbolicHamiltonian) – The observable to be measured.

  • noise_model (qibo.models.noise.Noise, optional) – the noise model to be applied. Defaults to None.

  • nshots (int, optional) – the number of shots for the circuit execution. Defaults to \(10000\).

  • readout (dict, optional) –

    a dictionary that may contain the following keys:

    • ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.

    • response_matrix: numpy.ndarray, used for applying a pre-computed response matrix for readout error mitigation.

    • ibu_iters: int, specifies the number of iterations for the iterative Bayesian unfolding method of readout error mitigation. If provided, the corresponding readout error mitigation method is used. Defaults to {}.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – the backend to be used in the execution. If None, it uses the global backend. Defaults to None.

Returns:

the mitigated expectation value of the observable.

Return type:

float

Randomized readout mitigation#

This approach converts the effect of any noise map \(A\) into a single multiplication factor for each Pauli observable, that is, diagonalizes the measurement channel. The multiplication factor \(\lambda\) can be directly measured even without the quantum circuit. Dividing the measured value \(\langle O\rangle_{noisy}\) by these factor results in the mitigated Pauli expectation value \(\langle O\rangle_{ideal}\),

\[\langle O\rangle_{ideal} = \frac{\langle O\rangle_{noisy}}{\lambda}\]

This process can be implemented with the aforementioned qibo.models.error_mitigation.apply_randomized_readout_mitigation().

Zero Noise Extrapolation (ZNE)#

Given a noisy circuit \(C\) and an observable \(A\), Zero Noise Extrapolation (ZNE) consists in running \(n+1\) versions of the circuit with different noise levels \(\{c_j\}_{j=0..n}\) and, for each of them, measuring the expected value of the observable \(E_j=\langle A\rangle_j\).

Then, an estimate for the expected value of the observable in the noise-free condition is obtained as:

\[\hat{E} = \sum_{j=0}^n \gamma_jE_j\]

with \(\gamma_j\) satisfying:

\[\sum_{j=0}^n \gamma_j = 1 \qquad \sum_{j=0}^n \gamma_j c_j^k = 0 \quad \text{for}\,\, k=1,..,n\]

This implementation of ZNE relies on the insertion of gate pairs (that resolve to the identity in the noise-free case) to realize the different noise levels \(\{c_j\}\), see He et al for more details. Hence, the canonical levels are mapped to the number of inserted pairs as \(c_j\rightarrow 2 c_j + 1\).

qibo.models.error_mitigation.ZNE(circuit, observable, noise_levels, noise_model=None, nshots=10000, solve_for_gammas=False, global_unitary_folding=True, insertion_gate='CNOT', readout=None, qubit_map=None, seed=None, backend=None)[source]#

Runs the Zero Noise Extrapolation method for error mitigation.

The different noise levels are realized by the insertion of pairs of either CNOT or RX(pi/2) gates that resolve to the identiy in the noise-free case.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit.

  • observable (qibo.hamiltonians.Hamiltonian/:class:`qibo.hamiltonians.SymbolicHamiltonian) – Observable to measure.

  • noise_levels (numpy.ndarray) – Sequence of noise levels.

  • noise_model (qibo.noise.NoiseModel, optional) – Noise model applied to simulate noisy computation.

  • nshots (int, optional) – Number of shots. Defaults to \(10000\).

  • solve_for_gammas (bool, optional) – If True, explicitly solve the equations to obtain the gamma coefficients. Default is False.

  • global_unitary_folding (bool, optional) – If True, noise is increased by global unitary folding. If False, local unitary folding is used. Defaults to True.

  • insertion_gate (str, optional) – gate to be folded in the local unitary folding. If RX, the gate used is :math:RX(\pi / 2). Otherwise, it is the CNOT gate.

  • readout (dict, optional) –

    a dictionary that may contain the following keys:

    • ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.

    • response_matrix: numpy.ndarray, used for applying a pre-computed response matrix for readout error mitigation.

    • ibu_iters: int, specifies the number of iterations for the iterative Bayesian unfolding method of readout error mitigation. If provided, the corresponding readout error mitigation method is used. Defaults to {}.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Estimate of the expected value of observable in the noise free condition.

Return type:

numpy.ndarray

Reference:
  1. K. Temme, S. Bravyi et al, Error mitigation for short-depth quantum circuits. arXiv:1612.02058 [quant-ph].

qibo.models.error_mitigation.get_gammas(noise_levels, analytical: bool = True)[source]#

Standalone function to compute the ZNE coefficients given the noise levels.

Parameters:
  • noise_levels (numpy.ndarray) – array containing the different noise levels. Note that in the CNOT insertion paradigm this corresponds to the number of CNOT pairs to be inserted. The canonical ZNE noise levels are obtained as 2 * c + 1.

  • analytical (bool, optional) – if True, computes the coeffients by solving the linear system. If False, use the analytical solution valid for the CNOT insertion method. Default is True.

Returns:

the computed coefficients.

Return type:

numpy.ndarray

qibo.models.error_mitigation.get_noisy_circuit(circuit, num_insertions: int, global_unitary_folding=True, insertion_gate=None)[source]#

Standalone function to generate the noisy circuit with the inverse gate pairs insertions.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – circuit to modify.

  • num_insertions (int) – number of insertion gate pairs / global unitary folds to add.

  • global_unitary_folding (bool) – If True, noise is increased by global unitary folding. If False, local unitary folding is used. Defaults to True.

  • insertion_gate (str, optional) – gate to be folded in the local unitary folding. If RX, the gate used is :math:RX(\pi / 2). Otherwise, it is the CNOT gate.

Returns:

circuit with the inserted gate pairs or with global folding.

Return type:

qibo.models.Circuit

Clifford Data Regression (CDR)#

In the Clifford Data Regression (CDR) method, a set of \(n\) circuits \(S_n=\{C_i\}_{i=1,..,n}\) is generated starting from the original circuit \(C_0\) by replacing some of the non-Clifford gates with Clifford ones. Given an observable \(A\), all the circuits of \(S_n\) are both simulated to obtain the correspondent expected values of \(A\) in noise-free condition \(\{a_i^{exact}\}_{i=1,..,n}\), and run in noisy conditions to obtain the noisy expected values \(\{a_i^{noisy}\}_{i=1,..,n}\).

Finally a model \(f\) is trained to minimize the mean squared error:

\[E = \sum_{i=1}^n \bigg(a_i^{exact}-f(a_i^{noisy})\bigg)^2\]

and learn the mapping \(a^{noisy}\rightarrow a^{exact}\). The mitigated expected value of \(A\) at the end of \(C_0\) is then obtained simply with \(f(a_0^{noisy})\).

In this implementation the initial circuit is expected to be decomposed in the three Clifford gates \(RX(\frac{\pi}{2})\), \(CNOT\), \(X\) and in \(RZ(\theta)\) (which is Clifford only for \(\theta=\frac{n\pi}{2}\)). By default the set of Clifford gates used for substitution is \(\{RZ(0),RZ(\frac{\pi}{2}),RZ(\pi),RZ(\frac{3}{2}\pi)\}\). See Sopena et al for more details.

qibo.models.error_mitigation.CDR(circuit, observable, noise_model, nshots: int = 10000, model=<function <lambda>>, n_training_samples: int = 100, full_output: bool = False, readout=None, qubit_map=None, seed=None, backend=None)[source]#

Runs the Clifford Data Regression error mitigation method.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit decomposed in the primitive gates X, CNOT, RX(pi/2), RZ(theta).

  • observable (qibo.hamiltonians.Hamiltonian/:class:`qibo.hamiltonians.SymbolicHamiltonian) – observable to be measured.

  • noise_model (qibo.noise.NoiseModel) – noise model used for simulating noisy computation.

  • nshots (int, optional) – number of shots. Defaults \(10000\).

  • model (callable, optional) – model used for fitting. This should be a callable function object f(x, *params), taking as input the predictor variable and the parameters. Default is a simple linear model f(x,a,b) := a*x + b.

  • n_training_samples (int, optional) – number of training circuits to sample. Defaults to 100.

  • full_output (bool, optional) – if True, this function returns additional information: val, optimal_params, train_val. Defaults to False.

  • readout (dict, optional) –

    a dictionary that may contain the following keys:

    • ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.

    • response_matrix: numpy.ndarray, used for applying a pre-computed response matrix for readout error mitigation.

    • ibu_iters: int, specifies the number of iterations for the iterative Bayesian unfolding method of readout error mitigation. If provided, the corresponding readout error mitigation method is used. Defaults to {}.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Mitigated expectation value of observable. val (float): Noisy expectation value of observable. optimal_params (list): Optimal values for params. train_val (dict): Contains the noise-free and noisy expectation values obtained with the training circuits.

Return type:

mit_val (float)

Reference:
  1. P. Czarnik, A. Arrasmith et al, Error mitigation with Clifford quantum-circuit data. arXiv:2005.10189 [quant-ph].

qibo.models.error_mitigation.sample_training_circuit_cdr(circuit, replacement_gates: list | None = None, sigma: float = 0.5, seed=None, backend=None)[source]#

Samples a training circuit for CDR by susbtituting some of the non-Clifford gates.

Parameters:
  • circuit (qibo.models.Circuit) – circuit to sample from, decomposed in RX(pi/2), X, CNOT and RZ gates.

  • replacement_gates (list, optional) – candidates for the substitution of the non-Clifford gates. The list should be composed by tuples of the form (gates.XYZ, kwargs). For example, phase gates are used by default: list((RZ, {'theta':0}), (RZ, {'theta':pi/2}), (RZ, {'theta':pi}), (RZ, {'theta':3*pi/2})).

  • sigma (float, optional) – standard devation of the Gaussian distribution used for sampling.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

The sampled circuit.

Return type:

qibo.models.Circuit

Variable Noise CDR (vnCDR)#

Variable Noise CDR (vnCDR) is an extension of the CDR method described above that factors in different noise levels as in ZNE. In detail, the set of circuits \(S_n=\{\mathbf{C}_i\}_{i=1,..,n}\) is still generated as in CDR, but for each \(\mathbf{C}_i\) we have \(k\) different versions of it with increased noise \(\mathbf{C}_i=C_i^0,C_i^1,...,C_i^{k-1}\).

Therefore, in this case we have a \(k\)-dimensional predictor variable \(\mathbf{a}_i^{noisy}=\big(a_i^0, a_i^1,..,a_i^{k-1}\big)^{noisy}\) for the same noise-free targets \(a_i^{exact}\), and we want to learn the mapping:

\[f:\mathbf{a}_i^{noisy}\rightarrow a_i^{exact}\]

via minimizing the same mean squared error:

\[E = \sum_{i=1}^n \bigg(a_i^{exact}-f(\mathbf{a}_i^{noisy})\bigg)^2\]

In particular, the default choice is to take \(f(\mathbf{x}):=\Gamma\cdot \mathbf{x}\;\), with \(\Gamma=\text{diag}(\gamma_0,\gamma_1,...,\gamma_{k-1})\;\), that corresponds to the ZNE calculation for the estimate of the expected value.

Here, as in the implementation of the CDR above, the circuit is supposed to be decomposed in the set of primitive gates \({RX(\frac{\pi}{2}),CNOT,X,RZ(\theta)}\). See Sopena et al for all the details.

qibo.models.error_mitigation.vnCDR(circuit, observable, noise_levels, noise_model, nshots: int = 10000, model=None, n_training_samples: int = 100, insertion_gate: str = 'CNOT', full_output: bool = False, readout=None, qubit_map=None, seed=None, backend=None)[source]#

Runs the variable-noise Clifford Data Regression error mitigation method.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit decomposed in the primitive gates X, CNOT, RX(pi/2), RZ(theta).

  • observable (qibo.hamiltonians.Hamiltonian/:class:`qibo.hamiltonians.SymbolicHamiltonian) – observable to be measured.

  • noise_levels (numpy.ndarray) – sequence of noise levels.

  • noise_model (qibo.noise.NoiseModel) – noise model used for simulating noisy computation.

  • nshots (int, optional) – number of shots. Defaults to \(10000\).

  • model (callable, optional) – model used for fitting. This should be a callable function object f(x, *params), taking as input the predictor variable and the parameters. Default is a simple linear model f(x,a,b) := a*x + b.

  • n_training_samples (int, optional) – number of training circuits to sample.

  • insertion_gate (str, optional) – gate to be used in the insertion. If "RX", the gate used is :math:RX(\pi / 2). Default is "CNOT".

  • full_output (bool, optional) – if True, this function returns additional information: val, optimal_params, train_val. Defaults to False.

  • readout (dict, optional) –

    a dictionary that may contain the following keys:

    • ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.

    • response_matrix: numpy.ndarray, used for applying a pre-computed response matrix for readout error mitigation.

    • ibu_iters: int, specifies the number of iterations for the iterative Bayesian unfolding method of readout error mitigation. If provided, the corresponding readout error mitigation method is used. Defaults to {}.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Mitigated expectation value of observable. val (list): Expectation value of observable with increased noise levels. optimal_params (list): Optimal values for params. train_val (dict): Contains the noise-free and noisy expectation values obtained with the training circuits.

Return type:

mit_val (float)

Reference:
  1. A. Lowe, MH. Gordon et al, Unified approach to data-driven quantum error mitigation. arXiv:2011.01157 [quant-ph].

Importance Clifford Sampling (ICS)#

In the Importance Clifford Sampling (ICS) method, a set of \(n\) circuits \(S_n=\{C_i\}_{i=1,..,n}\) that stabilizes a given Pauli observable is generated starting from the original circuit \(C_0\) by replacing all the non-Clifford gates with Clifford ones. Given an observable \(A\), all the circuits of \(S_n\) are both simulated to obtain the correspondent expected values of \(A\) in noise-free condition \(\{a_i^{exact}\}_{i=1,..,n}\), and run in noisy conditions to obtain the noisy expected values \(\{a_i^{noisy}\}_{i=1,..,n}\).

Finally, a theoretically inspired model \(f\) is learned using the training data.

The mitigated expected value of \(A\) at the end of \(C_0\) is then obtained simply with \(f(a_0^{noisy})\).

In this implementation the initial circuit is expected to be decomposed in the three Clifford gates \(RX(\frac{\pi}{2})\), \(CNOT\), \(X\) and in \(RZ(\theta)\) (which is Clifford only for \(\theta=\frac{n\pi}{2}\)). By default the set of Clifford gates used for substitution is \(\{RZ(0),RZ(\frac{\pi}{2}),RZ(\pi),RZ(\frac{3}{2}\pi)\}\). See Sopena et al for more details.

qibo.models.error_mitigation.ICS(circuit, observable, readout=None, qubit_map=None, noise_model=None, nshots=10000, n_training_samples=10, full_output=False, seed=None, backend=None)[source]#

Computes the Important Clifford Sampling method.

Parameters:
  • circuit (qibo.models.Circuit) – input circuit.

  • observable (qibo.hamiltonians.Hamiltonian/:class:`qibo.hamiltonians.SymbolicHamiltonian) – the observable to be measured.

  • readout (dict, optional) –

    a dictionary that may contain the following keys:

    • ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.

    • response_matrix: numpy.ndarray, used for applying a pre-computed response matrix for readout error mitigation.

    • ibu_iters: int, specifies the number of iterations for the iterative Bayesian unfolding method of readout error mitigation. If provided, the corresponding readout error mitigation method is used. Defaults to {}.

  • qubit_map (list, optional) – the qubit map. If None, a list of range of circuit’s qubits is used. Defaults to None.

  • noise_model (qibo.models.noise.Noise, optional) – the noise model to be applied. Defaults to None.

  • nshots (int, optional) – the number of shots for the circuit execution. Defaults to \(10000\).

  • n_training_samples (int, optional) – the number of training samples. Defaults to 10.

  • full_output (bool, optional) – if True, this function returns additional information: val, optimal_params, train_val. Defaults to False.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – the backend to be used in the execution. If None, it uses the global backend. Defaults to None.

Returns:

the mitigated expectated value. mitigated_expectation_std (float): the standard deviation of the mitigated expectated value. dep_param (float): the depolarizing parameter. dep_param_std (float): the standard deviation of the depolarizing parameter. lambda_list (list): the list of the depolarizing parameters. data (dict): the data dictionary containing the noise-free and noisy expectation values obtained with the training circuits.

Return type:

mitigated_expectation (float)

Reference:
  1. Dayue Qin, Yanzhu Chen et al, Error statistics and scalability of quantum error mitigation formulas. arXiv:2112.06255 [quant-ph].

qibo.models.error_mitigation.sample_clifford_training_circuit(circuit, seed=None, backend=None)[source]#

Samples a training circuit for CDR by susbtituting all the non-Clifford gates.

Parameters:
  • circuit (qibo.models.Circuit) – circuit to sample from.

  • seed (int or numpy.random.Generator, optional) – Either a generator of random numbers or a fixed seed to initialize a generator. If None, initializes a generator with a random seed. Default: None.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

the sampled circuit.

Return type:

qibo.models.Circuit


Gates#

All supported gates can be accessed from the qibo.gates module. Read below for a complete list of supported gates.

All gates support the controlled_by method that allows to control the gate on an arbitrary number of qubits. For example

  • gates.X(0).controlled_by(1, 2) is equivalent to gates.TOFFOLI(1, 2, 0),

  • gates.RY(0, np.pi).controlled_by(1, 2, 3) applies the Y-rotation to qubit 0 when qubits 1, 2 and 3 are in the |111> state.

  • gates.SWAP(0, 1).controlled_by(3, 4) swaps qubits 0 and 1 when qubits 3 and 4 are in the |11> state.

Abstract gate#

class qibo.gates.abstract.Gate[source]#

The base class for gate implementation.

All base gates should inherit this class.

name#

Name of the gate.

Type:

str

draw_label#

Optional label for drawing the gate in a circuit with qibo.models.Circuit.draw().

Type:

str

is_controlled_by#

True if the gate was created using the qibo.gates.abstract.Gate.controlled_by() method, otherwise False.

Type:

bool

init_args#

Arguments used to initialize the gate.

Type:

list

init_kwargs#

Arguments used to initialize the gate.

Type:

dict

target_qubits#

Tuple with ids of target qubits.

Type:

tuple

control_qubits#

Tuple with ids of control qubits sorted in increasing order.

Type:

tuple

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property raw: dict#

Serialize to dictionary.

The values used in the serialization should be compatible with a JSON dump (or any other one supporting a minimal set of scalar types). Though the specific implementation is up to the specific gate.

static from_dict(raw: dict)[source]#

Load from serialization.

Essentially the counter-part of raw().

to_json()[source]#

Dump gate to JSON.

Note

Consider using raw() directly.

property qubits: Tuple[int, ...]#

Tuple with ids of all qubits (control and target) that the gate acts.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

property target_qubits: Tuple[int, ...]#

Tuple with ids of target qubits.

property control_qubits: Tuple[int, ...]#

Tuple with ids of control qubits sorted in increasing order.

property parameters#

Returns a tuple containing the current value of gate’s parameters.

commutes(gate: Gate) bool[source]#

Checks if two gates commute.

Parameters:

gate – Gate to check if it commutes with the current gate.

Returns:

True if the gates commute, False otherwise.

Return type:

bool

on_qubits(qubit_map) Gate[source]#

Creates the same gate targeting different qubits.

Parameters:

qubit_map (int) – Dictionary mapping original qubit indices to new ones.

Returns:

A qibo.gates.Gate object of the original gate type targeting the given qubits.

Example

from qibo import Circuit, gates
circuit = Circuit(4)
# Add some CNOT gates
circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1)
circuit.draw()
0: ───X─────
1: ───|─o─X─
2: ─o─|─|─o─
3: ─X─o─X───
dagger() Gate[source]#

Returns the dagger (conjugate transpose) of the gate.

Note that dagger is not persistent for parametrized gates. For example, applying a dagger to an qibo.gates.gates.RX gate will change the sign of its parameter at the time of application. However, if the parameter is updated after that, for example using qibo.models.circuit.Circuit.set_parameters(), then the action of dagger will be lost.

Returns:

object representing the dagger of the original gate.

Return type:

qibo.gates.Gate

decompose(*free) List[Gate][source]#

Decomposes multi-control gates to gates supported by OpenQASM.

Decompositions are based on arXiv:9503016.

Parameters:

free – Ids of free qubits to use for the gate decomposition.

Returns:

gates that have the same effect as applying the original gate.

Return type:

list

matrix(backend=None)[source]#

Returns the matrix representation of the gate.

If gate has controlled qubits inserted by qibo.gates.Gate.controlled_by(), then qibo.gates.Gate.matrix() returns the matrix of the original gate.

from qibo import gates

gate = gates.SWAP(3, 4).controlled_by(0, 1, 2)
print(gate.matrix())

To return the full matrix that takes the control qubits into account, one should use qibo.models.Circuit.unitary(), e.g.

from qibo import Circuit, gates

nqubits = 5
circuit = Circuit(nqubits)
circuit.add(gates.SWAP(3, 4).controlled_by(0, 1, 2))
print(circuit.unitary())
Parameters:

backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Matrix representation of gate.

Return type:

ndarray

Note

Gate.matrix was defined as an atribute in qibo versions prior to 0.2.0. From 0.2.0 on, it has been converted into a method and has replaced the asmatrix method.

generator_eigenvalue()[source]#

This function returns the eigenvalues of the gate’s generator.

Returns:

eigenvalue of the generator.

Return type:

float

basis_rotation()[source]#

Transformation required to rotate the basis for measuring the gate.

Single qubit gates#

Hadamard (H)#

class qibo.gates.H(q)[source]#

The Hadamard gate.

Corresponds to the following unitary matrix

\[\begin{split}\frac{1}{\sqrt{2}} \, \begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Pauli X (X)#

class qibo.gates.X(q)[source]#

The Pauli-\(X\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose(*free, use_toffolis=True)[source]#

Decomposes multi-control X gate to one-qubit, CNOT and TOFFOLI gates.

Parameters:
  • free – Ids of free qubits to use for the gate decomposition.

  • use_toffolis – If True the decomposition contains only TOFFOLI gates. If False a congruent representation is used for TOFFOLI gates. See qibo.gates.TOFFOLI for more details on this representation.

Returns:

List with one-qubit, CNOT and TOFFOLI gates that have the same effect as applying the original multi-control gate.

basis_rotation()[source]#

Transformation required to rotate the basis for measuring the gate.

Pauli Y (Y)#

class qibo.gates.Y(q)[source]#

The Pauli-\(Y\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 0 & -i \\ i & 0 \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

basis_rotation()[source]#

Transformation required to rotate the basis for measuring the gate.

Pauli Z (Z)#

class qibo.gates.Z(q)[source]#

The Pauli-\(Z\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 \\ 0 & -1 \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

basis_rotation()[source]#

Transformation required to rotate the basis for measuring the gate.

Square-root of Pauli X (SX)#

class qibo.gates.SX(q)[source]#

The \(\sqrt{X}\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\frac{1}{2} \, \begin{pmatrix} 1 + i & 1 - i \\ 1 - i & 1 + i \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose()[source]#

Decomposition of \(\sqrt{X}\) up to global phase.

A global phase difference exists between the definitions of \(\sqrt{X}\) and \(\text{RX}(\pi / 2)\), with \(\text{RX}\) being the qibo.gates.RX gate. More precisely, \(\sqrt{X} = e^{i \pi / 4} \, \text{RX}(\pi / 2)\).

S gate (S)#

class qibo.gates.S(q)[source]#

The \(S\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 \\ 0 & i \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

T gate (T)#

class qibo.gates.T(q)[source]#

The T gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 \\ 0 & e^{i \pi / 4} \\ \end{pmatrix}\end{split}\]
Parameters:

q (int) – the qubit id number.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Identity (I)#

class qibo.gates.I(*q)[source]#

The identity gate.

Parameters:

*q (int) – the qubit id numbers.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Align (A)#

class qibo.gates.Align(q, delay=0, trainable=False)[source]#

Aligns proceeding qubit operations and (optionally) waits delay amount of time.

Note

For this gate, the trainable parameter is by default set to False.

Parameters:
  • q (int) – The qubit ID.

  • delay (int, optional) – The time (in ns) for which to delay circuit execution on the specified qubits. Defaults to 0 (zero).

Measurement (M)#

class qibo.gates.M(*q, register_name: str | None = None, collapse: bool = False, basis: ~qibo.gates.abstract.Gate | str = <class 'qibo.gates.gates.Z'>, p0: ProbsType | None = None, p1: ProbsType | None = None)[source]#

The measure gate.

Parameters:
  • *q (int) – id numbers of the qubits to measure. It is possible to measure multiple qubits using gates.M(0, 1, 2, ...). If the qubits to measure are held in an iterable (eg. list) the * operator can be used, for example gates.M(*[0, 1, 4]) or gates.M(*range(5)).

  • register_name (str, optional) – Optional name of the register to distinguish it from other registers when used in circuits.

  • collapse (bool) – Collapse the state vector after the measurement is performed. Can be used only for single shot measurements. If True the collapsed state vector is returned. If False the measurement result is returned.

  • basis (qibo.gates.Gate or str or list, optional) – Basis to measure. Can be either: - a qibo gate - the string representing the gate - a callable that accepts a qubit, for example: lambda q: gates.RX(q, 0.2) - a list of the above, if a different basis will be used for each measurement qubit. Defaults is to qibo.gates.Z.

  • p0 (dict, optional) – bitflip probability map. Can be: A dictionary that maps each measured qubit to the probability that it is flipped, a list or tuple that has the same length as the tuple of measured qubits or a single float number. If a single float is given the same probability will be used for all qubits.

  • p1 (dict, optional) – bitflip probability map for asymmetric bitflips. Same as p0 but controls the 1->0 bitflip probability. If p1 is None then p0 will be used both for 0->1 and 1->0 bitflips.

property raw: dict#

Serialize to dictionary.

The values used in the serialization should be compatible with a JSON dump (or any other one supporting a minimal set of scalar types). Though the specific implementation is up to the specific gate.

add(gate)[source]#

Adds target qubits to a measurement gate.

This method is only used for creating the global measurement gate used by the qibo.models.Circuit. The user is not supposed to use this method and a ValueError is raised if he does so.

Parameters:

gate – Measurement gate to add its qubits in the current gate.

classmethod load(payload)[source]#

Constructs a measurement gate starting from a json serialized one.

on_qubits(qubit_map) Gate[source]#

Creates the same measurement gate targeting different qubits and preserving the measurement result register.

Parameters:

qubit_map (dict) – dictionary mapping original qubit indices to new ones.

Returns:

object of the original gate type targeting the given qubits.

Return type:

qibo.gates.Gate.M

Example

from qibo import Circuit, gates

measurement = gates.M(0, 1)

circuit = Circuit(3)
circuit.add(measurement.on_qubits({0: 0, 1: 2}))
assert circuit.queue[0].result is measurement.result
circuit.draw()
0: ─M─
1: ─|─
2: ─M─

Rotation X-axis (RX)#

class qibo.gates.RX(q, theta, trainable=True)[source]#

Rotation around the X-axis of the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\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:
  • q (int) – the qubit id number.

  • theta (float) – the rotation angle.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

generator_eigenvalue()[source]#

This function returns the eigenvalues of the gate’s generator.

Returns:

eigenvalue of the generator.

Return type:

float

Rotation Y-axis (RY)#

class qibo.gates.RY(q, theta, trainable=True)[source]#

Rotation around the Y-axis of the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} \cos \frac{\theta }{2} & -\sin \frac{\theta }{2} \\ \sin \frac{\theta }{2} & \cos \frac{\theta }{2} \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

generator_eigenvalue()[source]#

This function returns the eigenvalues of the gate’s generator.

Returns:

eigenvalue of the generator.

Return type:

float

Rotation Z-axis (RZ)#

class qibo.gates.RZ(q, theta, trainable=True)[source]#

Rotation around the Z-axis of the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} e^{-i \theta / 2} & 0 \\ 0 & e^{i \theta / 2} \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

generator_eigenvalue()[source]#

This function returns the eigenvalues of the gate’s generator.

Returns:

eigenvalue of the generator.

Return type:

float

First general unitary (U1)#

class qibo.gates.U1(q, theta, trainable=True)[source]#

First general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 \\ 0 & e^{i \theta} \\ \end{pmatrix}\end{split}\]
Parameters:
  • q (int) – the qubit id number.

  • theta (float) – the rotation angle.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Second general unitary (U2)#

class qibo.gates.U2(q, phi, lam, trainable=True)[source]#

Second general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\frac{1}{\sqrt{2}} \begin{pmatrix} e^{-i(\phi + \lambda )/2} & -e^{-i(\phi - \lambda )/2} \\ e^{i(\phi - \lambda )/2} & e^{i (\phi + \lambda )/2} \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Third general unitary (U3)#

class qibo.gates.U3(q, theta, phi, lam, trainable=True)[source]#

Third general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} e^{-i(\phi + \lambda )/2}\cos\left (\frac{\theta }{2}\right ) & -e^{-i(\phi - \lambda )/2}\sin\left (\frac{\theta }{2}\right ) \\ e^{i(\phi - \lambda )/2}\sin\left (\frac{\theta }{2}\right ) & e^{i (\phi + \lambda )/2}\cos\left (\frac{\theta }{2}\right ) \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose() List[Gate][source]#

Decomposition of \(U_{3}\) up to global phase.

A global phase difference exists between the definitions of \(U3\) and this decomposition. More precisely,

\[U_{3}(\theta, \phi, \lambda) = e^{i \, \frac{3 \pi}{2}} \, \text{RZ}(\phi + \pi) \, \sqrt{X} \, \text{RZ}(\theta + \pi) \, \sqrt{X} \, \text{RZ}(\lambda) \, ,\]

where \(\text{RZ}\) and \(\sqrt{X}\) are, respectively, qibo.gates.RZ and :class`qibo.gates.SX`.

Two qubit gates#

Controlled-NOT (CNOT)#

class qibo.gates.CNOT(q0, q1)[source]#

The Controlled-NOT gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the control qubit id number.

  • q1 (int) – the target qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposes multi-control gates to gates supported by OpenQASM.

Decompositions are based on arXiv:9503016.

Parameters:

free – Ids of free qubits to use for the gate decomposition.

Returns:

gates that have the same effect as applying the original gate.

Return type:

list

Controlled-Y (CY)#

class qibo.gates.CY(q0, q1)[source]#

The Controlled-\(Y\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & -i \\ 0 & 0 & i & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the control qubit id number.

  • q1 (int) – the target qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose() List[Gate][source]#

Decomposition of \(\text{CY}\) gate.

Decompose \(\text{CY}\) gate into qibo.gates.SDG in the target qubit, followed by qibo.gates.CNOT, followed by a qibo.gates.S in the target qubit.

Controlled-phase (CZ)#

class qibo.gates.CZ(q0, q1)[source]#

The Controlled-Phase gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the control qubit id number.

  • q1 (int) – the target qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose() List[Gate][source]#

Decomposition of \(\text{CZ}\) gate.

Decompose \(\text{CZ}\) gate into qibo.gates.H in the target qubit, followed by qibo.gates.CNOT, followed by another qibo.gates.H in the target qubit

Controlled-Square Root of X (CSX)#

class qibo.gates.CSX(q0, q1)[source]#

The Controlled-\(\sqrt{X}\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{i\pi/4} & e^{-i\pi/4} \\ 0 & 0 & e^{-i\pi/4} & e^{i\pi/4} \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the control qubit id number.

  • q1 (int) – the target qubit id number.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Controlled-rotation X-axis (CRX)#

class qibo.gates.CRX(q0, q1, theta, trainable=True)[source]#

Controlled rotation around the X-axis for the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\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:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Controlled-rotation Y-axis (CRY)#

class qibo.gates.CRY(q0, q1, theta, trainable=True)[source]#

Controlled rotation around the Y-axis for the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\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}\]

Note that this differs from the qibo.gates.RZ gate.

Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Controlled-rotation Z-axis (CRZ)#

class qibo.gates.CRZ(q0, q1, theta, trainable=True)[source]#

Controlled rotation around the Z-axis for the Bloch sphere.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{-i \theta / 2} & 0 \\ 0 & 0 & 0 & e^{i \theta / 2} \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Controlled first general unitary (CU1)#

class qibo.gates.CU1(q0, q1, theta, trainable=True)[source]#

Controlled first general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i \theta } \\ \end{pmatrix}\end{split}\]

Note that this differs from the qibo.gates.CRZ gate.

Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Controlled second general unitary (CU2)#

class qibo.gates.CU2(q0, q1, phi, lam, trainable=True)[source]#

Controlled second general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{-i(\phi + \lambda )/2} & -e^{-i(\phi - \lambda )/2} \\ 0 & 0 & e^{i(\phi - \lambda )/2} & e^{i (\phi + \lambda )/2} \\ \end{pmatrix}\end{split}\]
Parameters:

Controlled third general unitary (CU3)#

class qibo.gates.CU3(q0, q1, theta, phi, lam, trainable=True)[source]#

Controlled third general unitary gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{-i(\phi + \lambda )/2}\cos\left (\frac{\theta }{2}\right ) & -e^{-i(\phi - \lambda )/2}\sin\left (\frac{\theta }{2}\right ) \\ 0 & 0 & e^{i(\phi - \lambda )/2}\sin\left (\frac{\theta }{2}\right ) & e^{i (\phi + \lambda )/2}\cos\left (\frac{\theta }{2}\right ) \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Swap (SWAP)#

class qibo.gates.SWAP(q0, q1)[source]#

The swap gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

iSwap (iSWAP)#

class qibo.gates.iSWAP(q0, q1)[source]#

The iSWAP gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Square root of iSwap (SiSWAP)#

class qibo.gates.SiSWAP(q0, q1)[source]#

The \(\sqrt{\text{iSWAP}}\) gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1/\sqrt{2} & i/\sqrt{2} & 0 \\ 0 & i/\sqrt{2} & 1/\sqrt{2} & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

f-Swap (FSWAP)#

class qibo.gates.FSWAP(q0, q1)[source]#

The fermionic swap gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & -1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be f-swapped id number.

  • q1 (int) – the second qubit to be f-swapped id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

fSim#

class qibo.gates.fSim(q0, q1, theta, phi, trainable=True)[source]#

The fSim gate defined in arXiv:2001.08343.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta & -i\sin \theta & 0 \\ 0 & -i\sin \theta & \cos \theta & 0 \\ 0 & 0 & 0 & e^{-i \phi } \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

  • theta (float) – Angle for the one-qubit rotation.

  • phi (float) – Angle for the |11> phase.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.Circuit.set_parameters(). Defaults to True.

Sycamore gate#

class qibo.gates.SYC(q0, q1)[source]#

The Sycamore gate, defined in the Supplementary Information of Quantum supremacy using a programmable superconducting processor.

Corresponding to the following unitary matrix

\[\begin{split}\text{fSim}(\pi / 2, \, \pi / 6) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -i & 0 \\ 0 & -i & 0 & 0 \\ 0 & 0 & 0 & e^{-i \pi / 6} \\ \end{pmatrix} \, ,\end{split}\]

where \(\text{fSim}\) is the qibo.gates.fSim gate.

Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

fSim with general rotation#

class qibo.gates.GeneralizedfSim(q0, q1, unitary, phi, trainable=True)[source]#

The fSim gate with a general rotation.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & R_{00} & R_{01} & 0 \\ 0 & R_{10} & R_{11} & 0 \\ 0 & 0 & 0 & e^{-i \phi } \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

  • unitary (np.ndarray) – Unitary that corresponds to the one-qubit rotation.

  • phi (float) – Angle for the |11> phase.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.Circuit.set_parameters(). Defaults to True.

property parameters#

Returns a tuple containing the current value of gate’s parameters.

Parametric XX interaction (RXX)#

class qibo.gates.RXX(q0, q1, theta, trainable=True)[source]#

Parametric 2-qubit XX interaction, or rotation about XX-axis.

Corresponds to the following unitary matrix

\[\begin{split}\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:
  • q0 (int) – the first entangled qubit id number.

  • q1 (int) – the second entangled qubit id number.

  • theta (float) – the rotation angle.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Parametric YY interaction (RYY)#

class qibo.gates.RYY(q0, q1, theta, trainable=True)[source]#

Parametric 2-qubit YY interaction, or rotation about YY-axis.

Corresponds to the following unitary matrix

\[\begin{split}\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:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Parametric ZZ interaction (RZZ)#

class qibo.gates.RZZ(q0, q1, theta, trainable=True)[source]#

Parametric 2-qubit ZZ interaction, or rotation about ZZ-axis.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} e^{-i \theta / 2} & 0 & 0 & 0 \\ 0 & e^{i \theta / 2} & 0 & 0 \\ 0 & 0 & e^{i \theta / 2} & 0 \\ 0 & 0 & 0 & e^{-i \theta / 2} \\ \end{pmatrix}\end{split}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

Parametric ZX interaction (RZX)#

class qibo.gates.RZX(q0, q1, theta, trainable=True)[source]#

Parametric 2-qubit ZX interaction, or rotation about ZX-axis.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} \text{RX}(\theta) & 0 \\ 0 & \text{RX}(-\theta) \\ \end{pmatrix} = \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}\]

where \(\text{RX}\) is the qibo.gates.RX gate.

Parameters:

Parametric XX-YY interaction (RXXYY)#

class qibo.gates.RXXYY(q0, q1, theta, trainable=True)[source]#

Parametric 2-qubit \(XX + YY\) interaction, or rotation about \(XX + YY\)-axis.

Corresponds to the following unitary matrix

\[\begin{split}\exp\left(-i \frac{\theta}{4}(XX + YY)\right) = \begin{pmatrix} 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}} & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \, ,\end{split}\]
Parameters:
decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposition of \(\text{R_{XX-YY}}\) up to global phase.

This decomposition has a global phase difference with respect to the original gate due to a phase difference in \(\left(\sqrt{X}\right)^{\dagger}\).

Givens gate#

class qibo.gates.GIVENS(q0, q1, theta, trainable=True)[source]#

The Givens gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\theta) & -\sin(\theta) & 0 \\ 0 & \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit id number.

  • q1 (int) – the second qubit id number.

  • theta (float) – the rotation angle.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposition of RBS gate according to ArXiv:2109.09685.

Reconfigurable Beam Splitter gate (RBS)#

class qibo.gates.RBS(q0, q1, theta, trainable=True)[source]#

The Reconfigurable Beam Splitter gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\theta) & \sin(\theta) & 0 \\ 0 & -\sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \\ \end{pmatrix}\end{split}\]

Note that, in our implementation, \(\text{RBS}(\theta) = \text{Givens}(-\theta)\), where \(\text{Givens}\) is the qibo.gates.GIVENS gate. However, we point out that this definition is not unique.

Parameters:
  • q0 (int) – the first qubit id number.

  • q1 (int) – the second qubit id number.

  • theta (float) – the rotation angle.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposition of RBS gate according to ArXiv:2109.09685.

Echo Cross-Resonance gate (ECR)#

class qibo.gates.ECR(q0, q1)[source]#

THe Echo Cross-Resonance gate.

Corresponds ot the following matrix

\[\begin{split}\frac{1}{\sqrt{2}} \left( X \, I - Y \, X \right) = \frac{1}{\sqrt{2}} \, \begin{pmatrix} 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit id number.

  • q1 (int) – the second qubit id number.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposition of \(\textup{ECR}\) gate up to global phase.

A global phase difference exists between the definitions of \(\textup{ECR}\) and this decomposition. More precisely,

\[\textup{ECR} = e^{i 7 \pi / 4} \, S(q_{0}) \, \sqrt{X}(q_{1}) \, \textup{CNOT}(q_{0}, q_{1}) \, X(q_{0})\]

Special gates#

Toffoli#

class qibo.gates.TOFFOLI(q0, q1, q2)[source]#

The Toffoli gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 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 & 1 & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first control qubit id number.

  • q1 (int) – the second control qubit id number.

  • q2 (int) – the target qubit id number.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose(*free, use_toffolis: bool = True) List[Gate][source]#

Decomposes multi-control gates to gates supported by OpenQASM.

Decompositions are based on arXiv:9503016.

Parameters:

free – Ids of free qubits to use for the gate decomposition.

Returns:

gates that have the same effect as applying the original gate.

Return type:

list

congruent(use_toffolis: bool = True) List[Gate][source]#

Congruent representation of TOFFOLI gate.

This is a helper method for the decomposition of multi-control X gates. The congruent representation is based on Sec. 6.2 of arXiv:9503016. The sequence of the gates produced here has the same effect as TOFFOLI with the phase of the |101> state reversed.

Parameters:

use_toffolis – If True a single TOFFOLI gate is returned. If False the congruent representation is returned.

Returns:

List with RY and CNOT gates that have the same effect as applying the original TOFFOLI gate.

CCZ#

class qibo.gates.CCZ(q0, q1, q2)[source]#

The controlled-CZ gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & -1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first control qubit id number.

  • q1 (int) – the second control qubit id number.

  • q2 (int) – the target qubit id number.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose() List[Gate][source]#

Decomposition of \(\text{CCZ}\) gate.

Decompose \(\text{CCZ}\) gate into qibo.gates.H in the target qubit, followed by qibo.gates.TOFFOLI, followed by a qibo.gates.H in the target qubit.

Deutsch#

class qibo.gates.DEUTSCH(q0, q1, q2, theta, trainable=True)[source]#

The Deutsch gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & i \cos{\theta} & \sin{\theta} \\ 0 & 0 & 0 & 0 & 0 & 0 & \sin{\theta} & i \cos{\theta} \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first control qubit id number.

  • q1 (int) – the second control qubit id number.

  • q2 (int) – the target qubit id number.

Generalized Reconfigurable Beam Splitter (RBS)#

class qibo.gates.GeneralizedRBS(qubits_in: Tuple[int] | List[int], qubits_out: Tuple[int] | List[int], theta: float, phi: float = 0.0, trainable: bool = True)[source]#

The generalized (complex) Reconfigurable Beam Splitter gate (\(\text{gRBS}\)).

Given a register called qubits_in containing \(m\) qubits and a register named qubits_out containing \(m'\) qubits, the \(\text{gRBS}\) is a \((m + m')\)-qubit gate that has the following matrix representation:

\[\begin{split}\begin{pmatrix} I & & & & \\ & e^{-i\phi}\cos\theta & & e^{-i\phi}\sin\theta & \\ & & I' & & \\ & -e^{i\phi}\sin\theta & & e^{i\phi}\cos\theta & \\ & & & & I\\ \end{pmatrix} \,\, ,\end{split}\]

where \(I\) and \(I'\) are, respectively, identity matrices of size \(2^{m} - 1\) and \(2^{m}(2^{m'} - 2)\).

This unitary matrix is also known as a Givens rotation.

References

1. R. M. S. Farias, T. O. Maciel, G. Camilo, R. Lin, S. Ramos-Calderer, and L. Aolita, Quantum encoder for fixed Hamming-weight subspaces. arXiv:2405.20408 [quant-ph]

Parameters:
  • qubits_in (tuple or list) – ids of “input” qubits.

  • qubits_out (tuple or list) – ids of “output” qubits.

  • theta (float) – the rotation angle.

  • phi (float) – the phase angle. Defaults to \(0.0\).

  • trainable (bool) – whether gate parameter can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

decompose() List[Gate][source]#

Decomposition of \(\text{gRBS}\) gate.

Decompose \(\text{gRBS}\) gate into qibo.gates.X, qibo.gates.CNOT, qibo.gates.RY, and qibo.gates.RZ.

Arbitrary unitary#

class qibo.gates.Unitary(unitary, *q, trainable=True, name: str | None = None, check_unitary: bool = True)[source]#

Arbitrary unitary gate.

Parameters:
  • unitary – Unitary matrix as a tensor supported by the backend.

  • *q (int) – Qubit id numbers that the gate acts on.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.Circuit.set_parameters(). Defaults to True.

  • name (str) – Optional name for the gate.

  • check_unitary (bool) – if True, checks if unitary is an unitary operator. If False, check is not performed and unitary attribute defaults to False. Note that, even when the check is performed, there is no enforcement. This allows the user to create non-unitary gates. Default is True.

property parameters#

Returns a tuple containing the current value of gate’s parameters.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

on_qubits(qubit_map)[source]#

Creates the same gate targeting different qubits.

Parameters:

qubit_map (int) – Dictionary mapping original qubit indices to new ones.

Returns:

A qibo.gates.Gate object of the original gate type targeting the given qubits.

Example

from qibo import Circuit, gates
circuit = Circuit(4)
# Add some CNOT gates
circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3)
circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1)
circuit.draw()
0: ───X─────
1: ───|─o─X─
2: ─o─|─|─o─
3: ─X─o─X───

Callback gate#

class qibo.gates.CallbackGate(callback: Callback)[source]#

Calculates a qibo.callbacks.Callback at a specific point in the circuit.

This gate performs the callback calulation without affecting the state vector.

Parameters:

callback (qibo.callbacks.Callback) – Callback object to calculate.

Fusion gate#

class qibo.gates.FusedGate(*q)[source]#

Collection of gates that will be fused and applied as single gate during simulation. This gate is constructed automatically by qibo.models.circuit.Circuit.fuse() and should not be used by user.

can_fuse(gate, max_qubits)[source]#

Check if two gates can be fused.

matrix(backend=None)[source]#

Returns matrix representation of special gate.

Parameters:

backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Matrix representation of special gate.

Return type:

ndarray

fuse(gate)[source]#

Fuses two gates.

IONQ Native gates#

GPI#

class qibo.gates.GPI(q, phi, trainable=True)[source]#

The GPI gate.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} 0 & e^{- i \phi} \\ e^{i \phi} & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q (int) – the qubit id number.

  • phi (float) – phase.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

GPI2#

class qibo.gates.GPI2(q, phi, trainable=True)[source]#

The GPI2 gate.

Corresponds to the following unitary matrix

\[\begin{split}\frac{1}{\sqrt{2}} \, \begin{pmatrix} 1 & -i e^{- i \phi} \\ -i e^{i \phi} & 1 \\ \end{pmatrix}\end{split}\]
Parameters:
  • q (int) – the qubit id number.

  • phi (float) – phase.

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.AbstractCircuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

property clifford#

Return boolean value representing if a Gate is Clifford or not.

Mølmer–Sørensen (MS)#

class qibo.gates.MS(q0, q1, phi0, phi1, theta: float = 1.5707963267948966, trainable=True)[source]#

The Mølmer–Sørensen (MS) gate is a two-qubit gate native to trapped ions.

Corresponds to the following unitary matrix

\[\begin{split}\begin{pmatrix} \cos(\theta / 2) & 0 & 0 & -i e^{-i( \phi_0 + \phi_1)} \sin(\theta / 2) \\ 0 & \cos(\theta / 2) & -i e^{-i( \phi_0 - \phi_1)} \sin(\theta / 2) & 0 \\ 0 & -i e^{i( \phi_0 - \phi_1)} \sin(\theta / 2) & \cos(\theta / 2) & 0 \\ -i e^{i( \phi_0 + \phi_1)} \sin(\theta / 2) & 0 & 0 & \cos(\theta / 2) \\ \end{pmatrix}\end{split}\]
Parameters:
  • q0 (int) – the first qubit to be swapped id number.

  • q1 (int) – the second qubit to be swapped id number.

  • phi0 (float) – first qubit’s phase.

  • phi1 (float) – second qubit’s phase

  • theta (float, optional) – arbitrary angle in the interval \(0 \leq \theta \leq \pi /2\). If \(\theta \rightarrow \pi / 2\), the fully-entangling MS gate is defined. Defaults to \(\pi / 2\).

  • trainable (bool) – whether gate parameters can be updated using qibo.models.circuit.Circuit.set_parameters(). Defaults to True.

property qasm_label#

String corresponding to OpenQASM operation of the gate.

Quantinuum native gates#

U1q#

class qibo.gates.U1q(q, theta, phi, trainable=True)[source]#

Native single-qubit gate in the Quantinuum platform.

Corresponds to the following unitary matrix:

\[\begin{split}\begin{pmatrix} \cos\left(\frac{\theta}{2}\right) & -i \, e^{-i \, \phi} \, \sin\left(\frac{\theta}{2}\right) \\ -i \, e^{i \, \phi} \, \sin\left(\frac{\theta}{2}\right) & \cos\left(\frac{\theta}{2}\right) \\ \end{pmatrix}\end{split}\]

Note that \(U_{1q}(\theta, \phi) = U_{3}(\theta, \phi - \frac{\pi}{2}, \frac{\pi}{2} - \phi)\), where \(U_{3}\) is qibo.gates.U3.

Parameters:

Note

The other Quantinuum single-qubit and two-qubit native gates are implemented in Qibo as:

IQM native gates#

Phase-\(RX\)#

class qibo.gates.PRX(q, theta, phi, trainable=True)[source]#

Phase \(RX\) gate.

Corresponds to the following unitary matrix

\[\begin{pmatrix} \cos{(\theta / 2)} & -i e^{-i \phi} \sin{(\theta / 2)} \ -i e^{i \phi} \sin{(\theta / 2)} & \cos{(\theta / 2)} \end{pmatrix}\]
Parameters:
property qasm_label#

String corresponding to OpenQASM operation of the gate.

decompose()[source]#

Decomposition of Phase-\(RX\) gate.

Note

The other IQM two-qubit native gate is implemented in Qibo as:


Channels#

Channels are implemented in Qibo as additional gates and can be accessed from the qibo.gates module. Channels can be used on density matrices to perform noisy simulations. Channels that inherit qibo.gates.UnitaryChannel can also be applied to state vectors using sampling and repeated execution. For more information on the use of channels to simulate noise we refer to How to perform noisy simulation? The following channels are currently implemented:

Kraus channel#

class qibo.gates.KrausChannel(qubits, operators)[source]#

General channel defined by arbitrary Kraus operators.

Implements the following transformation:

\[\mathcal{E}(\rho ) = \sum _k A_k \rho A_k^\dagger\]

where A are arbitrary Kraus operators given by the user. Note that Kraus operators set should be trace preserving, however this is not checked. Simulation of this gate requires the use of density matrices. For more information on channels and Kraus operators please check J. Preskill’s notes.

Parameters:
  • qubits (int or list or tuple) – Qubits that the Kraus operators act on. Type int and tuple will be considered as the same qubit ids for all operators. A list should contain tuples of qubits corresponding to each operator. Can be [] if operators are of type qibo.gates.Gate, otherwise adds given gates on specified qubits.

  • operators (list) – List of Kraus operators Ak as matrices of type ndarray | tf.Tensor or gates qibo.gates.Gate.

Example

import numpy as np

from qibo import Circuit, gates

# initialize circuit with 3 qubits
circuit = Circuit(3, density_matrix=True)
# define a sqrt(0.4) * X gate
a_1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]])
# define a sqrt(0.6) * CNOT gate
a_2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0],
                              [0, 0, 0, 1], [0, 0, 1, 0]])
# define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2}
channel_1 = gates.KrausChannel([(1,), (0, 2)], [a_1, a_2])
# add channel to the circuit
circuit.add(channel_1)

# define the same channel using qibo.gates.Unitary
a_1 = gates.Unitary(a_1, 1)
a_2 = gates.Unitary(a_2, 0, 2)
channel_2 = gates.KrausChannel([], [a_1, a_2])
# add channel to the circuit
circuit.add(channel_2)

# define the channel rho -> 0.4 X{0} rho X{0} + 0.6 CNOT{1, 2} rho CNOT{1, 2}
channel_3 = gates.KrausChannel([(0,), (1, 2)], [a_1, a_2])
# add channel to the circuit
circuit.add(channel_3)

Unitary channel#

class qibo.gates.UnitaryChannel(qubits, operators)[source]#

Channel that is a probabilistic sum of unitary operations.

Implements the following transformation:

\[\mathcal{E}(\rho ) = \left (1 - \sum _k p_k \right )\rho + \sum _k p_k U_k \rho U_k^\dagger\]

where U are arbitrary unitary operators and p are floats between 0 and 1. Note that unlike qibo.gates.KrausChannel which requires density matrices, it is possible to simulate the unitary channel using state vectors and probabilistic sampling. For more information on this approach we refer to Using repeated execution.

Parameters:
  • qubits (int or list or tuple) – Qubits that the unitary operators act on. Types int and tuple will be considered as the same qubit(s) for all unitaries. A list should contain tuples of qubits corresponding to each operator. Can be [] if operators are of type qibo.gates.Gate, otherwise adds given gates on specified qubits.

  • operators (list) – List of operators as pairs (pk, Uk) where pk is float probability corresponding to a unitary Uk of type ndarray/tf.Tensor or gates qibo.gates.Gate.

Pauli noise channel#

class qibo.gates.PauliNoiseChannel(qubits: Tuple[int, list, tuple], operators: list)[source]#

Multi-qubit noise channel that applies Pauli operators with given probabilities.

Implements the following transformation:

\[\mathcal{E}(\rho ) = \left (1 - \sum _{k} p_{k} \right ) \, \rho + \sum_{k} \, p_{k} \, P_{k} \, \rho \, P_{k}\]

where \(P_{k}\) is the \(k\)-th Pauli string and \(p_{k}\) is the probability associated to \(P_{k}\).

Example

from itertools import product

import numpy as np

from qibo.gates.channels import PauliNoiseChannel

qubits = (0, 2)
nqubits = len(qubits)

# excluding the Identity operator
paulis = list(product(["I", "X"], repeat=nqubits))[1:]
# this next line is optional
paulis = [''.join(pauli) for pauli in paulis]

probabilities = np.random.rand(len(paulis) + 1)
probabilities /= np.sum(probabilities)
#Excluding probability of Identity operator
probabilities = probabilities[1:]

channel = PauliNoiseChannel(
    qubits, list(zip(paulis, probabilities))
)

This channel can be simulated using either density matrices or state vectors and sampling with repeated execution. See How to perform noisy simulation? for more information.

Parameters:
  • qubits (int or list or tuple) – Qubits that the noise acts on.

  • operators (list) – list of operators as pairs \((P_{k}, p_{k})\).

Depolarizing channel#

class qibo.gates.DepolarizingChannel(qubits, lam: float)[source]#

\(n\)-qubit Depolarizing quantum error channel,

\[\mathcal{E}(\rho ) = (1 - \lambda) \rho + \lambda \text{Tr}_q[\rho]\otimes \frac{I}{2^n}\]

where \(\lambda\) is the depolarizing error parameter and \(0 \le \lambda \le 4^n / (4^n - 1)\).

  • If \(\lambda = 1\) this is a completely depolarizing channel \(E(\rho) = I / 2^n\)

  • If \(\lambda = 4^n / (4^n - 1)\) this is a uniform Pauli error channel: \(E(\rho) = \sum_j P_j \rho P_j / (4^n - 1)\) for all \(P_j \neq I\).

Parameters:
  • qubits (int or list or tuple) – Qubit ids that the noise acts on.

  • lam (float) – Depolarizing error parameter.

Thermal relaxation channel#

class qibo.gates.ThermalRelaxationChannel(qubit: int, parameters: list)[source]#

Single-qubit thermal relaxation error channel.

Implements the following transformation:

If \(T_1 \geq T_2\):

\[\mathcal{E} (\rho ) = (1 - p_z - p_0 - p_1) \rho + p_z \, Z\rho Z + \mathrm{Tr}_{q}[\rho] \otimes (p_0 | 0\rangle \langle 0| + p_1|1\rangle \langle 1|)\]

while if \(T_1 < T_2\):

\[\mathcal{E}(\rho ) = \mathrm{Tr}_\mathcal{X} \left[\Lambda_{\mathcal{X}\mathcal{Y}} (\rho_\mathcal{X}^T \otimes I_{\mathcal{Y}}) \right]\]

with

\[\begin{split}\Lambda = \begin{pmatrix} 1 - p_1 & 0 & 0 & e^{-t / T_2} \\ 0 & p_1 & 0 & 0 \\ 0 & 0 & p_0 & 0 \\ e^{-t / T_2} & 0 & 0 & 1 - p_0 \end{pmatrix}\end{split}\]

where \(p_0 = (1 - e^{-t / T_1})(1 - \eta )\), \(p_1 = (1 - e^{-t / T_1})\eta\), and \(p_z = (e^{-t / T_1} - e^{-t / T_2})/2\). Here \(\eta\) is the excited_population and \(t\) is the time, both controlled by the user. This gate is based on Qiskit’s thermal relaxation error channel.

Parameters:
  • qubit (int) – Qubit id that the noise channel acts on.

  • parameters (list) – list of 3 or 4 parameters (t_1, t_2, time, excited_population=0), where t_1 (float): T1 relaxation time. Should satisfy t_1 > 0. t_2 (float): T2 dephasing time. Should satisfy t_1 > 0 and t_2 < 2 * t_1. time (float): the gate time for relaxation error. excited_population (float): the population of the excited state at equilibrium. Default is 0.

Amplitude damping channel#

class qibo.gates.AmplitudeDampingChannel(qubit, gamma: float)[source]#

Single-qubit amplitude damping channel in its Kraus representation, i.e.

\[\begin{split}K_{0} = \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1 - \gamma} \\ \end{pmatrix} \,\, , \,\, K_{1} = \begin{pmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \\ \end{pmatrix}\end{split}\]
Parameters:
  • qubit (int) – Qubit id that the noise channel acts on.

  • gamma (float) – amplitude damping strength.

Phase damping channel#

class qibo.gates.PhaseDampingChannel(qubit, gamma: float)[source]#

Single-qubit phase damping channel in its Kraus representation, i.e.

\[\begin{split}K_{0} = \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1 - \gamma} \\ \end{pmatrix} \,\, , \,\, K_{1} = \begin{pmatrix} 0 & 0 \\ 0 & \sqrt{\gamma} \\ \end{pmatrix}\end{split}\]
Parameters:
  • qubit (int) – Qubit id that the noise channel acts on.

  • gamma (float) – phase damping strength.

Readout error channel#

class qibo.gates.ReadoutErrorChannel(qubits: Tuple[int, list, tuple], probabilities)[source]#

Readout error channel implemented as a quantum-to-classical channel.

Parameters:
  • qubits (int or list or tuple) – Qubit ids that the channel acts on.

  • probabilities (array) – row-stochastic matrix \(P\) with all readout transition probabilities.

Example

For 1 qubit, the transition matrix \(P\) would be

\[\begin{split}P = \begin{pmatrix} p(0 \, | \, 0) & p(1 \, | \, 0) \\ p(0 \, | \, 1) & p(1 \, | \, 1) \end{pmatrix} \, .\end{split}\]

Reset channel#

class qibo.gates.ResetChannel(qubit, probabilities)[source]#

Single-qubit reset channel.

Implements the following transformation:

\[\mathcal{E}(\rho ) = (1 - p_{0} - p_{1}) \rho + \mathrm{Tr}_{q}[\rho] \otimes (p_{0} \, |0\rangle \langle 0| + p_{1} \, |1\rangle \langle 1|),\]
Parameters:
  • qubit (int) – qubit id that the channel acts on.

  • probabilities (list or ndarray) – list \([p_{0}, p_{1}]\), where \(p_{0}\) and \(p_{1}\) are the probabilities to reset to 0 and 1, respectively.


Noise#

In Qibo it is possible to create a custom noise model using the class qibo.noise.NoiseModel. This enables the user to create circuits where the noise is gate and qubit dependent.

For more information on the use of qibo.noise.NoiseModel see How to perform noisy simulation?

class qibo.noise.NoiseModel[source]#

Class for the implementation of a custom noise model.

Example:

from qibo import Circuit, gates
from qibo.noise import NoiseModel, PauliError

# Build specific noise model with 2 quantum errors:
# - Pauli error on H only for qubit 1.
# - Pauli error on CNOT for all the qubits.

noise_model = NoiseModel()
noise_model.add(PauliError([("X", 0.5)]), gates.H, 1)
noise_model.add(PauliError([("Y", 0.5)]), gates.CNOT)

# Generate noiseless circuit.
circuit = Circuit(2)
circuit.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)])

# Apply noise to the circuit according to the noise model.
noisy_circuit = noise_model.apply(circuit)
add(error, gate: Gate | None = None, qubits: int | tuple | None = None, conditions=None)[source]#

Add a quantum error for a specific gate and qubit to the noise model.

Parameters:

Example:

import numpy as np
from qibo import Circuit, gates
from qibo.noise import NoiseModel, PauliError

# Check if a gate is RX(pi/2).
def is_sqrt_x(gate):
    return np.pi/2 in gate.parameters

# Build a noise model with a Pauli error on RX(pi/2) gates.
error = PauliError(list(zip(["X", "Y", "Z"], [0.01, 0.5, 0.1])))
noise = NoiseModel()
noise.add(PauliError([("X", 0.5)]), gates.RX, conditions=is_sqrt_x)

# Generate a noiseless circuit.
circuit = Circuit(1)
circuit.add(gates.RX(0, np.pi / 2))
circuit.add(gates.RX(0, 3 * np.pi / 2))
circuit.add(gates.X(0))

# Apply noise to the circuit.
noisy_circuit = noise.apply(circuit)
apply(circuit)[source]#

Generate a noisy quantum circuit according to the noise model built.

Parameters:

circuit (qibo.models.circuit.Circuit) – quantum circuit

Returns:

initial circuit with noise gates

added according to the noise model.

Return type:

qibo.models.circuit.Circuit

Quantum errors#

The quantum errors available to build a noise model are the following:

class qibo.noise.KrausError(ops)[source]#

Quantum error associated with the qibo.gates.KrausChannel.

Parameters:

ops (list) – List of Kraus operators of type np.ndarray or tf.Tensor and of the same shape.

channel(qubits, options)[source]#

Returns the quantum channel associated to the quantum error.

class qibo.noise.UnitaryError(probabilities, unitaries)[source]#

Quantum error associated with the qibo.gates.UnitaryChannel.

Parameters:
  • probabilities (list) – List of floats that correspond to the probability that each unitary Uk is applied.

  • unitaries (list) – List of unitary matrices as np.ndarray/tf.Tensor of the same shape. Must have the same length as the given probabilities p.

channel(qubits, options)[source]#

Returns the quantum channel associated to the quantum error.

class qibo.noise.PauliError(operators)[source]#

Quantum error associated with the qibo.gates.PauliNoiseChannel.

Parameters:

operators (list) – see qibo.gates.PauliNoiseChannel

channel(qubits, options)[source]#

Returns the quantum channel associated to the quantum error.

class qibo.noise.DepolarizingError(lam)[source]#

Quantum error associated with the qibo.gates.DepolarizingChannel.

Parameters:

options (float) – see qibo.gates.DepolarizingChannel

class qibo.noise.ThermalRelaxationError(t1, t2, time, excited_population=0)[source]#

Quantum error associated with the qibo.gates.ThermalRelaxationChannel.

Parameters:

options (tuple) – see qibo.gates.ThermalRelaxationChannel

class qibo.noise.AmplitudeDampingError(gamma)[source]#

Quantum error associated with the qibo.gates.AmplitudeDampingChannel.

Parameters:

options (float) – see qibo.gates.AmplitudeDampingChannel

class qibo.noise.PhaseDampingError(gamma)[source]#

Quantum error associated with the qibo.gates.PhaseDampingChannel.

Parameters:

options (float) – see qibo.gates.PhaseDampingChannel

class qibo.noise.ReadoutError(probabilities)[source]#

Quantum error associated with :class:’qibo.gates;ReadoutErrorChannel’.

Parameters:

options (array) – see :class:’qibo.gates.ReadoutErrorChannel’

class qibo.noise.ResetError(p0, p1)[source]#

Quantum error associated with the qibo.gates.ResetChannel.

Parameters:

options (tuple) – see qibo.gates.ResetChannel

class qibo.noise.CustomError(channel)[source]#

Quantum error associated with the qibo.gates.Channel

Parameters:

channel (qibo.gates.Channel) – any channel

Example:

import numpy as np
from qibo.gates import KrausChannel
from qibo.noise import CustomError

# define |0><0|
a1 = np.array([[1, 0], [0, 0]])
# define |0><1|
a2 = np.array([[0, 1], [0, 0]])

# Create an Error associated with Kraus Channel
# rho -> |0><0| rho |0><0| + |0><1| rho |0><1|
error = CustomError(gates.KrausChannel((0,), [a1, a2]))

IBMQ noise model#

In Qibo, it is possible to build noisy circuits based on IBMQ’s reported noise model of for its quantum computer by using the qibo.noise.IBMQNoiseModel class. The noise model is built using a combination of the qibo.gates.ThermalRelaxationChannel and qibo.gates.DepolarizingChannel channels. . At the end of the circuit, if the qubit is measured, bitflips errors are set. Moreover, the model handles idle qubits by applying a thermal relaxation channel for the duration of the idle-time.

For more information on the qibo.noise.IBMQNoiseModel class, see the example on Simulating quantum hardware.

class qibo.noise.IBMQNoiseModel[source]#

Class for the implementation of a IBMQ noise model.

This noise model applies a qibo.gates.DepolarizingChannel followed by a qibo.gates.ThermalRelaxationChannel after each one- or two-qubit gate in the circuit. It also applies single-qubit qibo.gates.ReadoutErrorChannel before every measurement gate.

Example:

from qibo import Circuit, gates
from qibo.models.encodings import phase_encoder
from qibo.noise import DepolarizingError, ThermalRelaxationError, ReadoutError
from qibo.noise import IBMQNoiseModel, NoiseModel

nqubits = 4

# creating circuit
phases = list(range(nqubits))
circuit = phase_encoder(phases, rotation="RY")
circuit.add(gates.CNOT(qubit, qubit + 1) for qubit in range(nqubits - 1))
circuit.add(gates.M(qubit) for qubit in range(1, nqubits - 1))

# creating noise model from dictionary
parameters = {
    "depolarizing_one_qubit" : {"0": 0.1, "2": 0.04, "3": 0.15},
    "depolarizing_two_qubit": {"0-1": 0.2},
    "t1" : {"0": 0.1, "1": 0.2, "3": 0.01},
    "t2" : {"0": 0.01, "1": 0.02, "3": 0.0001},
    "gate_times" : (0.1, 0.2),
    "excited_population" : 0.1,
    "readout_one_qubit" : {"0": (0.1, 0.1), "1": 0.1, "3": [0.1, 0.1]},
    }

noise_model = IBMQNoiseModel()
noise_model.from_dict(parameters)
noisy_circuit = noise_model.apply(circuit)
from_dict(parameters: dict)[source]#

Method used to pass noise parameters as inside dictionary.

Parameters:

parameters (dict) –

Contains parameters necessary to initialise qibo.noise.DepolarizingError, qibo.noise.ThermalRelaxationError, and qibo.noise.ReadoutError.

The keys and values of the dictionary parameters are defined below:

  • "depolarizing_one_qubit" (int or float or dict): If int or

    float, all qubits share the same single-qubit depolarizing parameter. If dict, expects qubit indexes as keys and their respective depolarizing parameter as values. See qibo.gates.channels.DepolarizingChannel for a detailed definition of depolarizing parameter.

  • "depolarizing_two_qubit" (int or float or dict): If int or

    float, all two-qubit gates share the same two-qubit depolarizing parameter regardless in which pair of qubits the two-qubit gate is acting on. If dict, expects pair qubit indexes as keys separated by a hiphen (e.g. “0-1” for gate that has “0” as control and “1” as target) and their respective depolarizing parameter as values. See qibo.gates.channels.DepolarizingChannel for a detailed definition of depolarizing parameter.

  • "t1" (int or float or dict): If int or float, all qubits

    share the same t1. If dict, expects qubit indexes as keys and its respective t1 as values. See qibo.gates.channels.ThermalRelaxationChannel for a detailed definition of t1. Note that t1 and t2 must be passed with the same type.

  • "t2" (int or float or dict): If int or float, all qubits share

    the same t2. If dict, expects qubit indexes as keys and its respective t2 as values. See qibo.gates.channels.ThermalRelaxationChannel for a detailed definition of t2. Note that t2 and t1 must be passed with the same type.

  • "gate_times" (tuple or list): pair of gate times representing

    gate times for ThermalRelaxationError following, respectively, one- and two-qubit gates.

  • "excited_population" (int or float): See

    ThermalRelaxationChannel.

  • "readout_one_qubit" (int or float or dict): If int or float,

    \(p(0|1) = p(1|0)\), and all qubits share the same readout error probabilities. If dict, expects qubit indexes as keys and values as tuple (or list) in the format \((p(0|1),\,p(1|0))\). If values are tuple or list of length 1 or float or int, then it is assumed that \(p(0|1) = p(1|0)\).


Hamiltonians#

The main abstract Hamiltonian object of Qibo is:

class qibo.hamiltonians.abstract.AbstractHamiltonian[source]#

Qibo abstraction for Hamiltonian objects.

abstract eigenvalues(k=6)[source]#

Computes the eigenvalues for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigvalsh() for more details.

abstract eigenvectors(k=6)[source]#

Computes a tensor with the eigenvectors for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigh() for more details.

ground_state()[source]#

Computes the ground state of the Hamiltonian.

Uses qibo.hamiltonians.AbstractHamiltonian.eigenvectors() and returns eigenvector corresponding to the lowest energy.

abstract exp(a)[source]#

Computes a tensor corresponding to \(\exp(-i \, a \, H)\).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

abstract expectation(state, normalize=False)[source]#

Computes the real expectation value for a given state.

Parameters:
  • state (ndarray) – state in which to calculate the expectation value.

  • normalize (bool, optional) – If True, the expectation value \(\ell_{2}\)-normalized. Defaults to False.

Returns:

real number corresponding to the expectation value.

Return type:

float

abstract expectation_from_samples(freq, qubit_map=None)[source]#

Computes the expectation value of a diagonal observable, given computational-basis measurement frequencies.

Parameters:
  • freq (collections.Counter) – the keys are the observed values in binary form and the values the corresponding frequencies, that is the number of times each measured value/bitstring appears.

  • qubit_map (tuple) – Mapping between frequencies and qubits. If None, then defaults to \([1, \, 2, \, \cdots, \, \mathrm{len}(\mathrm{key})]\). Defaults to None.

Returns:

real number corresponding to the expectation value.

Return type:

float

Matrix Hamiltonian#

The first implementation of Hamiltonians uses the full matrix representation of the Hamiltonian operator in the computational basis. For \(n\) qubits, this matrix has size \(2^{n} \times 2^{n}\). Therefore, its construction is feasible only when \(n\) is small.

Alternatively, the user can construct this Hamiltonian using a sparse matrices. Sparse matrices from the scipy.sparse module are supported by the numpy and qibojit backends while the tensorflow.sparse can be used for tensorflow. Scipy sparse matrices support algebraic operations (addition, subtraction, scalar multiplication), linear algebra operations (eigenvalues, eigenvectors, matrix exponentiation) and multiplication to dense or other sparse matrices. All these properties are inherited by qibo.hamiltonians.Hamiltonian objects created using sparse matrices. Tensorflow sparse matrices support only multiplication to dense matrices. Both backends support calculating Hamiltonian expectation values using a sparse Hamiltonian matrix.

class qibo.hamiltonians.Hamiltonian(nqubits, matrix, backend=None)[source]#

Hamiltonian based on a dense or sparse matrix representation.

Parameters:
  • nqubits (int) – number of quantum bits.

  • matrix (ndarray) – Matrix representation of the Hamiltonian in the computational basis as an array of shape \(2^{n} \times 2^{n}\). Sparse matrices based on scipy.sparse for numpy / qibojit backends (or on tf.sparse for the tensorflow backend) are also supported.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

property matrix#

Returns the full matrix representation.

For \(n\) qubits, can be a dense \(2^{n} \times 2^{n}\) array or a sparse matrix, depending on how the Hamiltonian was created.

classmethod from_symbolic(symbolic_hamiltonian, symbol_map, backend=None)[source]#

Creates a qibo.hamiltonian.Hamiltonian from a symbolic Hamiltonian.

We refer to How to define custom Hamiltonians using symbols? for more details.

Parameters:
  • symbolic_hamiltonian (sympy.Expr) – full Hamiltonian written with sympy symbols.

  • symbol_map (dict) – Dictionary that maps each symbol that appears in the Hamiltonian to a pair (target, matrix).

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

object that implements the Hamiltonian represented by the given symbolic expression.

Return type:

qibo.hamiltonians.SymbolicHamiltonian

eigenvalues(k=6)[source]#

Computes the eigenvalues for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigvalsh() for more details.

eigenvectors(k=6)[source]#

Computes a tensor with the eigenvectors for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigh() for more details.

exp(a)[source]#

Computes a tensor corresponding to \(\exp(-i \, a \, H)\).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

expectation(state, normalize=False)[source]#

Computes the real expectation value for a given state.

Parameters:
  • state (ndarray) – state in which to calculate the expectation value.

  • normalize (bool, optional) – If True, the expectation value \(\ell_{2}\)-normalized. Defaults to False.

Returns:

real number corresponding to the expectation value.

Return type:

float

expectation_from_samples(freq, qubit_map=None)[source]#

Computes the expectation value of a diagonal observable, given computational-basis measurement frequencies.

Parameters:
  • freq (collections.Counter) – the keys are the observed values in binary form and the values the corresponding frequencies, that is the number of times each measured value/bitstring appears.

  • qubit_map (tuple) – Mapping between frequencies and qubits. If None, then defaults to \([1, \, 2, \, \cdots, \, \mathrm{len}(\mathrm{key})]\). Defaults to None.

Returns:

real number corresponding to the expectation value.

Return type:

float

eye(dim: int | None = None)[source]#

Generate Identity matrix with dimension dim

energy_fluctuation(state)[source]#

Evaluate energy fluctuation:

\[\Xi_{k}(\mu) = \sqrt{\bra{\mu} \, H^{2} \, \ket{\mu} - \bra{\mu} \, H \, \ket{\mu}^2} \, .\]

for a given state \(\ket{\mu}\).

Parameters:

state (ndarray) – quantum state to be used to compute the energy fluctuation.

Returns:

Energy fluctuation value.

Return type:

float

Symbolic Hamiltonian#

Qibo allows the user to define Hamiltonians using sympy symbols. In this case the full Hamiltonian matrix is not constructed unless this is required. This makes the implementation more efficient for larger qubit numbers. For more information on constructing Hamiltonians using symbols we refer to the How to define custom Hamiltonians using symbols? example.

class qibo.hamiltonians.SymbolicHamiltonian(form=None, nqubits=None, symbol_map={}, backend=None)[source]#

Hamiltonian based on a symbolic representation.

Calculations using symbolic Hamiltonians are either done directly using the given sympy expression as it is (form) or by parsing the corresponding terms (which are qibo.core.terms.SymbolicTerm objects). The latter approach is more computationally costly as it uses a sympy.expand call on the given form before parsing the terms. For this reason the terms are calculated only when needed, for example during Trotterization. The dense matrix of the symbolic Hamiltonian can be calculated directly from form without requiring terms calculation (see qibo.core.hamiltonians.SymbolicHamiltonian.calculate_dense() for details).

Parameters:
  • form (sympy.Expr) – Hamiltonian form as a sympy.Expr. Ideally the Hamiltonian should be written using Qibo symbols. See How to define custom Hamiltonians using symbols? example for more details.

  • symbol_map (dict) – Dictionary that maps each sympy.Symbol to a tuple of (target qubit, matrix representation). This feature is kept for compatibility with older versions where Qibo symbols were not available and may be deprecated in the future. It is not required if the Hamiltonian is constructed using Qibo symbols. The symbol_map can also be used to pass non-quantum operator arguments to the symbolic Hamiltonian, such as the parameters in the qibo.hamiltonians.models.MaxCut() Hamiltonian.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

property dense: MatrixHamiltonian#

Creates the equivalent Hamiltonian matrix.

property terms#

List of terms of which the Hamiltonian is a sum of.

Terms will be objects of type qibo.core.terms.HamiltonianTerm.

property matrix#

Returns the full matrix representation.

Consisting of \(2^{n} \times 2^{n}`\) elements.

eigenvalues(k=6)[source]#

Computes the eigenvalues for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigvalsh() for more details.

eigenvectors(k=6)[source]#

Computes a tensor with the eigenvectors for the Hamiltonian.

Parameters:

k (int) – Number of eigenvalues to calculate if the Hamiltonian was created using a sparse matrix. This argument is ignored if the Hamiltonian was created using a dense matrix. See qibo.backends.abstract.AbstractBackend.eigh() for more details.

ground_state()[source]#

Computes the ground state of the Hamiltonian.

Uses qibo.hamiltonians.AbstractHamiltonian.eigenvectors() and returns eigenvector corresponding to the lowest energy.

exp(a)[source]#

Computes a tensor corresponding to \(\exp(-i \, a \, H)\).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

expectation(state, normalize=False)[source]#

Computes the real expectation value for a given state.

Parameters:
  • state (ndarray) – state in which to calculate the expectation value.

  • normalize (bool, optional) – If True, the expectation value \(\ell_{2}\)-normalized. Defaults to False.

Returns:

real number corresponding to the expectation value.

Return type:

float

expectation_from_circuit(circuit: Circuit, qubit_map: dict = None, nshots: int = 1000) float[source]#

Calculate the expectation value from a circuit. This even works for observables not completely diagonal in the computational basis, but only diagonal at a term level in a defined basis. Namely, for an observable of the form :math:H = \sum_i H_i, where each :math:H_i consists in a n-qubits pauli operator \(P_0 \otimes P_1 \otimes \cdots \otimes P_n\), the expectation value is computed by rotating the input circuit in the suitable basis for each term :math:H_i thus extracting the term-wise expectations that are then summed to build the global expectation value. Each term of the observable is treated separately, by measuring in the correct basis and re-executing the circuit.

Parameters:
  • circuit (Circuit) – input circuit.

  • qubit_map (dict) – qubit map, defaults to {0: 0, 1: 1, ..., n: n}

  • nshots (int) – number of shots, defaults to 1000.

Returns:

the calculated expectation value.

Return type:

(float)

expectation_from_samples(freq: dict, qubit_map: dict | None = None) float[source]#

Calculate the expectation value from the samples. The observable has to be diagonal in the computational basis.

Parameters:
  • freq (dict) – input frequencies of the samples.

  • qubit_map (dict) – qubit map.

Returns:

the calculated expectation value.

Return type:

(float)

apply_gates(state, density_matrix=False)[source]#

Applies gates corresponding to the Hamiltonian terms.

Gates are applied to the given state.

Helper method for qibo.hamiltonians.SymbolicHamiltonian.__matmul__().

circuit(dt, accelerators=None)[source]#

Circuit that implements a Trotter step of this Hamiltonian.

Parameters:
  • dt (float) – Time step used for Trotterization.

  • accelerators (dict, optional) – Dictionary with accelerators for distributed circuits. Defaults to None.

When a qibo.hamiltonians.SymbolicHamiltonian is used for time evolution then Qibo will automatically perform this evolution using the Trotter of the evolution operator. This is done by automatically splitting the Hamiltonian to sums of commuting terms, following the description of Sec. 4.1 of arXiv:1901.05824. For more information on time evolution we refer to the How to simulate time evolution? example.

In addition to the abstract Hamiltonian models, Qibo provides the following pre-coded Hamiltonians:

Heisenberg XXZ#

class qibo.hamiltonians.XXZ(nqubits, delta=0.5, dense: bool = True, backend=None)[source]#

Heisenberg \(\mathrm{XXZ}\) model with periodic boundary conditions.

\[H = \sum _{k=0}^N \, \left( X_{k} \, X_{k + 1} + Y_{k} \, Y_{k + 1} + \delta Z_{k} \, Z_{k + 1} \right) \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • delta (float, optional) – coefficient for the \(Z\) component. Defaults to \(0.5\).

  • dense (bool, optional) – If True, creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian. Defaults to True.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Example

from qibo.hamiltonians import XXZ
h = XXZ(3) # initialized XXZ model with 3 qubits

Non-interacting Pauli-X#

class qibo.hamiltonians.X(nqubits, dense: bool = True, backend=None)[source]#

Non-interacting Pauli-\(X\) Hamiltonian.

\[H = - \sum _{k=0}^N \, X_{k} \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • dense (bool, optional) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian. Defaults to True.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Non-interacting Pauli-Y#

class qibo.hamiltonians.Y(nqubits, dense: bool = True, backend=None)[source]#

Non-interacting Pauli-\(Y\) Hamiltonian.

\[H = - \sum _{k=0}^{N} \, Y_{k} \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Non-interacting Pauli-Z#

class qibo.hamiltonians.Z(nqubits, dense: bool = True, backend=None)[source]#

Non-interacting Pauli-\(Z\) Hamiltonian.

\[H = - \sum _{k=0}^{N} \, Z_{k} \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Transverse field Ising model#

class qibo.hamiltonians.TFIM(nqubits, h: float = 0.0, dense: bool = True, backend=None)[source]#

Transverse field Ising model with periodic boundary conditions.

\[H = - \sum _{k=0}^{N} \, \left(Z_{k} \, Z_{k + 1} + h \, X_{k}\right) \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • h (float, optional) – value of the transverse field. Defaults to \(0.0\).

  • dense (bool, optional) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian. Defaults to True.

Max Cut#

class qibo.hamiltonians.MaxCut(nqubits, dense: bool = True, backend=None)[source]#

Max Cut Hamiltonian.

\[H = -\frac{1}{2} \, \sum _{j, k = 0}^{N} \, \left(1 - Z_{j} \, Z_{k}\right) \, .\]
Parameters:
  • nqubits (int) – number of qubits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Note

All pre-coded Hamiltonians can be created as qibo.hamiltonians.Hamiltonian using dense=True or qibo.hamiltonians.SymbolicHamiltonian using the dense=False. In the first case the Hamiltonian is created using its full matrix representation of size (2 ** n, 2 ** n) where n is the number of qubits that the Hamiltonian acts on. This matrix is used to calculate expectation values by direct matrix multiplication to the state and for time evolution by exact exponentiation. In contrast, when dense=False the Hamiltonian contains a more compact representation as a sum of local terms. This compact representation can be used to calculate expectation values via a sum of the local term expectations and time evolution via the Trotter decomposition of the evolution operator. This is useful for systems that contain many qubits for which constructing the full matrix is intractable.


Symbols#

Qibo provides a basic set of symbols which inherit the sympy.Symbol object and can be used to construct qibo.hamiltonians.SymbolicHamiltonian objects as described in the previous section.

class qibo.symbols.Symbol(q, matrix=None, name='Symbol', commutative=False, **assumptions)[source]#

Qibo specialization for sympy symbols.

These symbols can be used to create qibo.hamiltonians.hamiltonians.SymbolicHamiltonian. See How to define custom Hamiltonians using symbols? for more details.

Example

from qibo import hamiltonians
from qibo.symbols import X, Y, Z
# construct a XYZ Hamiltonian on two qubits using Qibo symbols
form = X(0) * X(1) + Y(0) * Y(1) + Z(0) * Z(1)
ham = hamiltonians.SymbolicHamiltonian(form)
Parameters:
  • q (int) – Target qubit id.

  • matrix (np.ndarray) – 2x2 matrix represented by this symbol.

  • name (str) – Name of the symbol which defines how it is represented in symbolic expressions.

  • commutative (bool) – If True the constructed symbols commute with each other. Default is False. This argument should be used with caution because quantum operators are not commutative objects and therefore switching this to True may lead to wrong results. It is useful for improving performance in symbolic calculations in cases where the user is sure that the operators participating in the Hamiltonian form are commuting (for example when the Hamiltonian consists of Z terms only).

property gate#

Qibo gate that implements the action of the symbol on states.

full_matrix(nqubits)[source]#

Calculates the full dense matrix corresponding to the symbol as part of a bigger system.

Parameters:

nqubits (int) – Total number of qubits in the system.

Returns:

Matrix of dimension (2^nqubits, 2^nqubits) composed of the Kronecker product between identities and the symbol’s single-qubit matrix.

class qibo.symbols.I(q, commutative=False, **assumptions)[source]#

Qibo symbol for the identity operator.

Parameters:

q (int) – Target qubit id.

class qibo.symbols.X(q, commutative=False, **assumptions)[source]#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.

class qibo.symbols.Y(q, commutative=False, **assumptions)[source]#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.

class qibo.symbols.Z(q, commutative=False, **assumptions)[source]#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.


Execution Outcomes#

Qibo circuits return different objects when executed depending on what the circuit contains and on the settings of the simulation. The following table summarizes which outcomes to expect depending on whether:

  • the circuit contains noise channels

  • the qubits are measured at the end of the execution

  • some collapse measurement is present in the circuit

  • density_matrix is set to True in simulation

Noise

Measurements

Collapse

Density Matrix

Outcome

❌ / ✅

qibo.result.QuantumState

❌ / ✅

qibo.result.CircuitResult

❌ / ✅

❌ / ✅

qibo.result.QuantumState

❌ / ✅

❌ / ✅

qibo.result.MeasurementOutcomes

❌ / ✅

❌ / ✅

qibo.result.CircuitResult

Therefore, one of the three objects qibo.result.QuantumState, qibo.result.MeasurementOutcomes or qibo.result.CircuitResult is going to be returned by the circuit execution. The first gives acces to the final state and probabilities via the qibo.result.QuantumState.state() and qibo.result.QuantumState.probabilities() methods, whereas the second allows to retrieve the final samples, the frequencies and the probabilities (calculated as frequencies/nshots) with the qibo.result.MeasurementOutcomes.samples(), qibo.result.MeasurementOutcomes.frequencies() and qibo.result.MeasurementOutcomes.probabilities() methods respectively. The qibo.result.CircuitResult object includes all the above instead.

Every time some measurement is performed at the end of the execution, the result will be a CircuitResult unless the final state could not be represented with the current simulation settings, i.e. if some stochasticity is present in the ciruit (via noise channels or collapse measurements) and density_matrix=False. In that case a simple MeasurementOutcomes object is returned.

If no measurement is appended at the end of the circuit, the final QuantumState is going to be provided as output. However, if the circuit is stochastic, density_matrix should be set to True in order to recover the final state, otherwise an error is raised.

The final result of the circuit execution can also be saved to disk and loaded back:

circuit = Circuit(2)
circuit.add(gates.M(0,1))
# this will be a CircuitResult object
result = circuit()
# save it to final_result.npy
result.dump('final_result.npy')
# can be loaded back
from qibo.result import load_result

loaded_result = load_result('final_result.npy')
class qibo.result.QuantumState(state, backend=None)[source]#

Data structure to represent the final state after circuit execution.

Parameters:
  • state (np.ndarray) – Input quantum state as np.ndarray.

  • backend (qibo.backends.AbstractBackend) – Backend used for the calculations. If not provided, the current backend is going to be used.

symbolic(decimals: int = 5, cutoff: float = 1e-10, max_terms: int = 20)[source]#

Dirac notation representation of the state in the computational basis.

Parameters:
  • decimals (int, optional) – Number of decimals for the amplitudes. Defaults to \(5\).

  • cutoff (float, optional) – Amplitudes with absolute value smaller than the cutoff are ignored from the representation. Defaults to 1e-10.

  • max_terms (int, optional) – Maximum number of terms to print. If the state contains more terms they will be ignored. Defaults to \(20\).

Returns:

A string representing the state in the computational basis.

Return type:

(str)

state(numpy: bool = False)[source]#

State’s tensor representation as a backend tensor.

Parameters:

numpy (bool, optional) – If True the returned tensor will be a numpy array, otherwise it will follow the backend tensor type. Defaults to False.

Returns:

The state in the computational basis.

probabilities(qubits: list | set | None = None)[source]#

Calculates measurement probabilities by tracing out qubits.

When noisy model is applied to a circuit and circuit.density_matrix=False, this method returns the average probability resulting from repeated execution. This probability distribution approximates the exact probability distribution obtained when circuit.density_matrix=True.

Parameters:

qubits (list or set, optional) – Set of qubits that are measured. If None, qubits equates the total number of qubits. Defauts to None.

Returns:

Probabilities over the input qubits.

Return type:

(np.ndarray)

to_dict()[source]#

Returns a dictonary containinig all the information needed to rebuild the QuantumState

dump(filename: str)[source]#

Writes to file the QuantumState for future reloading.

Parameters:

filename (str) – Path to the file to write to.

classmethod from_dict(payload: dict)[source]#

Builds a QuantumState object starting from a dictionary.

Parameters:

payload (dict) – Dictionary containing all the information to load the QuantumState object.

Returns:

Quantum state object..

Return type:

qibo.result.QuantumState

classmethod load(filename: str)[source]#

Builds the QuantumState object stored in a file.

Parameters:

filename (str) – Path to the file containing the QuantumState.

Returns:

Quantum state object.

Return type:

qibo.result.QuantumState

class qibo.result.MeasurementOutcomes(measurements, backend=None, probabilities=None, samples: int | None = None, nshots: int = 1000)[source]#

Object to store the outcomes of measurements after circuit execution.

Parameters:
  • measurements (qibo.gates.M) – Measurement gates.

  • backend (qibo.backends.AbstractBackend) – Backend used for the calculations. If None, then the current backend is used. Defaults to None.

  • probabilities (np.ndarray) – Use these probabilities to generate samples and frequencies.

  • samples (np.darray) – Use these samples to generate probabilities and frequencies.

  • nshots (int) – Number of shots used for samples, probabilities and frequencies generation.

frequencies(binary: bool = True, registers: bool = False)[source]#

Returns the frequencies of measured samples.

Parameters:
  • binary (bool, optional) – If True, returns frequency keys in binary form. If False, returns them in decimal form. Defaults to True.

  • registers (bool, optional) – Group frequencies according to registers. Defaults to False.

Returns:

A collections.Counter where the keys are the observed values and the values the corresponding frequencies, that is the number of times each measured value/bitstring appears.

If binary is True

the keys of the collections.Counter are in binary form, as strings of \(0\) and :math`1`.

If binary is False

the keys of the collections.Counter are integers.

If registers is True

a dict of collections.Counter is returned where keys are the name of each register.

If registers is False

a single collections.Counter is returned which contains samples from all the measured qubits, independently of their registers.

probabilities(qubits: list | set | None = None)[source]#

Calculate the probabilities as frequencies / nshots

Returns:

The array containing the probabilities of the measured qubits.

has_samples()[source]#

Check whether the samples are available already.

Returns:

True if the samples are available, False otherwise.

Return type:

(bool)

samples(binary: bool = True, registers: bool = False)[source]#

Returns raw measurement samples.

Parameters:
  • binary (bool, optional) – Return samples in binary or decimal form.

  • registers (bool, optional) – Group samples according to registers.

Returns:

If binary is True

samples are returned in binary form as a tensor of shape (nshots, n_measured_qubits).

If binary is False

samples are returned in decimal form as a tensor of shape (nshots,).

If registers is True

samples are returned in a dict where the keys are the register names and the values are the samples tensors for each register.

If registers is False

a single tensor is returned which contains samples from all the measured qubits, independently of their registers.

property measurement_gate#

Single measurement gate containing all measured qubits.

Useful for sampling all measured qubits at once when simulating.

apply_bitflips(p0: float, p1: float | None = None)[source]#

Apply bitflips to the measurements with probabilities p0 and p1

Parameters:
  • p0 (float) – Probability of the 0->1 flip.

  • p1 (float) – Probability of the 1->0 flip.

expectation_from_samples(observable)[source]#

Computes the real expectation value of a diagonal observable from frequencies.

Parameters:

observable (Hamiltonian/SymbolicHamiltonian) – diagonal observable in the computational basis.

Returns:

expectation value from samples.

Return type:

(float)

to_dict()[source]#

Returns a dictonary containinig all the information needed to rebuild the qibo.result.MeasurementOutcomes.

dump(filename: str)[source]#

Writes to file the qibo.result.MeasurementOutcomes for future reloading.

Parameters:

filename (str) – Path to the file to write to.

classmethod from_dict(payload: dict)[source]#

Builds a qibo.result.MeasurementOutcomes object starting from a dictionary.

Parameters:

payload (dict) – Dictionary containing all the information to load the qibo.result.MeasurementOutcomes object.

Returns:

A qibo.result.MeasurementOutcomes object.

classmethod load(filename: str)[source]#

Builds the qibo.result.MeasurementOutcomes object stored in a file.

Parameters:

filename (str) – Path to the file containing the qibo.result.MeasurementOutcomes.

Returns:

A qibo.result.MeasurementOutcomes object.

class qibo.result.CircuitResult(final_state, measurements, backend=None, samples=None, nshots=1000)[source]#

Object to store both the outcomes of measurements and the final state after circuit execution.

Parameters:
  • final_state (np.ndarray) – Input quantum state as np.ndarray.

  • measurements (qibo.gates.M) – The measurement gates containing the measurements.

  • backend (qibo.backends.AbstractBackend) – Backend used for the calculations. If not provided, then the current backend is going to be used.

  • probabilities (np.ndarray) – Use these probabilities to generate samples and frequencies.

  • samples (np.darray) – Use these samples to generate probabilities and frequencies.

  • nshots (int) – Number of shots used for samples, probabilities and frequencies generation.

probabilities(qubits: list | set | None = None)[source]#

Calculates measurement probabilities by tracing out qubits.

When noisy model is applied to a circuit and circuit.density_matrix=False, this method returns the average probability resulting from repeated execution. This probability distribution approximates the exact probability distribution obtained when circuit.density_matrix=True.

Parameters:

qubits (list or set, optional) – Set of qubits that are measured. If None, qubits equates the total number of qubits. Defauts to None.

Returns:

Probabilities over the input qubits.

Return type:

(np.ndarray)

to_dict()[source]#

Returns a dictonary containinig all the information needed to rebuild the CircuitResult.

classmethod from_dict(payload: dict)[source]#

Builds a CircuitResult object starting from a dictionary.

Parameters:

payload (dict) – Dictionary containing all the information to load the CircuitResult object.

Returns:

circuit result object.

Return type:

qibo.result.CircuitResult

Callbacks#

Callbacks provide a way to calculate quantities on the state vector as it propagates through the circuit. Example of such quantity is the entanglement entropy, which is currently the only callback implemented in qibo.callbacks.EntanglementEntropy. The user can create custom callbacks by inheriting the qibo.callbacks.Callback class. The point each callback is calculated inside the circuit is defined by adding a qibo.gates.CallbackGate. This can be added similarly to a standard gate and does not affect the state vector.

class qibo.callbacks.Callback[source]#

Base callback class.

Results of a callback can be accessed by indexing the corresponding object.

property nqubits#

Total number of qubits in the circuit that the callback was added in.

Entanglement entropy#

class qibo.callbacks.EntanglementEntropy(partition: List[int] | None = None, compute_spectrum: bool = False, base: float = 2, check_hermitian: bool = False)[source]#

Von Neumann entanglement entropy callback.

\[S = \mathrm{Tr} \left ( \rho \log _2 \rho \right )\]
Parameters:
  • partition (list) – List with qubit ids that defines the first subsystem for the entropy calculation. If partition is not given then the first subsystem is the first half of the qubits.

  • compute_spectrum (bool) – Compute the entanglement spectrum. Default is False.

Example

from qibo import Circuit, gates
from qibo.callbacks import EntanglementEntropy

# create entropy callback where qubit 0 is the first subsystem
entropy = EntanglementEntropy([0], compute_spectrum=True)
# initialize circuit with 2 qubits and add gates
circuit = Circuit(2)
# add callback gates between normal gates
circuit.add(gates.CallbackGate(entropy))
circuit.add(gates.H(0))
circuit.add(gates.CallbackGate(entropy))
circuit.add(gates.CNOT(0, 1))
circuit.add(gates.CallbackGate(entropy))
# execute the circuit
final_state = circuit()
print(entropy[:])
# Should print [0, 0, 1] which is the entanglement entropy
# after every gate in the calculation.
print(entropy.spectrum)
# Print the entanglement spectrum.
property nqubits#

Total number of qubits in the circuit that the callback was added in.

Norm#

class qibo.callbacks.Norm[source]#

State norm callback.

\[\mathrm{Norm} = \left \langle \Psi | \Psi \right \rangle = \mathrm{Tr} (\rho )\]

Overlap#

class qibo.callbacks.Overlap(state)[source]#

State overlap callback.

Calculates the overlap between the circuit state and a given target state:

\[\mathrm{Overlap} = |\left \langle \Phi | \Psi \right \rangle |\]
Parameters:
  • state (np.ndarray) – Target state to calculate overlap with.

  • normalize (bool) – If True the states are normalized for the overlap calculation.

Energy#

class qibo.callbacks.Energy(hamiltonian: hamiltonians.Hamiltonian)[source]#

Energy expectation value callback.

Calculates the expectation value of a given Hamiltonian as:

\[\left \langle H \right \rangle = \left \langle \Psi | H | \Psi \right \rangle = \mathrm{Tr} (\rho H)\]

assuming that the state is normalized.

Parameters:

hamiltonian (qibo.hamiltonians.Hamiltonian) – Hamiltonian object to calculate its expectation value.

Gap#

class qibo.callbacks.Gap(mode: str | int = 'gap', check_degenerate: bool = True)[source]#

Callback for calculating the gap of adiabatic evolution Hamiltonians.

Can also be used to calculate the Hamiltonian eigenvalues at each time step during the evolution. Note that this callback can only be added in qibo.evolution.AdiabaticEvolution models.

Parameters:
  • mode (str/int) – Defines which quantity this callback calculates. If mode == 'gap' then the difference between ground state and first excited state energy (gap) is calculated. If mode is an integer, then the energy of the corresponding eigenstate is calculated.

  • check_degenerate (bool) – If True the excited state number is increased until a non-zero gap is found. This is used to find the proper gap in the case of degenerate Hamiltonians. This flag is relevant only if mode is 'gap'. Default is True.

Example

from qibo import callbacks, hamiltonians
from qibo.models import AdiabaticEvolution
# define easy and hard Hamiltonians for adiabatic evolution
h0 = hamiltonians.X(3)
h1 = hamiltonians.TFIM(3, h=1.0)
# define callbacks for logging the ground state, first excited
# and gap energy
ground = callbacks.Gap(0)
excited = callbacks.Gap(1)
gap = callbacks.Gap()
# define and execute the ``AdiabaticEvolution`` model
evolution = AdiabaticEvolution(h0, h1, lambda t: t, dt=1e-1,
                               callbacks=[gap, ground, excited])
final_state = evolution(final_time=1.0)
# print results
print(ground[:])
print(excited[:])
print(gap[:])

Solvers#

Solvers are used to numerically calculate the time evolution of state vectors. They perform steps in time by integrating the time-dependent Schrodinger equation.

class qibo.solvers.BaseSolver(dt, hamiltonian)[source]#

Basic solver that should be inherited by all solvers.

Parameters:
property t#

Solver’s current time.

class qibo.solvers.TrotterizedExponential(dt, hamiltonian)[source]#

Solver that uses Trotterized exponentials.

Created automatically from the qibo.solvers.Exponential if the given Hamiltonian object is a qibo.hamiltonians.hamiltonians.TrotterHamiltonian.

class qibo.solvers.Exponential(dt, hamiltonian)[source]#

Solver that uses the matrix exponential of the Hamiltonian:

\[U(t) = e^{-i H(t) \delta t}\]

Calculates the evolution operator in every step and thus is compatible with time-dependent Hamiltonians.

class qibo.solvers.RungeKutta4(dt, hamiltonian)[source]#

Solver based on the 4th order Runge-Kutta method.

class qibo.solvers.RungeKutta45(dt, hamiltonian)[source]#

Solver based on the 5th order Runge-Kutta method.

Optimizers#

Optimizers are used automatically by the minimize methods of qibo.models.VQE and qibo.evolution.AdiabaticEvolution models. The user does not have to use any of the optimizer methods included in the current section, however the required options of each optimization method can be passed when calling the minimize method of the respective Qibo variational model.

qibo.optimizers.optimize(loss, initial_parameters, args=(), method='Powell', jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, compile=False, processes=None, backend=None)[source]#
Main optimization method. Selects one of the following optimizers:
Parameters:
  • loss (callable) – Loss as a function of parameters and optional extra arguments. Make sure the loss function returns a tensor for method=sgd and numpy object for all the other methods.

  • initial_parameters (np.ndarray) – Initial guess for the variational parameters that are optimized.

  • args (tuple) – optional arguments for the loss function.

  • method (str) – Name of optimizer to use. Can be 'cma', 'sgd' or one of the Newtonian methods supported by qibo.optimizers.newtonian() and 'parallel_L-BFGS-B'. sgd is only available for backends based on tensorflow.

  • jac (dict) – Method for computing the gradient vector for scipy optimizers.

  • hess (dict) – Method for computing the hessian matrix for scipy optimizers.

  • hessp (callable) – Hessian of objective function times an arbitrary vector for scipy optimizers.

  • bounds (sequence or Bounds) – Bounds on variables for scipy optimizers.

  • constraints (dict) – Constraints definition for scipy optimizers.

  • tol (float) – Tolerance of termination for scipy optimizers.

  • callback (callable) – Called after each iteration for scipy optimizers.

  • options (dict) – Dictionary with options. See the specific optimizer bellow for a list of the supported options.

  • compile (bool) – If True the Tensorflow optimization graph is compiled. This is relevant only for the 'sgd' optimizer.

  • processes (int) – number of processes when using the parallel BFGS method.

Returns:

Final best loss value; best parameters obtained by the optimizer; extra: optimizer-specific return object. For scipy methods it returns the OptimizeResult, for 'cma' the CMAEvolutionStrategy.result, and for 'sgd' the options used during the optimization.

Return type:

(float, float, custom)

Example

import numpy as np
from qibo import Circuit, gates
from qibo.optimizers import optimize

# create custom loss function
# make sure the return type matches the optimizer requirements.
def myloss(parameters, circuit):
    circuit.set_parameters(parameters)
    return np.square(np.sum(circuit().state())) # returns numpy array

# create circuit ansatz for two qubits
circuit = Circuit(2)
circuit.add(gates.RY(0, theta=0))

# optimize using random initial variational parameters
initial_parameters = np.random.uniform(0, 2, 1)
best, params, extra = optimize(myloss, initial_parameters, args=(circuit))

# set parameters to circuit
circuit.set_parameters(params)
qibo.optimizers.cmaes(loss, initial_parameters, args=(), callback=None, options=None)[source]#

Genetic optimizer based on pycma.

Parameters:
  • loss (callable) – Loss as a function of variational parameters to be optimized.

  • initial_parameters (np.ndarray) – Initial guess for the variational parameters.

  • args (tuple) – optional arguments for the loss function.

  • callback (list[callable]) – List of callable called after each optimization iteration. According to cma-es implementation take CMAEvolutionStrategy instance as argument. See: https://cma-es.github.io/apidocs-pycma/cma.evolution_strategy.CMAEvolutionStrategy.html.

  • options (dict) – Dictionary with options accepted by the cma optimizer. The user can use import cma; cma.CMAOptions() to view the available options.

qibo.optimizers.newtonian(loss, initial_parameters, args=(), method='Powell', jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, processes=None, backend=None)[source]#

Newtonian optimization approaches based on scipy.optimize.minimize.

For more details check the scipy documentation.

Note

When using the method parallel_L-BFGS-B the processes option controls the number of processes used by the parallel L-BFGS-B algorithm through the multiprocessing library. By default processes=None, in this case the total number of logical cores are used. Make sure to select the appropriate number of processes for your computer specification, taking in consideration memory and physical cores. In order to obtain optimal results you can control the number of threads used by each process with the qibo.set_threads method. For example, for small-medium size circuits you may benefit from single thread per process, thus set qibo.set_threads(1) before running the optimization.

Parameters:
  • loss (callable) – Loss as a function of variational parameters to be optimized.

  • initial_parameters (np.ndarray) – Initial guess for the variational parameters.

  • args (tuple) – optional arguments for the loss function.

  • method (str) – Name of method supported by scipy.optimize.minimize and 'parallel_L-BFGS-B' for a parallel version of L-BFGS-B algorithm.

  • jac (dict) – Method for computing the gradient vector for scipy optimizers.

  • hess (dict) – Method for computing the hessian matrix for scipy optimizers.

  • hessp (callable) – Hessian of objective function times an arbitrary vector for scipy optimizers.

  • bounds (sequence or Bounds) – Bounds on variables for scipy optimizers.

  • constraints (dict) – Constraints definition for scipy optimizers.

  • tol (float) – Tolerance of termination for scipy optimizers.

  • callback (callable) – Called after each iteration for scipy optimizers.

  • options (dict) – Dictionary with options accepted by scipy.optimize.minimize.

  • processes (int) – number of processes when using the parallel BFGS method.

qibo.optimizers.sgd(loss, initial_parameters, args=(), callback=None, options=None, compile=False, backend=None)[source]#

Stochastic Gradient Descent (SGD) optimizer using Tensorflow backpropagation.

See tf.keras.Optimizers for a list of the available optimizers for Tensorflow. See torch.optim for a list of the available optimizers for PyTorch.

Parameters:
  • loss (callable) – Loss as a function of variational parameters to be optimized.

  • initial_parameters (np.ndarray) – Initial guess for the variational parameters.

  • args (tuple) – optional arguments for the loss function.

  • callback (callable) – Called after each iteration.

  • options (dict) –

    Dictionary with options for the SGD optimizer. Supports the following keys:

    • 'optimizer' (str, default: 'Adagrad'): Name of optimizer.

    • 'learning_rate' (float, default: '1e-3'): Learning rate.

    • 'nepochs' (int, default: 1e6): Number of epochs for optimization.

    • 'nmessage' (int, default: 1e3): Every how many epochs to print a message of the loss function.

Parameter#

It can be useful to define custom parameters in an optimization context. For example, the rotational angles which encodes information in a Quantum Neural Network are usually built as a combination of features and trainable parameters. For doing this, the qibo.parameter.Parameter class can be used. It allows to define custom parameters which can be inserted into a qibo.models.circuit.Circuit. Moreover, it automatically precomputes the analytical derivative of the parameter function, which can be used to calculate the derivatives of a variational model with respect to its parameters.

qibo.parameter.calculate_derivatives(func)[source]#

Calculates derivatives w.r.t. to all parameters of a target function func.

class qibo.parameter.Parameter(func, trainable=None, features=None)[source]#

Object which allows for variational gate parameters. Several trainable parameters and possibly features are linked through a lambda function which returns the final gate parameter. All possible analytical derivatives of the lambda function are calculated at the object initialisation using Sympy.

Example:

from qibo.parameter import Parameter
param = Parameter(
        lambda x, th1, th2, th3: x**2 * th1 + th2 * th3**2,
        features=[7.0],
        trainable=[1.5, 2.0, 3.0],
    )

partial_derivative = param.get_partial_derivative(3)

param.update_parameters(trainable=[15.0, 10.0, 7.0], feature=[5.0])
param_value = param()
Parameters:
  • func (function) – lambda function which builds the gate parameter. If both features and trainable parameters compose the function, it must be passed by first providing the features and then the parameters, as described in the code example above.

  • features (list or np.ndarray) – array containing possible input features x.

  • trainable (list or np.ndarray) – array with initial trainable parameters theta.

property nparams#

Returns the number of trainable parameters

property nfeat#

Returns the number of features

property ncomponents#

Return the number of elements which compose the Parameter

trainable_parameter_indices(start_index)[source]#

Return list of respective indices of trainable parameters within the larger trainable parameter list of a circuit for example

unaffected_by(trainable_idx)[source]#

Retrieve constant term of lambda function with regard to a specific trainable parameter

partial_derivative(trainable_idx)[source]#

Get derivative w.r.t a trainable parameter

Gradients#

In the context of optimization, particularly when dealing with Quantum Machine Learning problems, it is often necessary to calculate the gradients of functions that are to be minimized (or maximized). Hybrid methods, which are based on the use of classical techniques for the optimization of quantum computation procedures, have been presented in the previous section. This approach is very useful in simulation, but some classical methods cannot be used when using real circuits: for example, in the context of neural networks, the Back-Propagation algorithm is used, where it is necessary to know the value of a target function during the propagation of information within the network. Using a real circuit, we would not be able to access this information without taking a measurement, causing the state of the system to collapse and losing the information accumulated up to that moment. For this reason, in qibo we have also implemented methods for calculating the gradients which can be performed directly on the hardware, such as the Parameter Shift Rule.

qibo.derivative.parameter_shift(circuit, hamiltonian, parameter_index, initial_state=None, scale_factor=1, nshots=None)[source]#

In this method the parameter shift rule (PSR) is implemented. Given a circuit \(U\) and an observable \(H\), the PSR allows to calculate the derivative of the expected value of \(H\) on the final state with respect to a variational parameter of the circuit. There is also the possibility of setting a scale factor. It is useful when a circuit’s parameter is obtained by combination of a variational parameter and an external object, such as a training variable in a Quantum Machine Learning problem. For example, performing a re-uploading strategy to embed some data into a circuit, we apply to the quantum state rotations whose angles are in the form \(\theta^{\prime} = x \, \theta\), where \(\theta\) is a variational parameter, and \(x\) an input variable. The PSR allows to calculate the derivative with respect to \(\theta^{\prime}\). However, if we want to optimize a system with respect to its variational parameters, we need to “free” this procedure from the \(x\) depencency. If the scale_factor is not provided, it is set equal to one and doesn’t affect the calculation. If the PSR is needed to be executed on a real quantum device, it is important to set nshots to some integer value. This enables the execution on the hardware by calling the proper methods.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – custom quantum circuit.

  • hamiltonian (qibo.hamiltonians.Hamiltonian) – target observable. if you want to execute on hardware, a symbolic hamiltonian must be provided as follows (example with Pauli-\(Z\) and \(n = 1\)): SymbolicHamiltonian(np.prod([ Z(i) for i in range(1) ])).

  • parameter_index (int) – the index which identifies the target parameter in the circuit.get_parameters() list.

  • initial_state (ndarray, optional) – initial state on which the circuit acts. If None, defaults to the zero state \(\ket{\mathbf{0}}\). Defaults to None.

  • scale_factor (float, optional) – parameter scale factor. Defaults to \(1\).

  • nshots (int, optional) – number of shots if derivative is evaluated on hardware. If None, the simulation mode is executed. Defaults to None.

Returns:

Value of the derivative of the expectation value of the hamiltonian

with respect to the target variational parameter.

Return type:

float

Example

import qibo
import numpy as np
from qibo import Circuit, gates, hamiltonians
from qibo.derivative import parameter_shift

# defining an observable
def hamiltonian(nqubits = 1):
    m0 = (1/nqubits)*hamiltonians.Z(nqubits).matrix
    ham = hamiltonians.Hamiltonian(nqubits, m0)

    return ham

# defining a dummy circuit
def circuit(nqubits = 1):
    c = Circuit(nqubits = 1)
    c.add(gates.RY(q = 0, theta = 0))
    c.add(gates.RX(q = 0, theta = 0))
    c.add(gates.M(0))

    return c

# initializing the circuit
c = circuit(nqubits = 1)

# some parameters
test_params = np.random.randn(2)
c.set_parameters(test_params)

test_hamiltonian = hamiltonian()

# running the psr with respect to the two parameters
grad_0 = parameter_shift(circuit=c, hamiltonian=test_hamiltonian, parameter_index=0)
grad_1 = parameter_shift(circuit=c, hamiltonian=test_hamiltonian, parameter_index=1)
qibo.derivative.finite_differences(circuit, hamiltonian, parameter_index, initial_state=None, step_size=1e-07)[source]#

Calculate derivative of the expectation value of hamiltonian on the final state obtained by executing circuit on initial_state with respect to the variational parameter identified by parameter_index in the circuit’s parameters list. This method can be used only in exact simulation mode.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – custom quantum circuit.

  • hamiltonian (qibo.hamiltonians.Hamiltonian) – target observable. To execute on hardware, a symbolic hamiltonian must be provided as follows (example with Pauli-\(Z\) and \(n = 1\)): SymbolicHamiltonian(np.prod([ Z(i) for i in range(1) ])).

  • parameter_index (int) – the index which identifies the target parameter in the qibo.models.Circuit.get_parameters() list.

  • initial_state (ndarray, optional) – initial state on which the circuit acts. If None, defaults to the zero state \(\ket{\mathbf{0}}\). Defaults to None.

  • step_size (float, optional) – step size used to evaluate the finite difference. Defaults to \(10^{-7}\).

Returns:

Value of the derivative of the expectation value of the hamiltonian

with respect to the target variational parameter.

Return type:

float

Quantum Information#

This module provides tools for generation and analysis of quantum (and classical) information.

Basis#

Set of functions related to basis and basis transformations.

Pauli basis#

qibo.quantum_info.pauli_basis(nqubits: int, normalize: bool = False, vectorize: bool = False, sparse: bool = False, order: str | None = None, pauli_order: str = 'IXYZ', backend=None)[source]#

Creates the nqubits-qubit Pauli basis.

Parameters:
  • nqubits (int) – number of qubits.

  • normalize (bool, optional) – If True, normalized basis is returned. Defaults to False.

  • vectorize (bool, optional) – If False, returns a nested array with all Pauli matrices. If True, retuns an array where every row is a vectorized Pauli matrix. Defaults to False.

  • sparse (bool, optional) – If True, retuns Pauli basis in a sparse representation. Defaults to False.

  • order (str, optional) – If "row", vectorization of Pauli basis is performed row-wise. If "column", vectorization is performed column-wise. If "system", system-wise vectorization is performed. If vectorization=False, then order=None is forced. Defaults to None.

  • pauli_order (str, optional) – corresponds to the order of 4 single-qubit Pauli elements. Defaults to "IXYZ".

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

all Pauli matrices forming the basis. If sparse=True

and vectorize=True, tuple is composed of an array of non-zero elements and an array with their row-wise indexes.

Return type:

ndarray or tuple

Computational basis to Pauli basis#

qibo.quantum_info.comp_basis_to_pauli(nqubits: int, normalize: bool = False, sparse: bool = False, order: str = 'row', pauli_order: str = 'IXYZ', backend=None)[source]#

Unitary matrix \(U\) that converts operators from the Liouville representation in the computational basis to the Pauli-Liouville representation.

The unitary \(U\) is given by

\[U = \sum_{k = 0}^{d^{2} - 1} \, |k)(P_{k}| \,\, ,\]

where \(|P_{k})\) is the vectorization of the \(k\)-th Pauli operator \(P_{k}\), and \(|k)\) is the vectorization of the \(k\)-th computational basis element. For a definition of vectorization, see qibo.quantum_info.vectorization().

Example

from qibo.quantum_info import random_density_matrix, vectorization, comp_basis_to_pauli
nqubits = 2
d = 2**nqubits
rho = random_density_matrix(d)
U_c2p = comp_basis_to_pauli(nqubits)
rho_liouville = vectorization(rho, order="system")
rho_pauli_liouville = U_c2p @ rho_liouville
Parameters:
  • nqubits (int) – number of qubits.

  • normalize (bool, optional) – If True, converts to the Pauli basis. Defaults to False.

  • sparse (bool, optional) – If True, returns unitary matrix in sparse representation. Defaults to False.

  • order (str, optional) – If "row", vectorization of Pauli basis is performed row-wise. If "column", vectorization is performed column-wise. If "system", system-wise vectorization is performed. Defaults to "row".

  • pauli_order (str, optional) – corresponds to the order of 4 single-qubit Pauli elements. Defaults to "IXYZ".

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Unitary matrix \(U\). If sparse=True,

tuple is composed of array of non-zero elements and an array with their row-wise indexes.

Return type:

ndarray or tuple

Pauli basis to computational basis#

qibo.quantum_info.pauli_to_comp_basis(nqubits: int, normalize: bool = False, sparse: bool = False, order: str = 'row', pauli_order: str = 'IXYZ', backend=None)[source]#

Unitary matrix \(U\) that converts operators from the Pauli-Liouville representation to the Liouville representation in the computational basis.

The unitary \(U\) is given by

\[U = \sum_{k = 0}^{d^{2} - 1} \, |P_{k})(b_{k}| \, ,\]

where \(|P_{k})\) is the vectorization of the \(k\)-th Pauli operator \(P_{k}\), and \(|k)\) is the vectorization of the \(k\)-th computational basis element. For a definition of vectorization, see qibo.quantum_info.vectorization().

Parameters:
  • nqubits (int) – number of qubits.

  • normalize (bool, optional) – If True, converts to the Pauli basis. Defaults to False.

  • sparse (bool, optional) – If True, returns unitary matrix in sparse representation. Defaults to False.

  • order (str, optional) – If "row", vectorization of Pauli basis is performed row-wise. If "column", vectorization is performed column-wise. If "system", system-wise vectorization is performed. Defaults to "row".

  • pauli_order (str, optional) – corresponds to the order of 4 single-qubit Pauli elements. Defaults to "IXYZ".

  • backend (qibo.backends.abstract.Backend, optional) – backend to be used in the execution. If None, it uses the current backend. Defaults to None.

Returns:

Unitary matrix \(U\). If sparse=True,

tuple is composed of array of non-zero elements and an array with their row-wise indexes.

Return type:

ndarray or tuple

Phase-space Representation of Stabilizer States#

A stabilizer state \(\ketbra{\psi}{\psi}\) can be uniquely defined by the set of its stabilizers, i.e. those unitary operators \(U\) that have \(\psi\) as an eigenstate with eigenvalue \(1\). In general, \(n\)-qubit stabilizer states are stabilized by \(d = 2^n\) Pauli operators on said \(n\) qubits. However, it is known that the set of \(d\) Paulis can be generated by only \(n\) unique members of the set. In that case, indeed, the number of operators needed to represent a stabilizer state reduces to \(n\). Each one of these \(n\) Pauli generators takes \(2n + 1\) bits to specify, yielding a \(n(2n+1)\) total number of bits needed. In particular, Aaronson and Gottesman (2004) demonstrated that the application of Clifford gates on stabilizer states can be efficiently simulated in this representation at the cost of storing the generators of the destabilizers, in addition to the stabilizers.

A \(n\)-qubit stabilizer state is uniquely defined by a symplectic matrix of the form

../_images/symplectic_matrix.png

where \((x_{kl},z_{kl})\) are the bits encoding the \(n\)-qubits Pauli generator as

\[P_{k} = \bigotimes_{l=1}^{n} \, i^{x_{kl} \, \oplus \, z_{kl}} \, X_{l}^{x_{kl}} \, Z_{l}^{z_{kl}}.\]

The qibo.quantum_info.clifford.Clifford object is in charge of storing the phase-space representation of a stabilizer state. This object is automatically created after the execution of a Clifford circuit through the qibo.backends.clifford.CliffordBackend, but it can also be created by directly passing a symplectic matrix to the constructor.

The generators of the stabilizers can be extracted with the qibo.quantum_info.clifford.Clifford.generators() method, or the complete set of \(d = 2^{n}\) stabilizers operators can be extracted through the qibo.quantum_info.clifford.Clifford.stabilizers() method.

generators, phases = clifford.generators()
stabilizers = clifford.stabilizers()

The destabilizers can be extracted analogously with qibo.quantum_info.clifford.Clifford.destabilizers().

We provide integration with the stim package. It is possible to run Clifford circuits using stim as an engine:

from qibo.backends import CliffordBackend
from qibo.quantum_info import Clifford, random_clifford

clifford_backend = CliffordBackend(engine="stim")

circuit = random_clifford(nqubits)
result = clifford_backend.execute_circuit(circuit)

## Note that the execution above is equivalent to the one below

result = Clifford.from_circuit(circuit, engine="stim")
class qibo.quantum_info.clifford.Clifford(data: ndarray | Circuit, nqubits: int | None = None, measurements: list | None = None, nshots: int = 1000, engine: str | None = None, _backend: CliffordBackend | None = None, _samples: int | None = None)[source]#

Object storing the results of a circuit execution with the qibo.backends.clifford.CliffordBackend.

Parameters:
  • data (ndarray or qibo.models.circuit.Circuit) – If ndarray, it is the symplectic matrix of the stabilizer state in phase-space representation. If qibo.models.circuit.Circuit, it is a circuit composed only of Clifford gates and computational-basis measurements.

  • nqubits (int, optional) – number of qubits of the state.

  • measurements (list, optional) – list of measurements gates qibo.gates.M. Defaults to None.

  • nshots (int, optional) – number of shots used for sampling the measurements. Defaults to \(1000\).

  • engine (str, optional) –

    engine to use in the execution of the qibo.backends.CliffordBackend. It accepts "numpy", "numba", "cupy", and "stim" (see stim). If None, defaults to the corresponding engine from the current backend. Defaults to None.

classmethod from_circuit(circuit: Circuit, initial_state: ndarray | None = None, nshots: int = 1000, engine: str | None = None)[source]#

Allows to create a qibo.quantum_info.clifford.Clifford object by executing the input circuit.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – Clifford circuit to run.

  • initial_state (ndarray, optional) – symplectic matrix of the initial state. If None, defaults to the symplectic matrix of the zero state. Defaults to None.

  • nshots (int, optional) – number of measurement shots to perform if circuit has measurement gates. Defaults to \(10^{3}\).

  • engine (str, optional) –

    engine to use in the execution of the qibo.backends.CliffordBackend. It accepts "numpy", "numba", "cupy", and "stim" (see stim). If None, defaults to the corresponding engine from the current backend. Defaults to None.

Returns:

Object storing the result of the circuit execution.

Return type:

(qibo.quantum_info.clifford.Clifford)

to_circuit(algorithm: str | None = 'AG04')[source]#

Converts symplectic matrix into a Clifford circuit.

Parameters:

algorithm (str, optional) – If AG04, uses the decomposition algorithm from Aaronson & Gottesman (2004). If BM20 and Clifford.nqubits <= 3, uses the decomposition algorithm from Bravyi & Maslov (2020). Defaults to AG04.

Returns:

circuit composed of Clifford gates.

Return type:

qibo.models.circuit.Circuit

generators(return_array: bool = False)[source]#

Extracts the generators of stabilizers and destabilizers.

Parameters:

return_array (bool, optional) – If True returns the generators as ndarray. If False, their representation as strings is returned. Defaults to False.

Returns:

Generators and their corresponding phases, respectively.

Return type:

(list, list)

stabilizers(symplectic: bool = False, return_array: bool = False)[source]#

Extracts the stabilizers of the state.

Parameters:
  • symplectic (bool, optional) – If True, returns the rows of the symplectic matrix that correspond to the \(n\) generators of the \(2^{n}\) total stabilizers, independently of return_array.

  • return_array (bool, optional) – To be used when symplectic = False. If True returns the stabilizers as ndarray. If False, returns stabilizers as strings. Defaults to False.

Returns:

Stabilizers of the state.

Return type:

(ndarray or list)

destabilizers(symplectic: bool = False, return_array: bool = False)[source]#

Extracts the destabilizers of the state.

Parameters:
  • symplectic (bool, optional) – If True, returns the rows of the symplectic matrix that correspond to the \(n\) generators of the \(2^{n}\) total destabilizers, independently of return_array.

  • return_array (bool, optional) – To be used when