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, accelerators=None, density_matrix=False)#

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.

Parameters:

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

on_qubits(*qubits)#

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 gates, models
# create small circuit on 4 qubits
smallc = models.Circuit(4)
smallc.add((gates.RX(i, theta=0.1) for i in range(4)))
smallc.add((gates.CNOT(0, 1), gates.CNOT(2, 3)))
# create large circuit on 8 qubits
largec = models.Circuit(8)
largec.add((gates.RY(i, theta=0.1) for i in range(8)))
# add the small circuit to the even qubits of the large one
largec.add(smallc.on_qubits(*range(0, 8, 2)))
light_cone(*qubits)#

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)#

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()#

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 measurement gates, these are included in the inverted circuit.

Returns:

The circuit inverse.

decompose(*free: int)#

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_noise(noise_map: Union[Tuple[int, int, int], Dict[int, Tuple[int, int, int]]])#

Creates a copy of the circuit with 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 using the density_matrix flag set to True. For more information we refer to the How to perform noisy simulation? example.

Parameters:

noise_map (dict) – Dictionary that maps qubit ids to noise probabilities (px, py, pz). If a tuple of probabilities (px, py, pz) is given instead of a dictionary, then the same probabilities will be used for all qubits.

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.models import Circuit
from qibo import gates
# use density matrices for noise simulation
c = Circuit(2, density_matrix=True)
c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)])
noise_map = {0: (0.1, 0.0, 0.2), 1: (0.0, 0.2, 0.1)}
noisy_c = c.with_noise(noise_map)
# ``noisy_c`` will be equivalent to the following circuit
c2 = Circuit(2, density_matrix=True)
c2.add(gates.H(0))
c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2))
c2.add(gates.H(1))
c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1))
c2.add(gates.CNOT(0, 1))
c2.add(gates.PauliNoiseChannel(0, 0.1, 0.0, 0.2))
c2.add(gates.PauliNoiseChannel(1, 0.0, 0.2, 0.1))
add(gate)#

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.

The QASM names are used as gate identifiers.

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

Finds all gate objects of specific type.

Parameters:

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

Returns:

List with all gates that are in the circuit and have the same type with the given gate. The list contains tuples (i, g) where i is the index of the gate g in the circuit’s gate queue.

set_parameters(parameters)#

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.models import Circuit
from qibo import gates
# create a circuit with all parameters set to 0.
c = Circuit(3)
c.add(gates.RX(0, theta=0))
c.add(gates.RY(1, theta=0))
c.add(gates.CZ(1, 2))
c.add(gates.fSim(0, 2, theta=0, phi=0))
c.add(gates.H(2))

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

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()#

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#

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.models import Circuit
from qibo import gates
c = Circuit(3)
c.add(gates.H(0))
c.add(gates.H(1))
c.add(gates.CNOT(0, 2))
c.add(gates.CNOT(1, 2))
c.add(gates.H(2))
c.add(gates.TOFFOLI(0, 1, 2))
print(c.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)#

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 models, gates
c = models.Circuit(2)
c.add([gates.H(0), gates.H(1)])
c.add(gates.CNOT(0, 1))
c.add([gates.Y(0), gates.Y(1)])
# create circuit with fused gates
fused_c = c.fuse()
# now ``fused_c`` contains a single ``FusedGate`` that is
# equivalent to applying the five original gates
unitary(backend=None)#

Creates the unitary matrix corresponding to all circuit gates.

This is a (2 ** nqubits, 2 ** nqubits) matrix obtained by multiplying all circuit gates.

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=None)#

Executes the circuit. Exact implementation depends on the backend.

See qibo.core.circuit.Circuit.execute() for more details.

to_qasm()#

Convert circuit to QASM.

Parameters:

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

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

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 models, gates
qasm_code = '''OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
h q[1];
cx q[0],q[1];'''
c = models.Circuit.from_qasm(qasm_code)
# is equivalent to creating the following circuit
c2 = models.Circuit(2)
c2.add(gates.H(0))
c2.add(gates.H(1))
c2.add(gates.CNOT(0, 1))
draw(line_wrap=70, legend=False) str#

Draw text circuit using unicode symbols.

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

  • legend (bool) – If True prints a legend below the circuit for callbacks and channels. Default is False.

Returns:

String containing text circuit diagram.

Circuit addition#

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

c1 = models.QFT(4)

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

c = c1 + c2

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 models, gates

c = models.Circuit(2)
c.add([gates.H(0), gates.H(1)])
c.add(gates.CZ(0, 1))
c.add([gates.X(0), gates.Y(1)])
fused_c = c.fuse()

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

from qibo import models, gates

c = models.Circuit(3)
c.add([gates.H(0), gates.H(1), gates.H(2)])
c.add(gates.CZ(0, 1))
c.add([gates.X(0), gates.Y(1), gates.Z(2)])
c.add(gates.CNOT(1, 2))
c.add([gates.H(0), gates.H(1), gates.H(2)])
fused_c = c.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, with_swaps=True, accelerators=None)#

Creates a circuit that implements the Quantum Fourier Transform.

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

  • with_swaps (bool) – Use SWAP gates at the end of the circuit so that the qubit order in the final state is the same as the initial state.

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

Returns:

A qibo.models.Circuit that implements the Quantum Fourier Transform.

Example

import numpy as np
from qibo.models import QFT
nqubits = 6
c = 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 = c(init_state)

Variational Quantum Eigensolver (VQE)#

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

This class implements the variational quantum eigensolver algorithm.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – Circuit that implements the variaional ansatz.

  • hamiltonian (qibo.hamiltonians.Hamiltonian) – Hamiltonian object.

Example

import numpy as np
from qibo import gates, models, hamiltonians
# create circuit ansatz for two qubits
circuit = models.Circuit(2)
circuit.add(gates.RY(0, theta=0))
# create XXZ Hamiltonian for two qubits
hamiltonian = hamiltonians.XXZ(2)
# create VQE model for the circuit and Hamiltonian
vqe = models.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', jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, compile=False, processes=None)#

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.

  • 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.

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)#

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 gates, models, hamiltonians
# create circuit ansatz for two qubits
circuit = models.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=hamiltonians.X(2)
problem_hamiltonian=hamiltonians.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 = models.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)#

Set scheduling function s(t) as func.

schedule(t)#

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

hamiltonian(t)#

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)#

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)#

Quantum Approximate Optimization Algorithm (QAOA) model.

The QAOA is introduced in arXiv:1411.4028.

Parameters:
  • hamiltonian (qibo.hamiltonians.Hamiltonian) – problem Hamiltonian whose ground state is sought.

  • mixer (qibo.hamiltonians.Hamiltonian) – mixer Hamiltonian. Must be of the same type and act on the same number of qubits as hamiltonian. If None, qibo.hamiltonians.X is used.

  • solver (str) – solver used to apply the exponential operators. Default solver is ‘exp’ (qibo.solvers.Exponential).

  • 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 hamiltonian is a qibo.hamiltonians.SymbolicHamiltonian.

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)#

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)#

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', mode=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None, compile=False, processes=None)#

Optimizes the variational parameters of the QAOA.

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.

  • mode (str) – the desired loss function. The default is None. Alternatives are “cvar”, and “gibbs”.

  • 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.

Feedback-based Algorithm for Quantum Optimization (FALQON)#

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

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

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

Parameters:
  • hamiltonian (qibo.hamiltonians.Hamiltonian) – problem Hamiltonian whose ground state is sought.

  • mixer (qibo.hamiltonians.Hamiltonian) – mixer Hamiltonian. If None, qibo.hamiltonians.X is used.

  • solver (str) – solver used to apply the exponential operators. Default solver is ‘exp’ (qibo.solvers.Exponential).

  • 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 hamiltonian is a qibo.hamiltonians.SymbolicHamiltonian.

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)#

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.

Style-based Quantum Generative Adversarial Network (style-qGAN)#

class qibo.models.qgan.StyleQGAN(latent_dim, layers=None, circuit=None, set_parameters=None, discriminator=None)#

Model that implements and trains a style-based quantum generative adversarial network.

For original manuscript: arXiv:2110.06933

Parameters:
  • latent_dim (int) – number of latent dimensions.

  • layers (int) – number of layers for the quantum generator. Provide this value only if not using a custom quantum generator.

  • circuit (qibo.models.circuit.Circuit) – custom quantum generator circuit. If not provided, the default quantum circuit will be used.

  • set_parameters (function) – function that creates the array of parameters for the quantum generator. If not provided, the default function will be used.

Example

import numpy as np
import qibo
from qibo.models.qgan import StyleQGAN
# set qibo backend to tensorflow which supports gradient descent training
qibo.set_backend("tensorflow")
# Create reference distribution.
# Example: 3D correlated Gaussian distribution normalized between [-1,1]
reference_distribution = []
samples = 10
mean = [0, 0, 0]
cov = [[0.5, 0.1, 0.25], [0.1, 0.5, 0.1], [0.25, 0.1, 0.5]]
x, y, z = np.random.multivariate_normal(mean, cov, samples).T/4
s1 = np.reshape(x, (samples,1))
s2 = np.reshape(y, (samples,1))
s3 = np.reshape(z, (samples,1))
reference_distribution = np.hstack((s1,s2,s3))
# Train qGAN with your particular setup
train_qGAN = StyleQGAN(latent_dim=1, layers=2)
train_qGAN.fit(reference_distribution, n_epochs=1)
define_discriminator(alpha=0.2, dropout=0.2)#

Define the standalone discriminator model.

set_params(circuit, params, x_input, i)#

Set the parameters for the quantum generator circuit.

generate_latent_points(samples)#

Generate points in latent space as input for the quantum generator.

train(d_model, circuit, hamiltonians_list, save=True)#

Train the quantum generator and classical discriminator.

fit(reference, initial_params=None, batch_samples=128, n_epochs=20000, lr=0.5, save=True)#

Execute qGAN training.

Parameters:
  • reference (array) – samples from the reference input distribution.

  • initial_parameters (array) – initial parameters for the quantum generator. If not provided, the default initial parameters will be used.

  • discriminator (tensorflow.keras.models) – custom classical discriminator. If not provided, the default classical discriminator will be used.

  • batch_samples (int) – number of training examples utilized in one iteration.

  • n_epochs (int) – number of training iterations.

  • lr (float) – initial learning rate for the quantum generator. It controls how much to change the model each time the weights are updated.

  • save (bool) – If True the results of training (trained parameters and losses) will be saved on disk. Default is True.

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)#

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 gates
from qibo.models import Circuit
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()#

Initialize the Grover algorithm with the superposition and Grover ancilla.

diffusion()#

Construct the diffusion operator out of the superposition circuit.

step()#

Combine oracle and diffusion for a Grover step.

circuit(iterations)#

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)#

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)#

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)#

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.states 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(small_tsp.backend, circuit, quantum_state, 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()#
Returns:

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

prepare_initial_state(ordering)#

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.

Time evolution#

State evolution#

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

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)#

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)#

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)#

Sets the variational parameters of the scheduling function.

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

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.

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.

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 CNOT 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, backend=None, noise_model=None, nshots=10000, solve_for_gammas=False)#

Runs the Zero Noise Extrapolation method for error mitigation.

The different noise levels are realized by the insertion of pairs of CNOT gates that resolve to the identiy in the noise-free case.

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

  • observable (numpy.ndarray) – Observable to measure.

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

  • backend (qibo.backends.abstract.Backend) – Calculation engine.

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

  • nshots (int) – Number of shots.

  • solve_for_gammas (bool) – If true, explicitely solve the equations to obtain the gamma coefficients.

Returns:

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

Return type:

numpy.ndarray

qibo.models.error_mitigation.get_gammas(c, solve=True)#

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

Parameters:
  • c (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.

  • solve (bool) – If True computes the coeffients by solving the linear system. Otherwise, use the analytical solution valid for the CNOT insertion method.

Returns:

The computed coefficients.

Return type:

numpy.ndarray

qibo.models.error_mitigation.get_noisy_circuit(circuit, cj)#

Standalone function to generate the noisy circuit with the CNOT pairs insertion.

Parameters:
Returns:

The circuit with the inserted CNOT pairs.

Return type:

qibo.models.circuit.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, backend, noise_model, nshots=10000, model=<function <lambda>>, n_training_samples=100, full_output=False)#

Runs the CDR error mitigation method.

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

  • observable (numpy.ndarray) – Observable to measure.

  • backend (qibo.backends.abstract.Backend) – Calculation engine.

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

  • nshots (int) – Number of shots.

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

  • n_training_samples (int) – Number of training circuits to sample.

  • full_output (bool) – If True, this function returns additional information: val, optimal_params, train_val.

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)

qibo.models.error_mitigation.sample_training_circuit(circuit, replacement_gates=None, sigma=0.5)#

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

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

  • replacement_gates (list) – 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) – Standard devation of the gaussian used for sampling.

Returns:

The sampled circuit.

Return type:

qibo.models.circuit.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, backend, noise_levels, noise_model, nshots=10000, model=<function <lambda>>, n_training_samples=100, full_output=False)#

Runs the vnCDR error mitigation method.

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

  • observable (numpy.ndarray) – Observable to measure.

  • backend (qibo.backends.abstract.Backend) – Calculation engine.

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

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

  • nshots (int) – Number of shots.

  • model – Model used for fitting. This should be a callable function object f(x, *params) taking as input the predictor variable and the parameters. By default a simple linear model f(x,a) := a*x is used, with a beeing the diagonal matrix containing the parameters.

  • n_training_samples (int) – Number of training circuits to sample.

  • full_output (bool) – If True, this function returns additional information: val, optimal_params, train_val.

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)


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#

The base class for gate implementation.

All base gates should inherit this class.

property qubits: Tuple[int]#

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

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#

Checks if two gates commute.

Parameters:

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

Returns:

True if the gates commute, otherwise False.

on_qubits(qubit_map) Gate#

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 models, gates
c = models.Circuit(4)
# Add some CNOT gates
c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3)
c.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0)
c.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3)
c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1)
print(c.draw())
q0: ───X─────
q1: ───|─o─X─
q2: ─o─|─|─o─
q3: ─X─o─X───
dagger() Gate#

Returns the dagger (conjugate transpose) of the gate.

Returns:

A qibo.gates.Gate object representing the dagger of the original gate.

decompose(*free) List[Gate]#

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:

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

generator_eigenvalue()#

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

Returns:

np.float generator’s eigenvalue or raise an error if not implemented.

Single qubit gates#

Hadamard (H)#

class qibo.gates.H(q)#

The Hadamard gate.

Parameters:

q (int) – the qubit id number.

Pauli X (X)#

class qibo.gates.X(q)#

The Pauli X gate.

Parameters:

q (int) – the qubit id number.

decompose(*free, use_toffolis=True)#

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.

Pauli Y (Y)#

class qibo.gates.Y(q)#

The Pauli Y gate.

Parameters:

q (int) – the qubit id number.

Pauli Z (Z)#

class qibo.gates.Z(q)#

The Pauli Z gate.

Parameters:

q (int) – the qubit id number.

S gate (S)#

class qibo.gates.S(q)#

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.

T gate (T)#

class qibo.gates.T(q)#

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.

Identity (I)#

class qibo.gates.I(*q)#

The identity gate.

Parameters:

*q (int) – the qubit id numbers.

Measurement (M)#

class qibo.gates.M(*q, register_name: Optional[str] = None, collapse: bool = False, p0: Optional[ProbsType] = None, p1: Optional[ProbsType] = None)#

The Measure Z 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 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.

  • 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.

add(gate)#

Adds target qubits to a measurement gate.

This method is only used for creating the global measurement gate used by the 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.

Rotation X-axis (RX)#

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

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() (default is True).

generator_eigenvalue()#

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

Returns:

np.float generator’s eigenvalue or raise an error if not implemented.

Rotation Y-axis (RY)#

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

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:
generator_eigenvalue()#

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

Returns:

np.float generator’s eigenvalue or raise an error if not implemented.

Rotation Z-axis (RZ)#

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

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:
generator_eigenvalue()#

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

Returns:

np.float generator’s eigenvalue or raise an error if not implemented.

First general unitary (U1)#

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

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() (default is True).

Second general unitary (U2)#

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

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:

Third general unitary (U3)#

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

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:

Two qubit gates#

Controlled-NOT (CNOT)#

class qibo.gates.CNOT(q0, q1)#

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.

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

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:

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

Controlled-phase (CZ)#

class qibo.gates.CZ(q0, q1)#

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.

Controlled-rotation X-axis (CRX)#

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

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:

Controlled-rotation Y-axis (CRY)#

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

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:

Controlled-rotation Z-axis (CRZ)#

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

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:

Controlled first general unitary (CU1)#

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

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:

Controlled second general unitary (CU2)#

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

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)#

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:

Swap (SWAP)#

class qibo.gates.SWAP(q0, q1)#

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.

iSwap (iSWAP)#

class qibo.gates.iSWAP(q0, q1)#

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.

f-Swap (FSWAP)#

class qibo.gates.FSWAP(q0, q1)#

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.

fSim#

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

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() (default is True).

fSim with general rotation#

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

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() (default is 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)#

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() (default is True).

Parametric YY interaction (RYY)#

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

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:

Parametric ZZ interaction (RZZ)#

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

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:

Special gates#

Toffoli#

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

The Toffoli gate.

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

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

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

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

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:

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

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

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.

Arbitrary unitary#

class qibo.gates.Unitary(unitary, *q, trainable=True, name=None)#

Arbitrary unitary gate.

Parameters:
  • unitary – Unitary matrix as a tensor supported by the backend. Note that there is no check that the matrix passed is actually unitary. This allows the user to create non-unitary gates.

  • *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() (default is True).

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

property parameters#

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

on_qubits(qubit_map)#

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 models, gates
c = models.Circuit(4)
# Add some CNOT gates
c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3)
c.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0)
c.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3)
c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1)
print(c.draw())
q0: ───X─────
q1: ───|─o─X─
q2: ─o─|─|─o─
q3: ─X─o─X───

Callback gate#

class qibo.gates.CallbackGate(callback: Callback)#

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)#

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)#

Check if two gates can be fused.

fuse(gate)#

Fuses two gates.

IONQ Native gates#

GPI#

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

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() (default is True).

GPI2#

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

The GPI2 gate.

Corresponds to the following unitary matrix

\[\begin{split}\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() (default is True).

Mølmer–Sørensen (MS)#

class qibo.gates.MS(q0, q1, phi0, phi1, trainable=True)#

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} 1 & 0 & 0 & -i e^{-i( \phi_0 + \phi_1)} \\ 0 & 1 & -i e^{-i( \phi_0 - \phi_1)} \\ 0 & -i e^{i( \phi_0 - \phi_1)} & 1 & 0 \\ -i e^{i( \phi_0 + \phi_1)} & 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.

  • phi0 (float) – first qubit’s phase.

  • phi1 (float) – second qubit’s phase

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


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(ops)#

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:

ops (list) – List of Kraus operators as pairs (qubits, Ak) where qubits refers the qubit ids that Ak acts on and Ak is the corresponding matrix as a np.ndarray or tf.Tensor.

Example

import numpy as np
from qibo.models import Circuit
from qibo import gates
# initialize circuit with 3 qubits
c = Circuit(3, density_matrix=True)
# define a sqrt(0.4) * X gate
a1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]])
# define a sqrt(0.6) * CNOT gate
a2 = 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 = gates.KrausChannel([((1,), a1), ((0, 2), a2)])
# add the channel to the circuit
c.add(channel)
to_superop(backend=None)#

Returns the Liouville representation of the Kraus channel.

Parameters:

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

Returns:

Liouville representation of the channel.

to_pauli_liouville(normalize: bool = False, backend=None)#

Returns the Liouville representation of the Kraus channel in the Pauli basis.

Parameters:
  • normalize (bool, optional) – If True, normalized basis ir returned. Defaults to False.

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

Returns:

Pauli-Liouville representation of the channel.

Unitary channel#

class qibo.gates.UnitaryChannel(probabilities, ops)#

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:
  • probabilities (list) – List of floats that correspond to the probability that each unitary Uk is applied.

  • ops (list) – List of operators as pairs (qubits, Uk) where qubits refers the qubit ids that Uk acts on and Uk is the corresponding matrix as a np.ndarray/tf.Tensor. Must have the same length as the given probabilities p.

Pauli noise channel#

class qibo.gates.PauliNoiseChannel(q, px=0, py=0, pz=0)#

Noise channel that applies Pauli operators with given probabilities.

Implements the following transformation:

\[\mathcal{E}(\rho ) = (1 - p_x - p_y - p_z) \rho + p_x X\rho X + p_y Y\rho Y + p_z Z\rho Z\]

which can be used to simulate phase flip and bit flip errors. 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:
  • q (int) – Qubit id that the noise acts on.

  • px (float) – Bit flip (X) error probability.

  • py (float) – Y-error probability.

  • pz (float) – Phase flip (Z) error probability.

Depolarizing channel#

class qibo.gates.DepolarizingChannel(q, lam=0)#

\(n\)-qubit Depolarizing quantum error channel,

\[\mathcal{E}(\rho ) = (1 - \lambda) \rho +\lambda \text{Tr}[\rho] \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 != I\).

Parameters:
  • q (tuple) – Qubit ids that the noise acts on.

  • lam (float) – Depolarizing error parameter.

Reset channel#

class qibo.gates.ResetChannel(q, p0=0.0, p1=0.0)#

Single-qubit reset channel.

Implements the following transformation:

\[\mathcal{E}(\rho ) = (1 - p_0 - p_1) \rho + \mathrm{Tr}\rho \otimes (p_0|0\rangle \langle 0| + p_1|1\rangle \langle 1|)\]
Parameters:
  • q (int) – Qubit id that the channel acts on.

  • p0 (float) – Probability to reset to 0.

  • p1 (float) – Probability to reset to 1.

Thermal relaxation channel#

class qibo.gates.ThermalRelaxationChannel(q, t1, t2, time, excited_population=0)#

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_zZ\rho Z + \mathrm{Tr}\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 \mathbb{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 = 1 - e^{-t / T_1} + e^{-t / T_2} - e^{t / T_1 - t / T_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:
  • q (int) – Qubit id that the noise channel acts on.

  • t1 (float) – T1 relaxation time. Should satisfy t1 > 0.

  • t2 (float) – T2 dephasing time. Should satisfy t1 > 0 and t2 < 2 * t1.

  • time (float) – the gate time for relaxation error.

  • excited_population (float) – the population of the excited state at equilibrium. Default is 0.


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#

Class for the implementation of a custom noise model.

Example:

from qibo import models, 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 = NoiseModel()
noise.add(PauliError(px = 0.5), gates.H, 1)
noise.add(PauliError(py = 0.5), gates.CNOT)

# Generate noiseless circuit.
c = models.Circuit(2)
c.add([gates.H(0), gates.H(1), gates.CNOT(0, 1)])

# Apply noise to the circuit according to the noise model.
noisy_c = noise.apply(c)
add(error, gate, qubits=None)#

Add a quantum error for a specific gate and qubit to the noise model.

Parameters:
apply(circuit)#

Generate a noisy quantum circuit according to the noise model built.

Parameters:

circuit (qibo.models.circuit.Circuit) – quantum circuit

Returns:

A (qibo.models.circuit.Circuit) which corresponds to the initial circuit with noise gates added according to the noise model.

Quantum errors#

The quantum errors available to build a noise model are the following:

class qibo.noise.CustomError(channel)#

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), ((0,), a2)]))
class qibo.noise.PauliError(px=0, py=0, pz=0)#

Quantum error associated with the qibo.gates.PauliNoiseChannel.

Parameters:

options (tuple) – see qibo.gates.PauliNoiseChannel

class qibo.noise.ThermalRelaxationError(t1, t2, time, excited_population=0)#

Quantum error associated with the qibo.gates.ThermalRelaxationChannel.

Parameters:

options (tuple) – see qibo.gates.ThermalRelaxationChannel

class qibo.noise.DepolarizingError(lam)#

Quantum error associated with the qibo.gates.DepolarizingChannel.

Parameters:

options (float) – see qibo.gates.DepolarizingChannel

class qibo.noise.ResetError(p0, p1)#

Quantum error associated with the qibo.gates.ResetChannel.

Parameters:

options (tuple) – see qibo.gates.ResetChannel

class qibo.noise.UnitaryError(probabilities, unitaries)#

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.

class qibo.noise.KrausError(ops)#

Quantum error associated with the qibo.gates.KrausChannel.

Parameters:
  • ops (list) – List of Kraus operators as a np.ndarray or tf.Tensor.

  • nqubits (int) – Number of qubits each that Kraus operator acts on.


Hamiltonians#

The main abstract Hamiltonian object of Qibo is:

class qibo.hamiltonians.abstract.AbstractHamiltonian#

Qibo abstraction for Hamiltonian objects.

abstract eigenvalues(k=6)#

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)#

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()#

Computes the ground state of the Hamiltonian.

Uses qibo.hamiltonians.AbstractHamiltonian.eigenvectors() and returns eigenvector corresponding to the lowest energy.

abstract exp(a)#

Computes a tensor corresponding to exp(-1j * a * H).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

abstract expectation(state, normalize=False)#

Computes the real expectation value for a given state.

Parameters:
  • state (array) – the expectation state.

  • normalize (bool) – If True the expectation value is divided with the state’s norm squared.

Returns:

Real number corresponding to the expectation value.

abstract expectation_from_samples(freq, qubit_map=None)#

Computes the real expectation value of a diagonal observable given the frequencies when measuring in the computational basis.

Parameters:
  • freq (collections.Counter) – the keys are the observed values in binary form

  • frequencies (and the values the corresponding) –

  • number (that is the) –

  • appears. (of times each measured value/bitstring) –

  • qubit_map (tuple) – Mapping between frequencies and qubits. If None, [1,…,len(key)]

Returns:

Real number corresponding to the expectation value.

Matrix Hamiltonian#

The first implementation of Hamiltonians uses the full matrix representation of the Hamiltonian operator in the computational basis. This matrix has size (2 ** nqubits, 2 ** nqubits) and therefore its construction is feasible only when number of qubits 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 tf.sparse <https://www.tensorflow.org/api_docs/python/tf/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=None, backend=None)

Hamiltonian based on a dense or sparse matrix representation.

Parameters:
  • nqubits (int) – number of quantum bits.

  • matrix (np.ndarray) – Matrix representation of the Hamiltonian in the computational basis as an array of shape (2 ** nqubits, 2 ** nqubits). Sparse matrices based on scipy.sparse for numpy/qibojit backends or on tf.sparse for the tensorflow backend are also supported.

property matrix

Returns the full matrix representation.

Can be a dense (2 ** nqubits, 2 ** nqubits) array or a sparse matrix, depending on how the Hamiltonian was created.

classmethod from_symbolic(symbolic_hamiltonian, symbol_map, backend=None)

Creates a Hamiltonian from a symbolic Hamiltonian.

We refer to the How to define custom Hamiltonians using symbols? example for more details.

Parameters:
  • symbolic_hamiltonian (sympy.Expr) – The full Hamiltonian written with symbols.

  • symbol_map (dict) – Dictionary that maps each symbol that appears in the Hamiltonian to a pair of (target, matrix).

Returns:

A qibo.hamiltonians.SymbolicHamiltonian object that implements the Hamiltonian represented by the given symbolic expression.

eigenvalues(k=6)

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)

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)

Computes a tensor corresponding to exp(-1j * a * H).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

expectation(state, normalize=False)

Computes the real expectation value for a given state.

Parameters:
  • state (array) – the expectation state.

  • normalize (bool) – If True the expectation value is divided with the state’s norm squared.

Returns:

Real number corresponding to the expectation value.

expectation_from_samples(freq, qubit_map=None)

Computes the real expectation value of a diagonal observable given the frequencies when measuring in the computational basis.

Parameters:
  • freq (collections.Counter) – the keys are the observed values in binary form

  • frequencies (and the values the corresponding) –

  • number (that is the) –

  • appears. (of times each measured value/bitstring) –

  • qubit_map (tuple) – Mapping between frequencies and qubits. If None, [1,…,len(key)]

Returns:

Real number corresponding to the expectation value.

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, symbol_map={}, backend=None)

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.

property dense

Creates the equivalent qibo.hamiltonians.MatrixHamiltonian.

property terms

List of qibo.core.terms.HamiltonianTerm objects of which the Hamiltonian is a sum of.

property matrix

Returns the full (2 ** nqubits, 2 ** nqubits) matrix representation.

eigenvalues(k=6)

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)

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()

Computes the ground state of the Hamiltonian.

Uses qibo.hamiltonians.AbstractHamiltonian.eigenvectors() and returns eigenvector corresponding to the lowest energy.

exp(a)

Computes a tensor corresponding to exp(-1j * a * H).

Parameters:

a (complex) – Complex number to multiply Hamiltonian before exponentiation.

expectation(state, normalize=False)

Computes the real expectation value for a given state.

Parameters:
  • state (array) – the expectation state.

  • normalize (bool) – If True the expectation value is divided with the state’s norm squared.

Returns:

Real number corresponding to the expectation value.

expectation_from_samples(freq, qubit_map=None)

Computes the real expectation value of a diagonal observable given the frequencies when measuring in the computational basis.

Parameters:
  • freq (collections.Counter) – the keys are the observed values in binary form

  • frequencies (and the values the corresponding) –

  • number (that is the) –

  • appears. (of times each measured value/bitstring) –

  • qubit_map (tuple) – Mapping between frequencies and qubits. If None, [1,…,len(key)]

Returns:

Real number corresponding to the expectation value.

apply_gates(state, density_matrix=False)

Applies gates corresponding to the Hamiltonian terms to a given state. Helper method for __matmul__.

circuit(dt, accelerators=None)

Circuit that implements a Trotter step of this Hamiltonian for a given time step dt.

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=True, backend=None)#

Heisenberg XXZ model with periodic boundary conditions.

\[H = \sum _{i=0}^N \left ( X_iX_{i + 1} + Y_iY_{i + 1} + \delta Z_iZ_{i + 1} \right ).\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • delta (float) – coefficient for the Z component (default 0.5).

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

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=True, backend=None)#

Non-interacting Pauli-X Hamiltonian.

\[H = - \sum _{i=0}^N X_i.\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

Non-interacting Pauli-Y#

class qibo.hamiltonians.Y(nqubits, dense=True, backend=None)#

Non-interacting Pauli-Y Hamiltonian.

\[H = - \sum _{i=0}^N Y_i.\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

Non-interacting Pauli-Z#

class qibo.hamiltonians.Z(nqubits, dense=True, backend=None)#

Non-interacting Pauli-Z Hamiltonian.

\[H = - \sum _{i=0}^N Z_i.\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

Transverse field Ising model#

class qibo.hamiltonians.TFIM(nqubits, h=0.0, dense=True, backend=None)#

Transverse field Ising model with periodic boundary conditions.

\[H = - \sum _{i=0}^N \left ( Z_i Z_{i + 1} + h X_i \right ).\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • h (float) – value of the transverse field.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

Max Cut#

class qibo.hamiltonians.MaxCut(nqubits, dense=True, backend=None)#

Max Cut Hamiltonian.

\[H = - \sum _{i,j=0}^N \frac{1 - Z_i Z_j}{2}.\]
Parameters:
  • nqubits (int) – number of quantum bits.

  • dense (bool) – If True it creates the Hamiltonian as a qibo.core.hamiltonians.Hamiltonian, otherwise it creates a qibo.core.hamiltonians.SymbolicHamiltonian.

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)#

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)#

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.X(q, commutative=False, **assumptions)#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.

class qibo.symbols.Y(q, commutative=False, **assumptions)#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.

class qibo.symbols.Z(q, commutative=False, **assumptions)#

Qibo symbol for the Pauli-X operator.

Parameters:

q (int) – Target qubit id.


States#

Qibo circuits return qibo.states.CircuitResult objects when executed. By default, Qibo works as a wave function simulator in the sense that propagates the state vector through the circuit applying the corresponding gates. In this default usage the result of a circuit execution is the full final state vector which can be accessed via qibo.states.CircuitResult.state(). However, for specific applications it is useful to have measurement samples from the final wave function, instead of its full vector form. To that end, qibo.states.CircuitResult provides the qibo.states.CircuitResult.samples() and qibo.states.CircuitResult.frequencies() methods.

The state vector (or density matrix) is saved in memory as a tensor supported by the currently active backend (see Backends for more information). A copy of the state can be created using qibo.states.CircuitResult.copy(). The new state will point to the same tensor in memory as the original one unless the deep=True option was used during the copy call. Note that the qibojit backend performs in-place updates state is used as input to a circuit or time evolution. This will modify the state’s tensor and the tensor of all shallow copies and the current state vector values will be lost. If you intend to keep the current state values, we recommend creating a deep copy before using it as input to a qibo model.

In order to perform measurements the user has to add the measurement gate qibo.gates.M to the circuit and then execute providing a number of shots. If this is done, the qibo.states.CircuitResult returned by the circuit will contain the measurement samples.

For more information on measurements we refer to the How to perform measurements? example.

Circuit result#

class qibo.states.CircuitResult(backend, circuit, execution_result, nshots=None)#

Data structure returned by circuit execution.

Contains all the results produced by the circuit execution, such as the state vector or density matrix, measurement samples and frequencies.

Parameters:
  • backend (qibo.backends.abstract.AbstractBackend) – Backend to use for calculations.

  • circuit (qibo.models.Circuit) – Circuit object that is producing this result.

  • execution_result – Abstract raw data created by the circuit execution. The format of these data depends on the backend and they are processed by the backend. For simulation backends execution_result is a tensor holding the state vector or density matrix representation in the computational basis.

  • nshots (int) – Number of measurement shots, if measurements are performed.

state(numpy=False, decimals=-1, cutoff=1e-10, max_terms=20)#

State’s tensor representation as an backend tensor.

Parameters:
  • numpy (bool) – If True the returned tensor will be a numpy array, otherwise it will follow the backend tensor type. Default is False.

  • decimals (int) – If positive the Dirac representation of the state in the computational basis will be returned as a string. decimals will be the number of decimals of each amplitude. Default is -1.

  • cutoff (float) – Amplitudes with absolute value smaller than the cutoff are ignored from the Dirac representation. Ignored if decimals < 0. Default is 1e-10.

  • max_terms (int) – Maximum number of terms in the Dirac representation. If the state contains more terms they will be ignored. Ignored if decimals < 0. Default is 20.

Returns:

If decimals < 0 a tensor representing the state in the computational basis, otherwise a string with the Dirac representation of the state in the computational basis.

symbolic(decimals=5, cutoff=1e-10, max_terms=20)#

Dirac notation representation of the state in the computational basis.

Parameters:
  • decimals (int) – Number of decimals for the amplitudes. Default is 5.

  • cutoff (float) – Amplitudes with absolute value smaller than the cutoff are ignored from the representation. Default is 1e-10.

  • max_terms (int) – Maximum number of terms to print. If the state contains more terms they will be ignored. Default is 20.

Returns:

A string representing the state in the computational basis.

probabilities(qubits=None)#

Calculates measurement probabilities by tracing out qubits.

Parameters:

qubits (list, set) – Set of qubits that are measured.

property measurement_gate#

Single measurement gate containing all measured qubits.

Useful for sampling all measured qubits at once when simulating.

samples(binary=True, registers=False)#

Returns raw measurement samples.

Parameters:
  • binary (bool) – Return samples in binary or decimal form.

  • registers (bool) – 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.

frequencies(binary=True, registers=False)#

Returns the frequencies of measured samples.

Parameters:
  • binary (bool) – Return frequency keys in binary or decimal form.

  • registers (bool) – Group frequencies according to registers.

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 Counter are in binary form, as strings of 0s and 1s.

If binary is False

the keys of the Counter are integers.

If registers is True

a dict of Counter s is returned where keys are the name of each register.

If registers is False

a single Counter is returned which contains samples from all the measured qubits, independently of their registers.

expectation_from_samples(observable)#

Computes the real expectation value of a diagonal observable from frequencies.

Parameters:

observable (Hamiltonian/SymbolicHamiltonian) – diagonal observable in the computational basis.

Returns:

Real number corresponding to the expectation value.

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#

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: Optional[List[int]] = None, compute_spectrum: bool = False)#

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 models, gates, callbacks
# create entropy callback where qubit 0 is the first subsystem
entropy = callbacks.EntanglementEntropy([0], compute_spectrum=True)
# initialize circuit with 2 qubits and add gates
c = models.Circuit(2)
# add callback gates between normal gates
c.add(gates.CallbackGate(entropy))
c.add(gates.H(0))
c.add(gates.CallbackGate(entropy))
c.add(gates.CNOT(0, 1))
c.add(gates.CallbackGate(entropy))
# execute the circuit
final_state = c()
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#

State norm callback.

\[\mathrm{Norm} = \left \langle \Psi | \Psi \right \rangle = \mathrm{Tr} (\rho )\]

Overlap#

class qibo.callbacks.Overlap(state)#

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)#

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: Union[str, int] = 'gap', check_degenerate: bool = True)#

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)#

Basic solver that should be inherited by all solvers.

Parameters:
property t#

Solver’s current time.

class qibo.solvers.TrotterizedExponential(dt, hamiltonian)#

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)#

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)#

Solver based on the 4th order Runge-Kutta method.

class qibo.solvers.RungeKutta45(dt, hamiltonian)#

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)#
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 gates, models
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())) # returns numpy array

# create circuit ansatz for two qubits
circuit = models.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=(), options=None)#

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.

  • 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)#

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=(), options=None, compile=False, backend=None)#

Stochastic Gradient Descent (SGD) optimizer using Tensorflow backpropagation.

See tf.keras.Optimizers for a list of the available optimizers.

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.

  • 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.

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)#

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’ = theta * x, where theta is a variational parameter and x an input variable. The PSR allows to calculate the derivative with respect of theta’ but, if we want to optimize a system with respect 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.

Parameters:
  • circuit (qibo.models.circuit.Circuit) – custom quantum circuit.

  • hamiltonian (qibo.hamiltonians.Hamiltonian) – target observable.

  • parameter_index (int) – the index which identifies the target parameter in the circuit.get_parameters() list

  • initial_state ((2**nqubits) vector) – initial state on which the circuit acts (default None).

  • scale_factor (float) – parameter scale factor (default None).

Returns:

np.float value of the derivative of the expectation value of the hamiltonian with respect to the target variational parameter.

Example

import qibo
import numpy as np
from qibo import hamiltonians, gates
from qibo.models import Circuit
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)

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)#

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.

Returns:

list with all Pauli matrices forming the basis.

Return type:

list

Computational basis to Pauli basis#

qibo.quantum_info.comp_basis_to_pauli(nqubits: int, normalize: bool = False)#

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

..math::

U = sum_{k = 0}^{d^{2} - 1} , {|k)}{(P_{k}|} ,, ,

where \({|A)}\) is the system-vectorization of \(A\), \({|k)}\) is the vectorization of the computational basis element \(\ketbra{k}\), and \({|P_{k})}\) is the vectorization of the \(k\)-th Pauli matrix.

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

  • normalize (bool, optional) – If True, converts to the

  • False. (Pauli basis. Defaults to) –

Returns:

Unitary matrix \(U\).

Pauli basis to computational basis#

qibo.quantum_info.pauli_to_comp_basis(nqubits: int, normalize: bool = False)#

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

..math::

U = sum_{k = 0}^{d^{2} - 1} , {|P_{k})}{(b_{k}|} , .

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

  • normalize (bool, optional) – If True, converts to the

  • False. (Pauli basis. Defaults to) –

Returns:

Unitary matrix \(U\).

Metrics#

Set of functions that are used to calculate metrics of states, (pseudo-)distance measures between states, and distance measures between quantum channels.

Purity#

qibo.quantum_info.purity(state)#

Purity of a quantum state \(\rho\), which is given by \(\text{Tr}(\rho^{2})\).

Parameters:

state – state vector or density matrix.

Returns:

Purity of quantum state \(\rho\).

Return type:

float

Entropy#

qibo.quantum_info.entropy(state, base: float = 2, validate: bool = False)#

The von-Neumann entropy \(S(\rho)\) of a quantum state \(\rho\), which is given by

\[S(\rho) = - \text{Tr}\left[\rho \, \log(\rho)\right]\]
Parameters:
  • state – state vector or density matrix.

  • base (float, optional) – the base of the log. Default: 2.

  • validate (bool, optional) – if True, checks if state is Hermitian. If False, it assumes state is Hermitian . Default: False.

Returns:

The von-Neumann entropy \(S(\rho)\).

Return type:

float

Note

validate flag allows the user to choose if the function will check if input state is Hermitian or not. Default option is validate=False, i.e. the assumption of Hermiticity, because it is faster and, more importantly, the functions are intended to be used on Hermitian inputs. When validate=True and state is non-Hermitian, an error will be raised when using cupy backend.

Trace distance#

qibo.quantum_info.trace_distance(state, target, validate: bool = False)#

Trace distance between two quantum states, \(\rho\) and \(\sigma\):

\[T(\rho, \sigma) = \frac{1}{2} \, ||\rho - \sigma||_{1} = \frac{1}{2} \, \text{Tr}\left[ \sqrt{(\rho - \sigma)^{\dagger}(\rho - \sigma)} \right] \, ,\]

where \(||\cdot||_{1}\) is the Schatten 1-norm.

Parameters:
  • state – state vector or density matrix.

  • target – state vector or density matrix.

  • validate (bool, optional) – if True, checks if \(\rho - \sigma\) is Hermitian. If False, it assumes the difference is Hermitian. Default: False.

Returns:

Trace distance between state \(\rho\) and target \(\sigma\).

Return type:

float

Note

validate flag allows the user to choose if the function will check if difference between inputs, state - target, is Hermitian or not. Default option is validate=False, i.e. the assumption of Hermiticity, because it is faster and, more importantly, the functions are intended to be used on Hermitian inputs. When validate=True and state - target is non-Hermitian, an error will be raised when using cupy backend.

Hilbert-Schmidt distance#

qibo.quantum_info.hilbert_schmidt_distance(state, target)#

Hilbert-Schmidt distance between two quantum states:

\[<\rho \, , \, \sigma>_{\text{HS}} = \text{Tr}\left[(\rho - \sigma)^{2}\right]\]
Parameters:
  • state – state vector or density matrix.

  • target – state vector or density matrix.

Returns:

Hilbert-Schmidt distance between state \(\rho\) and target \(\sigma\).

Return type:

float

Fidelity#

qibo.quantum_info.fidelity(state, target, validate: bool = False)#

Fidelity between two quantum states (when at least one state is pure).

\[F(\rho, \sigma) = \text{Tr}^{2}\left( \sqrt{\sqrt{\sigma} \, \rho^{\dagger} \, \sqrt{\sigma}} \right) = \text{Tr}(\rho \, \sigma)\]

where the last equality holds because the target state \(\sigma\) is assumed to be pure.

Parameters:
  • state – state vector or density matrix.

  • target – state vector or density matrix.

  • validate (bool, optional) – if True, checks if one of the input states is pure. Default: False.

Returns:

Fidelity between state \(\rho\) and target \(\sigma\).

Return type:

float

Process fidelity#

qibo.quantum_info.process_fidelity(channel, target=None, validate: bool = False)#

Process fidelity between two quantum channels (when at least one channel is` unitary),

\[F_{pro}(\mathcal{E}, \mathcal{U}) = \frac{1}{d^{2}} \, \text{Tr}(\mathcal{E}^{\dagger} \, \mathcal{U})\]
Parameters:
  • channel – quantum channel.

  • target (optional) – quantum channel. If None, target is the Identity channel. Default: None.

  • validate (bool, optional) – if True, checks if one of the input channels is unitary. Default: False.

Returns:

Process fidelity between channels \(\mathcal{E}\) and target \(\mathcal{U}\).

Return type:

float

Average gate fidelity#

qibo.quantum_info.average_gate_fidelity(channel, target=None)#

Average gate fidelity between two quantum channels (when at least one channel is unitary),

\[F_{\text{avg}}(\mathcal{E}, \mathcal{U}) = \frac{d \, F_{pro}(\mathcal{E}, \mathcal{U}) + 1}{d + 1}\]

where \(d\) is the dimension of the channels and \(F_{pro}(\mathcal{E}, \mathcal{U})\) is the process_fidelily() of channel \(\mathcal{E}\) with respect to the unitary channel \(\mathcal{U}\).

Parameters:
  • channel – quantum channel \(\mathcal{E}\).

  • target (optional) – quantum channel \(\mathcal{U}\). If None, target is the Identity channel. Default: None.

Returns:

Process fidelity between channel \(\mathcal{E}\) and target unitary channel \(\mathcal{U}\).

Return type:

float

Gate error#

qibo.quantum_info.gate_error(channel, target=None)#

Gate error between two quantum channels (when at least one is unitary), which is defined as

\[E(\mathcal{E}, \mathcal{U}) = 1 - F_{\text{avg}}(\mathcal{E}, \mathcal{U}) \, ,\]

where \(F_{\text{avg}}(\mathcal{E}, \mathcal{U})\) is the average_gate_fidelity() between channel \(\mathcal{E}\) and target \(\mathcal{U}\).

Parameters:
  • channel – quantum channel \(\mathcal{E}\).

  • target (optional) – quantum channel \(\mathcal{U}\). If None, target is the Identity channel. Default: None.

Returns:

Gate error between \(\mathcal{E}\) and \(\mathcal{U}\).

Return type:

float

Random Ensembles#

Functions that can generate random quantum objects.

Random Gaussian matrix#

qibo.quantum_info.random_gaussian_matrix(dims: int, rank: Optional[int] = None, mean: float = 0, stddev: float = 1, seed=None)#

Generates a random Gaussian Matrix.

Gaussian matrices are matrices where each entry is sampled from a Gaussian probability distribution

\[p(x) = \frac{1}{\sqrt{2 \, \pi} \, \sigma} \, \exp{\left(-\frac{(x - \mu)^{2}}{2\,\sigma^{2}}\right)}\]

with mean \(\mu\) and standard deviation \(\sigma\).

Parameters:
  • dims (int) – dimension of the matrix.

  • rank (int, optional) – rank of the matrix. If None, then rank == dims. Default: None.

  • mean (float, optional) – mean of the Gaussian distribution. Default is 0.

  • stddev (float, optional) – standard deviation of the Gaussian distribution. Default is 1.

  • 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.

Returns:

Random Gaussian matrix with dimensions (dims, rank).

Return type:

(ndarray)

Random Hermitian matrix#

qibo.quantum_info.random_hermitian(dims: int, semidefinite: bool = False, normalize: bool = False, seed=None)#

Generates a random Hermitian matrix \(H\), i.e. a random matrix such that \(H = H^{\dagger}.\)

Parameters:
  • dims (int) – dimension of the matrix.

  • semidefinite (bool, optional) – if True, returns a Hermitian matrix that is also positive semidefinite. Default: False.

  • normalize (bool, optional) – if True and semidefinite=False, returns a Hermitian matrix with eigenvalues in the interval \([-1, \,1]\). If True and semidefinite=True, interval is \([0,\,1]\). Default: 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.

Returns:

Hermitian matrix \(H\) with dimensions (dims, dims).

Return type:

(ndarray)

Random unitary matrix#

qibo.quantum_info.random_unitary(dims: int, measure: Optional[str] = None, seed=None)#

Returns a random Unitary operator \(U\),, i.e. a random operator such that \(U^{-1} = U^{\dagger}\).

Parameters:
  • dims (int) – dimension of the matrix.

  • measure (str, optional) – probability measure in which to sample the unitary from. If None, functions returns \(\exp{(-i \, H)}\), where \(H\) is a Hermitian operator. If "haar", returns an Unitary matrix sampled from the Haar measure. Default: 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.

Returns:

Unitary matrix \(U\) with dimensions (dims, dims).

Return type:

(ndarray)

Random statevector#

qibo.quantum_info.random_statevector(dims: int, haar: bool = False, seed=None)#

Creates a random statevector \(\ket{\psi}\).

\[\ket{\psi} = \sum_{k = 0}^{d - 1} \, \sqrt{p_{k}} \, e^{i \phi_{k}} \, \ket{k} \, ,\]

where \(d\) is dims, and \(p_{k}\) and \(\phi_{k}\) are, respectively, the probability and phase corresponding to the computational basis state \(\ket{k}\).

Parameters:
  • dims (int) – dimension of the matrix.

  • haar (bool, optional) – if True, statevector is created by sampling a Haar random unitary \(U_{\text{haar}}\) and acting with it on a random computational basis state \(\ket{k}\), i.e. \(\ket{\psi} = U_{\text{haar}} \ket{k}\). Default: 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.

Returns:

Random statevector \(\ket{\psi}\).

Return type:

(ndarray)

Random density matrix#

qibo.quantum_info.random_density_matrix(dims, rank: Optional[int] = None, pure: bool = False, metric: str = 'Hilbert-Schmidt', seed=None)#

Creates a random density matrix \(\rho\).

Parameters:
  • dims (int) – dimension of the matrix.

  • rank (int, optional) – rank of the matrix. If None, then rank == dims. Default: None.

  • pure (bool, optional) – if True, returns a pure state. Default: False.

  • metric (str, optional) – metric to sample the density matrix from. Options: "Hilbert-Schmidt" and "Bures". Default: "Hilbert-Schmidt".

  • 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.

Returns:

Random density matrix \(\rho\).

Return type:

(ndarray)

Random Clifford#

qibo.quantum_info.random_clifford(qubits, return_circuit: bool = False, fuse: bool = False, seed=None)#

Generates random Clifford operator(s).

Parameters:
  • qubits (int or list or ndarray) – if int, the number of qubits for the Clifford. If list or ndarray, indexes of the qubits for the Clifford to act on.

  • return_circuit (bool, optional) – if True, returns a qibo.gates.Unitary object. If False, returns an ndarray object. Default: False.

  • fuse (bool, optional) – if False, returns an ndarray with one Clifford gate per qubit. If True, returns the tensor product of the Clifford gates that were sampled. Default: 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.

Returns:

Random Clifford operator(s).

Return type:

(ndarray or qibo.gates.Unitary)

Random Pauli#

qibo.quantum_info.random_pauli(qubits, depth: int, max_qubits: Optional[int] = None, subset: Optional[list] = None, return_circuit: bool = True, seed=None)#

Creates random Pauli operators.

Pauli operators are sampled from the single-qubit Pauli set \(\{I, X, Y, Z\}\).

Parameters:
  • qubits (int or list or ndarray) – if int and max_qubits=None, the number of qubits. If int and max_qubits != None, qubit index in which the Pauli sequence will act. If list or ndarray, indexes of the qubits for the Pauli sequence to act.

  • depth (int) – length of the sequence of Pauli gates.

  • max_qubits (int, optional) – total number of qubits in the circuit. If None, max_qubits = max(qubits). Default: None.

  • subset (list, optional) – list containing a subset of the 4 single-qubit Pauli operators. If None, defaults to the complete set. Default: None.

  • return_circuit (bool, optional) – if True, returns a qibo.models.Circuit object. If False, returns an ndarray with shape (qubits, depth, 2, 2) that contains all Pauli matrices that were sampled. Default: True.

  • 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.

Returns:

all sampled Pauli operators.

Return type:

(ndarray or qibo.models.Circuit)

Random stochastic matrix#

qibo.quantum_info.random_stochastic_matrix(dims: int, bistochastic: bool = False, precision_tol: Optional[float] = None, max_iterations: Optional[int] = None, seed=None)#

Creates a random stochastic matrix.

Parameters:
  • dims (int) – dimension of the matrix.

  • bistochastic (bool, optional) – if True, matrix is row- and column-stochastic. If False, matrix is row-stochastic. Default: False.

  • precision_tol (float, optional) – tolerance level for how much each probability distribution can deviate from summing up to 1.0. If None, it defaults to qibo.config.PRECISION_TOL. Default: None.

  • max_iterations (int, optional) – when bistochastic=True, maximum number of iterations used to normalize all rows and columns simultaneously. If None, defaults to qibo.config.MAX_ITERATIONS. Default: 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.

Returns:

a random stochastic matrix.

Return type:

(ndarray)

Utility Functions#

Functions that can be used to calculate metrics and distance measures on classical probability arrays.

Shannon entropy#

qibo.quantum_info.shannon_entropy(probability_array, base: float = 2)#

Calculate the Shannon entropy of a probability array \(\mathbf{p}\), which is given by

\[H(\mathbf{p}) = - \sum_{k = 0}^{d^{2} - 1} \, p_{k} \, \log_{b}(p_{k}) \, ,\]

where \(d = \text{dim}(\mathcal{H})\) is the dimension of the Hilbert space \(\mathcal{H}\), \(b\) is the log base (default 2), and \(0 \log_{b}(0) \equiv 0\).

Parameters:
  • probability_array – a probability array \(\mathbf{p}\).

  • base (float) – the base of the log. Default: 2.

Returns:

The Shannon entropy \(H(\mathcal{p})\).

Return type:

(float)

Hellinger distance#

qibo.quantum_info.hellinger_distance(prob_dist_p, prob_dist_q, validate: bool = False)#

Calculate the Hellinger distance \(H(p, q)\) between two discrete probability distributions, \(\mathbf{p}\) and \(\mathbf{q}\). It is defined as

\[H(\mathbf{p} \, , \, \mathbf{q}) = \frac{1}{\sqrt{2}} \, || \sqrt{\mathbf{p}} - \sqrt{\mathbf{q}} ||_{2}\]

where \(||\cdot||_{2}\) is the Euclidean norm.

Parameters:
  • prob_dist_p – (discrete) probability distribution \(p\).

  • prob_dist_q – (discrete) probability distribution \(q\).

  • validate (bool) – if True, checks if \(p\) and \(q\) are proper probability distributions. Default: False.

Returns:

Hellinger distance \(H(p, q)\).

Return type:

(float)

Hellinger fidelity#

qibo.quantum_info.hellinger_fidelity(prob_dist_p, prob_dist_q, validate: bool = False)#

Calculate the Hellinger fidelity between two discrete probability distributions, \(p\) and \(q\). The fidelity is defined as \((1 - H^{2}(p, q))^{2}\), where \(H(p, q)\) is the Hellinger distance.

Parameters:
  • prob_dist_p – (discrete) probability distribution \(p\).

  • prob_dist_q – (discrete) probability distribution \(q\).

  • validate (bool) – if True, checks if \(p\) and \(q\) are proper probability distributions. Default: False.

Returns:

Hellinger fidelity.

Return type:

(float)

Parallelism#

We provide CPU multi-processing methods for circuit evaluation for multiple input states and multiple parameters for fixed input state.

When using the methods below the processes option controls the number of processes used by the parallel algorithms 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.

Resources for parallel circuit evaluation.

qibo.parallel.parallel_execution(circuit, states, processes=None, backend=None)#

Execute circuit for multiple states.

Example

import qibo
original_backend = qibo.get_backend()
qibo.set_backend('qibojit')
from qibo import models, set_threads
from qibo.parallel import parallel_execution
import numpy as np
# create circuit
nqubits = 22
circuit = models.QFT(nqubits)
# create random states
states = [ np.random.random(2**nqubits) for i in range(5)]
# set threads to 1 per process (optional, requires tuning)
set_threads(1)
# execute in parallel
results = parallel_execution(circuit, states, processes=2)
qibo.set_backend(original_backend)
Parameters:
  • circuit (qibo.models.Circuit) – the input circuit.

  • states (list) – list of states for the circuit evaluation.

  • processes (int) – number of processes for parallel evaluation.

Returns:

Circuit evaluation for input states.

qibo.parallel.parallel_parametrized_execution(circuit, parameters, initial_state=None, processes=None, backend=None)#

Execute circuit for multiple parameters and fixed initial_state.

Example

import qibo
original_backend = qibo.get_backend()
qibo.set_backend('qibojit')
from qibo import models, gates, set_threads
from qibo.parallel import parallel_parametrized_execution
import numpy as np
# create circuit
nqubits = 6
nlayers = 2
circuit = models.Circuit(nqubits)
for l in range(nlayers):
    circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
    circuit.add((gates.CZ(q, q+1) for q in range(0, nqubits-1, 2)))
    circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
    circuit.add((gates.CZ(q, q+1) for q in range(1, nqubits-2, 2)))
    circuit.add(gates.CZ(0, nqubits-1))
circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
# create random parameters
size = len(circuit.get_parameters())
parameters = [ np.random.uniform(0, 2*np.pi, size) for _ in range(10) ]
# set threads to 1 per process (optional, requires tuning)
set_threads(1)
# execute in parallel
results = parallel_parametrized_execution(circuit, parameters, processes=2)
qibo.set_backend(original_backend)
Parameters:
  • circuit (qibo.models.Circuit) – the input circuit.

  • parameters (list) – list of parameters for the circuit evaluation.

  • initial_state (np.array) – initial state for the circuit evaluation.

  • processes (int) – number of processes for parallel evaluation.

Returns:

Circuit evaluation for input parameters.

Backends#

The main calculation engine is defined in the abstract backend object qibo.backends.abstract.Backend. This object defines the methods required by all Qibo models to perform simulation.

Qibo currently provides two different calculation backends, one based on numpy and one based on Tensorflow. It is possible to define new backends by inheriting qibo.backends.abstract.Backend and implementing its abstract methods.

An additional backend is shipped as the separate library qibojit. This backend is supplemented by custom operators defined under which can be used to efficiently apply gates to state vectors or density matrices.

We refer to Packages section for a complete list of the available computation backends and instructions on how to install each of these libraries on top of qibo.

Custom operators are much faster than implementations based on numpy or Tensorflow primitives, such as einsum, but do not support some features, such as automatic differentiation for backpropagation of variational circuits which is only supported by the native tensorflow backend.

The user can switch backends using

import qibo
qibo.set_backend("qibojit")
qibo.set_backend("numpy")

before creating any circuits or gates. The default backend is the first available from qibojit, tensorflow, numpy.

Some backends support different platforms. For example, the qibojit backend provides two platforms (cupy and cuquantum) when used on GPU. The active platform can be switched using

import qibo
qibo.set_backend("qibojit", platform="cuquantum")
qibo.set_backend("qibojit", platform="cupy")

The default backend order is qibojit (if available), tensorflow (if available), numpy. The default backend can be changed using the QIBO_BACKEND environment variable.

class qibo.backends.abstract.Backend#
abstract set_precision(precision)#

Set complex number precision.

Parameters:

precision (str) – ‘single’ or ‘double’.

abstract set_device(device)#

Set simulation device.

Parameters:

device (str) – Device such as ‘/CPU:0’, ‘/GPU:0’, etc.

abstract set_threads(nthreads)#

Set number of threads for CPU simulation.

Parameters:

nthreads (int) – Number of threads.

abstract cast(x, copy=False)#

Cast an object as the array type of the current backend.

Parameters:
  • x – Object to cast to array.

  • copy (bool) – If True a copy of the object is created in memory.

abstract issparse(x)#

Determine if a given array is a sparse tensor.

abstract to_numpy(x)#

Cast a given array to numpy.

abstract compile(func)#

Compile the given method.

Available only for the tensorflow backend.

abstract zero_state(nqubits)#

Generate |000…0> state vector as an array.

abstract zero_density_matrix(nqubits)#

Generate |000...0><000...0| density matrix as an array.

abstract plus_state(nqubits)#

Generate |+++…+> state vector as an array.

abstract plus_density_matrix(nqubits)#

Generate |+++...+><+++...+| density matrix as an array.

abstract asmatrix(gate)#

Convert a qibo.gates.Gate to the corresponding matrix.

abstract asmatrix_parametrized(gate)#

Equivalent to qibo.backends.abstract.Backend.asmatrix() for parametrized gates.

abstract asmatrix_fused(gate)#

Fuse matrices of multiple gates.

abstract control_matrix(gate)#

“Calculate full matrix representation of a controlled gate.

abstract apply_gate(gate, state, nqubits)#

Apply a gate to state vector.

abstract apply_gate_density_matrix(gate, state, nqubits)#

Apply a gate to density matrix.

abstract apply_gate_half_density_matrix(gate, state, nqubits)#

Apply a gate to one side of the density matrix.

abstract apply_channel(channel, state, nqubits)#

Apply a channel to state vector.

abstract apply_channel_density_matrix(channel, state, nqubits)#

Apply a channel to density matrix.

abstract collapse_state(state, qubits, shot, nqubits, normalize=True)#

Collapse state vector according to measurement shot.

abstract collapse_density_matrix(state, qubits, shot, nqubits, normalize=True)#

Collapse density matrix according to measurement shot.

abstract reset_error_density_matrix(gate, state, nqubits)#

Apply reset error to density matrix.

abstract thermal_error_density_matrix(gate, state, nqubits)#

Apply thermal relaxation error to density matrix.

abstract execute_circuit(circuit, initial_state=None, nshots=None)#

Execute a qibo.models.circuit.Circuit.

abstract execute_circuit_repeated(circuit, initial_state=None, nshots=None)#

Execute a qibo.models.circuit.Circuit multiple times.

Useful for noise simulation using state vectors or for simulating gates controlled by measurement outcomes.

abstract execute_distributed_circuit(circuit, initial_state=None, nshots=None)#

Execute a qibo.models.circuit.Circuit using multiple GPUs.

abstract circuit_result_representation(result)#

Represent a quantum state based on circuit execution results.

Parameters:

result (qibo.states.CircuitResult) – Result object that contains the data required to represent the state.

abstract circuit_result_tensor(result)#

State vector or density matrix representing a quantum state as an array.

Parameters:

result (qibo.states.CircuitResult) – Result object that contains the data required to represent the state.

abstract circuit_result_probabilities(result, qubits=None)#

Calculates measurement probabilities by tracing out qubits.

Parameters:
  • result (qibo.states.CircuitResult) – Result object that contains the data required to represent the state.

  • qubits (list, set) – Set of qubits that are measured.

abstract calculate_symbolic(state, nqubits, decimals=5, cutoff=1e-10, max_terms=20)#

Dirac representation of a state vector.

abstract calculate_symbolic_density_matrix(state, nqubits, decimals=5, cutoff=1e-10, max_terms=20)#

Dirac representation of a density matrix.

abstract calculate_probabilities(state, qubits, nqubits)#

Calculate probabilities given a state vector.

abstract calculate_probabilities_density_matrix(state, qubits, nqubits)#

Calculate probabilities given a density matrix.

abstract set_seed(seed)#

Set the seed of the random number generator.

abstract sample_shots(probabilities, nshots)#

Sample measurement shots according to a probability distribution.

abstract aggregate_shots(shots)#

Collect shots to a single array.

abstract samples_to_binary(samples, nqubits)#

Convert samples from decimal representation to binary.

abstract samples_to_decimal(samples, nqubits)#

Convert samples from binary representation to decimal.

abstract calculate_frequencies(samples)#

Calculate measurement frequencies from shots.

abstract sample_frequencies(probabilities, nshots)#

Sample measurement frequencies according to a probability distribution.

abstract partial_trace(state, qubits, nqubits)#

Trace out specific qubits of a state vector.

abstract partial_trace_density_matrix(state, qubits, nqubits)#

Trace out specific qubits of a density matrix.

abstract entanglement_entropy(rho)#

Calculate entangelement entropy of a reduced density matrix.

abstract calculate_norm(state)#

Calculate norm of a state vector.

abstract calculate_norm_density_matrix(state)#

Calculate norm (trace) of a density matrix.

abstract calculate_overlap(state1, state2)#

Calculate overlap of two state vectors.

abstract calculate_overlap_density_matrix(state1, state2)#

Calculate norm of two density matrices.

abstract calculate_eigenvalues(matrix, k=6)#

Calculate eigenvalues of a matrix.

abstract calculate_eigenvectors(matrix, k=6)#

Calculate eigenvectors of a matrix.

abstract calculate_matrix_exp(matrix, a, eigenvectors=None, eigenvalues=None)#

Calculate matrix exponential of a matrix.

If the eigenvectors and eigenvalues are given the matrix diagonalization is used for exponentiation.

abstract calculate_expectation_state(hamiltonian, state, normalize)#

Calculate expectation value of a state vector given the observable matrix.

abstract calculate_expectation_density_matrix(hamiltonian, state, normalize)#

Calculate expectation value of a density matrix given the observable matrix.

abstract calculate_hamiltonian_matrix_product(matrix1, matrix2)#

Multiply two matrices.

abstract calculate_hamiltonian_state_product(matrix, state)#

Multiply a matrix to a state vector or density matrix.

abstract test_regressions(name)#

Correct outcomes for tests that involve random numbers.

The outcomes of such tests depend on the backend.