blocks
eigenkit.core.blocks
Blocks: reusable parts of a circuit that do not depend on the provider.
Given the total number of qubits of the circuit, a block builds its gates and
states how many weights and inputs it uses. Blocks are combined with assemble:
from eigenkit.core.blocks import AngleEncoding, BasicVariational, assemble
spec = assemble([AngleEncoding(in_features=3), BasicVariational(depth=2)])
assemble goes through the blocks in order and chooses the total number of
qubits. It shifts the local WeightRef and InputRef indices of each block so
that they point to the right place in the weight and input vectors of the whole
circuit, and it returns a single CircuitSpec. Blocks do not depend on a
provider or on PyTorch, and most of them do not use NumPy. Only the blocks that
check or compute a matrix (QubitUnitary, ControlledUnitaryBlock and
QPEBlock) use NumPy. The
blocks use CircuitBuilder internally, because it makes their code easier to
read.
AmplitudeEncoding
dataclass
Encode a classical vector as the amplitudes of a quantum state.
The vector is padded with zeros to the next power of two. With
normalize=True, the default, it is also rescaled to norm 1. The number of
qubits follows from the length of the vector: it is
max(1, ceil(log2(len(amplitudes)))).
The block adds a single AmplitudeEncoding gate. The data field of the gate
holds the prepared amplitudes as a flat tuple, and each backend loads them with
its own state preparation method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amplitudes
|
object
|
The vector to load, as an array-like. Its length sets the
number of qubits, and it is padded with zeros to the next power of
two. A |
required |
normalize
|
bool
|
If |
True
|
Example
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'AmplitudeEncoding'
|
The rebuilt |
required_qubits()
Return the number of qubits that the padded vector needs.
It is ceil(log2(len(amplitudes))), and at least 1.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
AngleEncoding
dataclass
Encode in_features data values as single-qubit rotations.
Feature i sets the angle of one rotation on qubit i, so the block uses the
qubits 0 to in_features - 1. This matters only when the block is combined
with others. A circuit has as many qubits as its widest block, and when another
block in the list needs more qubits, this block leaves the extra qubits
unchanged.
The block takes in_features inputs and no weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_features
|
int
|
The number of features to encode, one per qubit. It must be
positive, and a |
required |
rotation
|
GateName
|
The rotation gate: |
'RY'
|
scale
|
float
|
A constant factor applied to every input angle. It is useful when
the data is normalised to |
1.0
|
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
The qubits with an index of in_features or more are left unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'AngleEncoding'
|
The rebuilt |
required_qubits()
Return in_features, because the block uses one qubit per feature.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
BasicVariational
dataclass
depth trainable layers of single-qubit rotations and entangling gates.
Each layer applies one trainable rotation to every qubit, then a controlled-NOT
(CNOT) gate from each qubit to the next one. The block takes
depth * n_qubits weights and no inputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
The number of layers. It must be positive, and a |
required |
rotation
|
GateName
|
The rotation gate of the trainable layer: |
'RY'
|
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
The layers act on every qubit of the circuit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'BasicVariational'
|
The rebuilt |
required_qubits()
Return 1, because the block extends to all the qubits of the circuit.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
Block
Bases: Protocol
The interface that every circuit block follows.
A block is a reusable part of a circuit, such as an encoding, a variational
layer or a fixed subroutine. assemble asks each block in turn how many qubits
it needs, gives the circuit as many qubits as the widest block needs, and then
asks every block to build its gates for that number of qubits. Any object with
these two methods works as a block. It does not need to inherit from this
class.
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
required_qubits()
Return the minimum number of qubits that this block needs.
ControlledUnitaryBlock
dataclass
Apply a unitary matrix U to target_wires, controlled by control_wires.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
unitary
|
object
|
A |
required |
control_wires
|
tuple[int, ...]
|
The qubits that control the gate. |
required |
target_wires
|
tuple[int, ...]
|
The qubits that the matrix acts on. |
required |
control_values
|
tuple[int, ...] | None
|
The bit value that activates each control, one for each
entry of |
None
|
Example
import numpy as np
import scipy.linalg
from eigenkit.core.blocks import ControlledUnitaryBlock, assemble
A = np.diag([1.0, 2.0, 3.0, 4.0]) # a Hermitian matrix on two qubits
t = 0.5
U = scipy.linalg.expm(1j * A * t)
block = ControlledUnitaryBlock(U, control_wires=(0,), target_wires=(1, 2))
spec = assemble([block])
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'ControlledUnitaryBlock'
|
The rebuilt |
required_qubits()
Return one more than the highest control or target qubit.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
CustomBlock
dataclass
A block made from the gates of a CircuitBuilder.
CircuitBuilder.to_block() creates it. It is useful when you want to write a
part of a circuit gate by gate and combine it with the standard blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits that the builder was given, which is also the number of qubits that this block needs. It must be positive. |
required |
gates
|
tuple[Gate, ...]
|
The gates that the builder added, in the order in which they are applied. |
required |
n_weights
|
int
|
The number of trainable weights that these gates use. It must not be negative. |
required |
n_inputs
|
int
|
The number of input features that these gates use. It must not be negative. |
required |
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
required_qubits()
Return n_qubits, the number of qubits that the builder was given.
Grover
dataclass
The Grover operator Q = A · S₀ · A† · S_ψ₁ used for amplitude estimation.
This is the operator that iterative quantum amplitude estimation (IQAE) uses.
The block builds it from a state-preparation oracle A, given as a
CircuitSpec, and from these standard parts:
S_ψ₁flips the phase of the ancilla qubit, which is the last qubit. It is the gateZ(n-1).A†is the adjoint of the oracle. It uses the same weights and inputs asA.S₀is the reflection about the all-zero state:X^⊗n → MCZ → X^⊗n, whereMCZis a multi-controlledZgate.Ais the oracle.
The gates are applied to the state in this order, from left to right:
A and A† share their weights and inputs, so
fragment.n_weights == a_circuit.n_weights: the count is not doubled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a_circuit
|
CircuitSpec
|
The state-preparation oracle |
required |
Example
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
The oracle A and its adjoint A† share their weights and inputs, so these
are not counted twice.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'Grover'
|
The rebuilt |
required_qubits()
Return the number of qubits of the oracle.
The operator adds no qubits of its own.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
HamiltonianQPEBlock
dataclass
Quantum phase estimation (QPE) of e^{iAt}, built with Trotter products.
This block replaces QPEBlock in the clock
register of the Harrow-Hassidim-Lloyd (HHL) solver with mode="honest", where
the circuit is built as hardware would run it. It turns the Hermitian matrix
A = Σ_k c_k P_k into elementary gates with Trotter products. It does not
exponentiate a dense oracle and raise it to powers with matrix_power: both of
these cost O(N³) for an N × N matrix, and neither gives a circuit of
elementary gates. The controlled evolution controlled-e^{iA·2^m·t} that a
clock qubit needs is a Trotter product with a total time of 2^m·t, and never
a matrix power. The step size t/steps is fixed, so the power 2^m uses
steps·2^m steps.
Only the single RZ of each Trotter term is controlled. The controlled RZ is
built from RZ and controlled-NOT (CNOT) gates, as CRZ = RZ·CNOT·RZ·CNOT.
The change of basis and the ladder gates cancel out when the control is |0⟩.
The identity term of A becomes a PhaseShift on the clock qubit, which is a
controlled global phase. As a result, the estimated phase reflects the full
eigenvalue and not only its traceless part, which matches what
QPEBlock gives from expm(1j·A·t).
The qubits are laid out as follows, with indices local to the block:
Clock qubit k controls e^{iA·2^(n_clock-1-k)·t}. This is the big-endian
convention of QPE, the same as in QPEBlock.
The caller maps these local indices to the qubits of the full circuit, for
example to skip the HHL ancilla qubit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pauli_terms
|
tuple[tuple[float, str], ...]
|
The |
required |
n_clock
|
int
|
The number of clock qubits, which sets the resolution of QPE. |
required |
time
|
float
|
The base evolution time |
required |
steps
|
int
|
The number of Trotter steps for the base time |
1
|
order
|
int
|
The Trotter order, |
1
|
inverse
|
bool
|
If |
False
|
Example
import numpy as np
from eigenkit.algorithms._hamiltonian_sim import pauli_decompose
from eigenkit.core.blocks import HamiltonianQPEBlock, assemble
A = np.array([[1.0, 0.5], [0.5, -1.0]]) # a Hermitian matrix on one qubit
terms = pauli_decompose(A)
spec = assemble([HamiltonianQPEBlock(terms, n_clock=4, time=1.0, steps=8)])
build(n_qubits)
Build the phase estimation, with Trotter products, for n_qubits qubits.
The qubit indices are local to the block, and the caller maps them to the qubits of the full circuit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'HamiltonianQPEBlock'
|
The rebuilt |
required_qubits()
Return n_clock plus the number of state qubits.
The clock qubits come first, then the state qubits.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
IRFragment
dataclass
The gates that one block adds to a circuit.
The WeightRef and InputRef indices of a fragment are local to it, and they
start at 0. When the blocks are combined into a circuit, assemble shifts
them so that they point to the right place in the weight and input vectors of
the whole circuit.
Attributes:
| Name | Type | Description |
|---|---|---|
gates |
tuple[Gate, ...]
|
The gates of the block, in the order in which they are applied. |
n_weights |
int
|
The number of trainable weights that the block uses. |
n_inputs |
int
|
The number of input features that the block uses. |
QFTBlock
dataclass
Quantum Fourier transform (QFT) on n_qubits qubits.
The transform is built from H, CPHASE and SWAP gates only, which are all
gates that the intermediate representation (IR) of the circuit defines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the transform. It must be positive. |
required |
inverse
|
bool
|
If |
False
|
Example
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'QFTBlock'
|
The rebuilt |
required_qubits()
Return n_qubits, the number of qubits that the transform acts on.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
QPEBlock
dataclass
Quantum phase estimation (QPE) of a unitary oracle.
The block applies H to every clock qubit, then the controlled powers
U^{2^k} of the oracle, then the inverse quantum Fourier transform (QFT†):
H^⊗n_clock → controlled-U^{2^k} → QFT†. Set inverse=True for the adjoint:
QFT → controlled-U†^{2^k} → H^⊗n_clock.
The oracle must be a CircuitSpec whose unitary can be read as a dense matrix.
In practice, this means a circuit made of a single
QubitUnitary gate. Parametrised
oracles made of several gates are not supported. To use an
AmplitudeEncoding or a
ControlledUnitaryBlock as an
oracle, first compute its unitary matrix and wrap it in a
QubitUnitary.
The qubits are laid out as follows, with indices local to the block:
The caller is responsible for mapping these local indices to the qubits of the full circuit, for example to skip the ancilla qubit of the Harrow-Hassidim-Lloyd (HHL) algorithm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
oracle_circuit
|
CircuitSpec
|
A |
required |
n_clock
|
int
|
The number of clock qubits, which sets the resolution of QPE. It
must be at least |
required |
inverse
|
bool
|
If |
False
|
Example
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
The qubit indices are local to the block, and the caller maps them to the qubits of the full circuit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'QPEBlock'
|
The rebuilt |
required_qubits()
Return n_clock plus the number of qubits of the oracle.
The clock qubits come first, then the state qubits.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
QubitUnitary
dataclass
Apply any unitary matrix to the given qubits.
The matrix is stored in the data field of the gate as a nested tuple, so that
the Gate stays hashable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
unitary
|
object
|
A |
required |
wires
|
tuple[int, ...]
|
The target qubits, in the order that the matrix expects. |
required |
Example
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'QubitUnitary'
|
The rebuilt |
required_qubits()
Return max(wires) + 1, one more than the highest target qubit.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
Reuploading
dataclass
Data reuploading: the input is encoded several times, between trainable layers.
The block repeats an encoding of the input followed by a variational layer,
depth times. Each encoding can have a trainable scale for each feature and
repetition.
The arguments give two patterns:
- Standard reuploading (the default; Pérez-Salinas et al. 2020). The same
input is encoded
depthtimes, with new variational weights between the encodings. - Trainable feature scaling (
trainable_scale=True; Schuld et al. 2021). Each encoding rotation takes the angleλ_{d,i} * x_i, where each pair of repetitiondand featureihas its own trainableλ_{d,i}.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_features
|
int
|
The total number of input features. It must be positive. |
required |
depth
|
int
|
The number of times the whole sequence is repeated. It must be positive. |
required |
encoding_rotation
|
GateName
|
The rotation gate of the encoding: |
'RY'
|
variational_rotation
|
GateName
|
The rotation gate of the variational layer: |
'RY'
|
variational_type
|
str
|
|
'basic'
|
trainable_scale
|
bool
|
If |
False
|
Weight count
- With
variational_type="basic"and no trainable scale, the block hasdepth * in_featuresweights. - With
variational_type="strong"and no trainable scale, it has3 * depth * in_featuresweights. trainable_scale=Trueaddsdepth * in_featuresweights.
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'Reuploading'
|
The rebuilt |
required_qubits()
Return in_features: one qubit for each feature.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
S0
dataclass
Reflection about the all-zero state |0…0⟩, built as X^⊗n → MCZ → X^⊗n.
The block applies X to every qubit, then a multi-controlled Z gate (MCZ)
on all of them, then X to every qubit again. It acts on the qubits 0 to
n_qubits - 1, which are all the qubits of the circuit unless another block in
the list needs more. This reflection is the S₀ part of the Grover operator
used by iterative quantum amplitude estimation (IQAE). The
Grover block adds the same gates itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the reflection. Every one of them takes part. It must be positive. |
required |
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
'IRFragment'
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'S0'
|
The rebuilt |
required_qubits()
Return n_qubits, the number of qubits that the reflection acts on.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
StrongVariational
dataclass
depth trainable layers of RZ, RY and RZ rotations and entangling gates.
Each layer applies the rotations RZ, RY and RZ to every qubit, then a
controlled-NOT (CNOT) gate from each qubit to the next one. The block is
modelled on PennyLane's StronglyEntanglingLayers.
The gates are added one layer at a time: all the RZ rotations, then all the
RY rotations, then all the RZ rotations, then the CNOT gates. They are not
added as three rotations per qubit. The result is the same, because rotations
on different qubits commute. The layered order lets the backends that support
it apply each layer of rotations in one fast call, in place of 3 * n_qubits
calls on single qubits.
The block takes 3 * depth * n_qubits weights and no inputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
depth
|
int
|
The number of layers. It must be positive, and a |
required |
build(n_qubits)
Build the gates of this block for a circuit with n_qubits qubits.
The layers act on every qubit of the circuit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'StrongVariational'
|
The rebuilt |
required_qubits()
Return 1, because the block extends to all the qubits of the circuit.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
TrotterBlock
dataclass
First-order or second-order Trotter approximation of e^{iAt}.
The block builds the time evolution under a Hermitian matrix
A = Σ_k c_k P_k, given as (coeff, pauli_string) terms (for example from
pauli_decompose()). It uses elementary gates only (H, RX, controlled-NOT
(CNOT) and RZ), and no dense QubitUnitary. It replaces applying
scipy.linalg.expm(1j*A*t) as a single opaque unitary, and it builds the
evolution as hardware would run it.
Each term e^{i·c·P·τ} is built as follows. Every factor of P that is not
Z is rotated into the eigenbasis of Z (with H for X, and RX(+π/2) for
Y). A ladder of CNOT gates collects the parity of the active qubits on the
last one, a single RZ(-2cτ) is applied there, and then the ladder and the
change of basis are undone. With the convention RZ(θ) = e^{-iθZ/2} of the
library, this gives e^{+iAt} up to a global phase, which matches the sign of
the reference Harrow-Hassidim-Lloyd (HHL) solver. The identity term is only a
global phase, so it is skipped: it has no observable effect here, and it
becomes a phase on the clock qubit in the controlled version used for quantum
phase estimation (QPE).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pauli_terms
|
tuple[tuple[float, str], ...]
|
A sequence of |
required |
time
|
float
|
The evolution time |
required |
steps
|
int
|
The number of Trotter steps. The approximation error decreases as
|
1
|
order
|
int
|
|
1
|
Example
import numpy as np
from eigenkit.algorithms._hamiltonian_sim import pauli_decompose
from eigenkit.core.blocks import TrotterBlock, assemble
A = np.array([[1.0, 0.5], [0.5, -1.0]]) # a Hermitian matrix on one qubit
terms = pauli_decompose(A)
spec = assemble([TrotterBlock(terms, time=1.0, steps=20, order=2)])
build(n_qubits)
Build the Trotter product for a circuit with n_qubits qubits.
The qubit indices are local to the block, and the caller maps them to the qubits of the full circuit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_qubits
|
int
|
The number of qubits of the circuit being assembled, which is the number of qubits of the widest block in the list. |
required |
Returns:
| Type | Description |
|---|---|
IRFragment
|
The gates of the block, as an |
from_dict(d)
classmethod
Rebuild a block from the output of to_dict().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
A dictionary returned by |
required |
Returns:
| Type | Description |
|---|---|
'TrotterBlock'
|
The rebuilt |
required_qubits()
Return the length of the Pauli strings, one qubit for each factor.
to_dict()
Return a description of this block that can be saved as JSON.
When a saved model is loaded, the type key selects the block class whose
from_dict() rebuilds the block.
Returns:
| Type | Description |
|---|---|
dict
|
The fields of the block, and its |
assemble(blocks)
Combine blocks into a single CircuitSpec.
A CircuitBuilder can take the place of a block. It is replaced by the result
of its to_block(), so a part of a circuit written gate by gate can go
directly in the list.
The blocks are combined as follows:
n_qubitsis the largestrequired_qubits()of the blocks.- The weights and the inputs of the blocks are placed one after the other, in the order of the blocks.
- The local
WeightRefandInputRefindices of each block are shifted by the number of weights and inputs of the blocks before it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocks
|
Iterable[Block | CircuitBuilder]
|
The blocks to combine, in the order in which they are applied. Any
of them can be a |
required |
Returns:
| Type | Description |
|---|---|
CircuitSpec
|
One |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |