Block system
qadence offers a block-based system to construct quantum circuits in a flexible manner.
AbstractBlock(tag=None, __array_priority__=1000)
dataclass
Bases: ABC
Base class for both primitive and composite blocks.
| ATTRIBUTE | DESCRIPTION |
|---|---|
name |
A human-readable name attached to the block type. Notice, this is the same for all the class instances so it cannot be used for identifying different blocks
TYPE:
|
qubit_support |
The qubit support of the block expressed as a tuple of integers
TYPE:
|
tag |
A tag identifying a particular instance of the block which can be used for identification and pretty printing
TYPE:
|
eigenvalues |
The eigenvalues of the matrix representing the block. This is used mainly for primitive blocks and it's needed for generalized parameter shift rule computations. Currently unused.
TYPE:
|
is_identity
property
Identity predicate for blocks.
n_qubits()
The number of qubits in the whole system.
A block acting on qubit N would has at least n_qubits >= N + 1.
n_supports()
qubit_support()
The indices of the qubit(s) the block is acting on.
Qadence uses the ordering [0..,N-1] for qubits.
Primitive blocks
ControlBlock(control, target_block, noise=None)
Bases: PrimitiveBlock
The abstract ControlBlock.
Source code in qadence/blocks/primitive.py
ParametricBlock(qubit_support, noise=None)
dataclass
Bases: PrimitiveBlock
Parameterized primitive blocks.
Source code in qadence/blocks/primitive.py
num_parameters()
abstractmethod
The number of parameters required by the block.
This is a class property since the number of parameters is defined automatically before instantiating the operation. Also, this could correspond to a larger number of actual user-facing parameters since any parameter expression is allowed
Examples: - RX operation has 1 parameter - U operation has 3 parameters - HamEvo has 2 parameters (generator and time evolution)
Source code in qadence/blocks/primitive.py
ParametricControlBlock(control, target_block, noise=None)
Bases: ParametricBlock
The abstract parametrized ControlBlock.
Source code in qadence/blocks/primitive.py
PrimitiveBlock(qubit_support, noise=None)
Bases: AbstractBlock
Primitive blocks represent elementary unitary operations.
Examples are single/multi-qubit gates or Hamiltonian evolution.
See qadence.operations for a full list of
primitive blocks.
Source code in qadence/blocks/primitive.py
digital_decomposition()
Decomposition into purely digital gates.
This method returns a decomposition of the Block in a combination of purely digital single-qubit and two-qubit 'gates', by manual/custom knowledge of how this can be done efficiently. :return:
Source code in qadence/blocks/primitive.py
ProjectorBlock(ket, bra, qubit_support, noise=None)
Bases: PrimitiveBlock
The abstract ProjectorBlock.
Arguments:
ket (str): The ket given as a bitstring.
bra (str): The bra given as a bitstring.
qubit_support (int | tuple[int]): The qubit_support of the block.
Source code in qadence/blocks/primitive.py
ScaleBlock(block, parameter)
Bases: ParametricBlock
Scale blocks are created when multiplying a block by a number or parameter.
Example:
Source code in qadence/blocks/primitive.py
TimeEvolutionBlock(qubit_support, noise=None)
dataclass
Bases: ParametricBlock
Simple time evolution block with time-independent Hamiltonian.
This class is just a convenience class which is used to label blocks which contains simple time evolution with time-independent Hamiltonian operators
Source code in qadence/blocks/primitive.py
Analog blocks
To learn how to use analog blocks and how to mix digital & analog blocks, check out the digital-analog section of the documentation.
Examples on how to use digital-analog blocks can be found in the *examples folder of the qadence repo:
- Fit a simple sinus:
examples/digital-analog/fit-sin.py - Solve a QUBO:
examples/digital-analog/qubo.py
AnalogChain(blocks)
dataclass
Bases: AnalogComposite
A chain of analog blocks.
Needed because analog blocks require
stricter validation than the general ChainBlock.
AnalogChains can only be constructed from AnalogKron blocks or
globally supported, primitive, analog blocks (like InteractionBlocks and
ConstantAnalogRotations).
Automatically constructed by the chain
function if only analog blocks are given.
Example:
from qadence import X, chain, AnalogInteraction
b = chain(AnalogInteraction(200), AnalogInteraction(200))
print(type(b)) # this is an `AnalogChain`
b = chain(X(0), AnalogInteraction(200))
print(type(b)) # this is a general `ChainBlock`
Source code in qadence/blocks/analog.py
AnalogKron(blocks, interaction=Interaction.NN)
dataclass
Bases: AnalogComposite
Stack analog blocks vertically (i.e. in time).
Needed because analog require
stricter validation than the general KronBlock.
AnalogKrons can only be constructed from non-global, analog blocks
with the same duration.
Source code in qadence/blocks/analog.py
ConstantAnalogRotation(tag=None, __array_priority__=1000, _eigenvalues_generator=None, parameters=ParamMap(alpha=0.0, duration=1000.0, omega=0.0, delta=0.0, phase=0.0), qubit_support=QubitSupport('global'), add_pattern=True)
dataclass
Bases: AnalogBlock
Implements a constant analog rotation with interaction dictated by the chosen Hamiltonian.
H/h = ∑ᵢ(Ω/2 cos(φ)*Xᵢ - sin(φ)*Yᵢ - δnᵢ) + Hᵢₙₜ.
To construct this block you can use of the following convenience wrappers:
- The general rotation operation AnalogRot
- Shorthands for rotatins around an axis:
AnalogRX,
AnalogRY,
AnalogRZ
WARNING: do not use ConstantAnalogRotation with alpha as differentiable parameter - use
the convenience wrappers mentioned above.
InteractionBlock(tag=None, __array_priority__=1000, _eigenvalues_generator=None, parameters=ParamMap(duration=1000.0), qubit_support=QubitSupport('global'), add_pattern=True)
dataclass
Bases: AnalogBlock
Free-evolution for the Hamiltonian interaction term of a register of qubits.
In real interacting quantum devices, it means letting the system evolve freely according to the time-dependent Schrodinger equation. With emulators, this block is translated to an appropriate interaction Hamiltonian, for example, an Ising interaction
Hᵢₙₜ = ∑ᵢⱼ C₆/rᵢⱼ⁶ nᵢnⱼ
or an XY-interaction
Hᵢₙₜ = ∑ᵢⱼ C₃/rⱼⱼ³ (XᵢXⱼ + ZᵢZⱼ)
with nᵢ = (1-Zᵢ)/2.
To construct, use the AnalogInteraction function.
Composite blocks
chain(*args)
Chain blocks sequentially.
On digital backends this can be interpreted loosely as a matrix mutliplication of blocks. In the analog case it chains blocks in time.
| PARAMETER | DESCRIPTION |
|---|---|
*args
|
Blocks to chain. Can also be a generator.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ChainBlock
|
ChainBlock |
Example:
from qadence import X, Y, chain
b = chain(X(0), Y(0))
# or use a generator
b = chain(X(i) for i in range(3))
print(b)
Source code in qadence/blocks/utils.py
kron(*args)
Stack blocks vertically.
On digital backends this can be intepreted loosely as a kronecker product of blocks. In the analog case it executes blocks parallel in time.
| PARAMETER | DESCRIPTION |
|---|---|
*args
|
Blocks to kron. Can also be a generator.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
KronBlock
|
KronBlock |
Example:
from qadence import X, Y, kron
b = kron(X(0), Y(1))
# or use a generator
b = kron(X(i) for i in range(3))
print(b)
Source code in qadence/blocks/utils.py
add(*args)
Sums blocks.
| PARAMETER | DESCRIPTION |
|---|---|
*args
|
Blocks to add. Can also be a generator.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AddBlock
|
AddBlock |
Example:
from qadence import X, Y, add
b = add(X(0), Y(0))
# or use a generator
b = add(X(i) for i in range(3))
print(b)
Source code in qadence/blocks/utils.py
AddBlock(blocks)
ChainBlock(blocks)
Bases: CompositeBlock
Chains blocks sequentially.
Constructed via chain
Source code in qadence/blocks/composite.py
CompositeBlock(tag=None, __array_priority__=1000)
dataclass
Bases: AbstractBlock
Block which composes multiple blocks into one larger block (which can again be composed).
Composite blocks are constructed via chain,
kron, and add.
KronBlock(blocks)
Bases: CompositeBlock
Stacks blocks horizontally.
Constructed via kron.
Source code in qadence/blocks/composite.py
Converting blocks to matrices
block_to_tensor(block, values={}, qubit_support=None, use_full_support=False, tensor_type=TensorType.DENSE, endianness=Endianness.BIG, device=None)
Convert a block into a torch tensor.
| PARAMETER | DESCRIPTION |
|---|---|
block
|
The block to convert.
TYPE:
|
values
|
A optional dict with values for parameters.
TYPE:
|
qubit_support
|
The qubit_support of the block.
TYPE:
|
use_full_support
|
True infers the total number of qubits.
TYPE:
|
tensor_type
|
the target tensor type.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
A torch.Tensor. |
Examples:
from qadence import hea, hamiltonian_factory, Z, block_to_tensor
block = hea(2,2)
print(block_to_tensor(block))
# In case you have a diagonal observable, you can use
obs = hamiltonian_factory(2, detuning = Z)
print(block_to_tensor(obs, tensor_type="SparseDiagonal"))
tensor([[[ 0.3554+0.3119j, -0.2412-0.5711j, -0.3515+0.0019j, -0.4690+0.2204j],
[-0.0097-0.4932j, 0.2821+0.3119j, -0.6343+0.0848j, -0.4093-0.0528j],
[-0.0809-0.5268j, -0.2912-0.4593j, 0.2103+0.2089j, -0.1240-0.5630j],
[-0.3588-0.3468j, -0.0729-0.3711j, -0.2612-0.5576j, 0.3390+0.3376j]]],
grad_fn=<UnsafeViewBackward0>)
tensor(indices=tensor([[0, 3],
[0, 3]]),
values=tensor([ 2.+0.j, -2.+0.j]),
size=(4, 4), nnz=2, layout=torch.sparse_coo)