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
.
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: AnalogComposite
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: 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
_controlled_block_with_params(block)
Redefines parameterized/non-parameterized controlled block.
PARAMETER | DESCRIPTION |
---|---|
block
|
original controlled rotation block
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
AbstractBlock
|
redefined controlled rotation block
TYPE:
|
dict[str, Tensor]
|
dict with new parameters which are added |
Source code in qadence/blocks/block_to_tensor.py
_fill_identities(block_mat, qubit_support, full_qubit_support, diag_only=False, endianness=Endianness.BIG, device=None)
Returns a Kronecker product of a block matrix with identities.
The block matrix can defined on a subset of qubits and the full matrix is filled with identities acting on the unused qubits.
PARAMETER | DESCRIPTION |
---|---|
block_mat
|
matrix of an arbitrary gate
TYPE:
|
qubit_support
|
qubit support of
TYPE:
|
full_qubit_support
|
full qubit support of the circuit
TYPE:
|
diag_only
|
Use diagonals only
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
Tensor
|
torch.Tensor: augmented matrix with dimensions (2nqubits, 2nqubits) |
Tensor
|
or a tensor (2**n_qubits) if diag_only |
Source code in qadence/blocks/block_to_tensor.py
_phase_matrix(theta)
Args:
theta(torch.Tensor): input parameter
Returns: torch.Tensor: a batch of gates after applying theta
Source code in qadence/blocks/block_to_tensor.py
_rot_matrices(theta, generator)
Args:
theta(torch.Tensor): input parameter
generator(torch.Tensor): the tensor of the generator
Returns: torch.Tensor: a batch of gates after applying theta
Source code in qadence/blocks/block_to_tensor.py
_swap_block(block)
Redefines SWAP block.
PARAMETER | DESCRIPTION |
---|---|
block
|
original SWAP block
TYPE:
|
RETURNS | DESCRIPTION |
---|---|
AbstractBlock
|
redefined SWAP block
TYPE:
|
Source code in qadence/blocks/block_to_tensor.py
_u_matrix(theta)
Args:
theta(tuple[torch.Tensor]): tuple of torch Tensor with 3 elements
per each parameter of the arbitrary rotation
Returns: torch.Tensor: matrix corresponding to the U gate after applying theta
Source code in qadence/blocks/block_to_tensor.py
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 |
---|---|
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.4351+4.2992e-02j, -0.6912-4.9491e-01j, -0.2856-6.6079e-02j,
-0.0100-1.1552e-02j],
[ 0.5522-3.8415e-01j, 0.4327+1.2980e-01j, -0.3996-3.3275e-01j,
-0.2587-7.7870e-02j],
[ 0.0706-5.1515e-01j, -0.0580-2.6393e-01j, 0.6698-9.0935e-02j,
-0.1004-4.3543e-01j],
[-0.0835-2.8112e-01j, -0.0134+3.5530e-04j, -0.0728-4.2592e-01j,
0.8465+1.0240e-01j]]], 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)