Block system
qadence
offers a block-based system to construct quantum circuits in a flexible manner.
AbstractBlock(tag=None, __array_priority__=1000)
dataclass
Bases:
Base class for both primitive and composite blocks.
ATTRIBUTE | DESCRIPTION |
---|---|
|
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:
|
|
The qubit support of the block expressed as a tuple of integers
TYPE:
|
|
A tag identifying a particular instance of the block which can be used for identification and pretty printing
TYPE:
|
|
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: bool
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:
The abstract ControlBlock.
Source code in qadence/blocks/primitive.py
ParametricBlock(qubit_support, noise=None)
dataclass
Bases:
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:
The abstract parametrized ControlBlock.
Source code in qadence/blocks/primitive.py
PrimitiveBlock(qubit_support, noise=None)
Bases:
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:
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:
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:
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:
A chain of analog blocks.
Needed because analog blocks require
stricter validation than the general ChainBlock
.
AnalogChain
s can only be constructed from AnalogKron
blocks or
globally supported, primitive, analog blocks (like InteractionBlock
s and
ConstantAnalogRotation
s).
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:
Stack analog blocks vertically (i.e. in time).
Needed because analog require
stricter validation than the general KronBlock
.
AnalogKron
s 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:
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:
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 |
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 |
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 |
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)
CompositeBlock(tag=None, __array_priority__=1000)
dataclass
KronBlock(blocks)
Bases:
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=True, 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 |
---|---|
|
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.1263+0.3718j, -0.2890-0.5904j, -0.5422-0.1020j, -0.2209+0.2461j],
[-0.0461-0.4404j, -0.0527+0.4613j, -0.5867-0.0333j, -0.4894-0.0591j],
[-0.3529-0.4149j, -0.3802-0.3835j, 0.2421+0.4150j, -0.1862-0.3824j],
[-0.3779-0.4585j, -0.2464+0.0058j, -0.0836-0.3354j, 0.5551+0.3983j]]],
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)