Skip to content
Pasqal Documentation

Transpilation

Contains functions that operate on blocks and circuits to transpile them to new blocks/circuits.

transpile(*fs: Callable[[AbstractBlock], AbstractBlock]) -> Callable[[AbstractBlock], AbstractBlock]
transpile(*fs: Callable[[QuantumCircuit], QuantumCircuit]) -> Callable[[QuantumCircuit], QuantumCircuit]

AbstractBlock or QuantumCircuit transpilation.

Compose functions that accept a circuit/block and returns a circuit/block.

PARAMETER DESCRIPTION
*fs

composable functions that either map blocks to blocks (Callable[[AbstractBlock], AbstractBlock]) or circuits to circuits (Callable[[QuantumCircuit], QuantumCircuit]).

TYPE: Callable DEFAULT: ()

RETURNS DESCRIPTION
Callable

Composed function.

Examples:

Flatten a block of nested chains and krons:

from qadence import *
from qadence.transpile import transpile, flatten, scale_primitive_blocks_only
b = chain(2 * chain(chain(X(0), Y(0))), kron(kron(X(0), X(1))))
print(b)
# both flatten and scale_primitive_blocks_only are functions that accept and
# return a block
t = transpile(flatten, scale_primitive_blocks_only)(b)
print(t)
ChainBlock(0,1)
├── [mul: 2]
│ └── ChainBlock(0)
│ └── ChainBlock(0)
│ ├── X(0)
│ └── Y(0)
└── KronBlock(0,1)
└── KronBlock(0,1)
├── X(0)
└── X(1)
ChainBlock(0,1)
├── [mul: 2]
│ └── X(0)
├── Y(0)
└── KronBlock(0,1)
├── X(0)
└── X(1)

We also proved a decorator to easily turn a function Callable[[AbstractBlock], AbstractBlock] into a Callable[[QuantumCircuit], QuantumCircuit] to be used in circuit transpilation.

from qadence import *
from qadence.transpile import transpile, blockfn_to_circfn, flatten
# We want to pass this circuit to `transpile` instead of a block,
# so we need functions that map from a circuit to a circuit.
circ = QuantumCircuit(2, chain(chain(X(0), chain(X(1)))))
@blockfn_to_circfn
def fn(block):
# un-decorated function accepts a block and returns a block
return block * block
transp = transpile(
# the decorated function accepts a circuit and returns a circuit
fn,
# already existing functions can also be decorated
blockfn_to_circfn(flatten)
)
print(transp(circ))
ChainBlock(0,1)
├── ChainBlock(0,1)
│ ├── X(0)
│ └── X(1)
└── ChainBlock(0,1)
├── X(0)
└── X(1)
Source code in qadence/transpile/transpile.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79def transpile(*fs: Callable) -> Callable:
"""`AbstractBlock` or `QuantumCircuit` transpilation.
Compose functions that
accept a circuit/block and returns a circuit/block.
Arguments:
*fs: composable functions that either map blocks to blocks
(`Callable[[AbstractBlock], AbstractBlock]`)
or circuits to circuits (`Callable[[QuantumCircuit], QuantumCircuit]`).
Returns:
Composed function.
Examples:
Flatten a block of nested chains and krons:
```python exec="on" source="material-block" result="json"
from qadence import *
from qadence.transpile import transpile, flatten, scale_primitive_blocks_only
b = chain(2 * chain(chain(X(0), Y(0))), kron(kron(X(0), X(1))))
print(b)
print() # markdown-exec: hide
# both flatten and scale_primitive_blocks_only are functions that accept and
# return a block
t = transpile(flatten, scale_primitive_blocks_only)(b)
print(t)
```
We also proved a decorator to easily turn a function `Callable[[AbstractBlock], AbstractBlock]`
into a `Callable[[QuantumCircuit], QuantumCircuit]` to be used in circuit transpilation.
```python exec="on" source="material-block" result="json"
from qadence import *
from qadence.transpile import transpile, blockfn_to_circfn, flatten
# We want to pass this circuit to `transpile` instead of a block,
# so we need functions that map from a circuit to a circuit.
circ = QuantumCircuit(2, chain(chain(X(0), chain(X(1)))))
@blockfn_to_circfn
def fn(block):
# un-decorated function accepts a block and returns a block
return block * block
transp = transpile(
# the decorated function accepts a circuit and returns a circuit
fn,
# already existing functions can also be decorated
blockfn_to_circfn(flatten)
)
print(transp(circ))
```
"""
return lambda x: reduce(lambda acc, f: f(acc), reversed(fs), x)

Transpile a chain of krons into a kron of chains of single qubit operations.

Examples:

from qadence import hea
from qadence.transpile.block import chain_single_qubit_ops
# Consider a single HEA layer
block = hea(2,1)
print(block)
# After applying chain_single_qubit_ops, we get:
print(chain_single_qubit_ops(block))
ChainBlock(0,1) [tag: HEA]
├── ChainBlock(0,1)
│ ├── KronBlock(0,1)
│ │ ├── RX(0) [params: ['theta_0']]
│ │ └── RX(1) [params: ['theta_1']]
│ ├── KronBlock(0,1)
│ │ ├── RY(0) [params: ['theta_2']]
│ │ └── RY(1) [params: ['theta_3']]
│ └── KronBlock(0,1)
│ ├── RX(0) [params: ['theta_4']]
│ └── RX(1) [params: ['theta_5']]
└── ChainBlock(0,1)
└── KronBlock(0,1)
└── CNOT(0, 1)
ChainBlock(0,1)
├── KronBlock(0,1)
│ ├── ChainBlock(0)
│ │ ├── RX(0) [params: ['theta_0']]
│ │ ├── RY(0) [params: ['theta_2']]
│ │ └── RX(0) [params: ['theta_4']]
│ └── ChainBlock(1)
│ ├── RX(1) [params: ['theta_1']]
│ ├── RY(1) [params: ['theta_3']]
│ └── RX(1) [params: ['theta_5']]
└── ChainBlock(0,1)
└── KronBlock(0,1)
└── CNOT(0, 1)
Source code in qadence/transpile/block.py
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417def chain_single_qubit_ops(block: AbstractBlock) -> AbstractBlock:
"""Transpile a chain of krons into a kron of chains of single qubit operations.
Examples:
```python exec="on" source="above" result="json"
from qadence import hea
from qadence.transpile.block import chain_single_qubit_ops
# Consider a single HEA layer
block = hea(2,1)
print(block)
# After applying chain_single_qubit_ops, we get:
print(chain_single_qubit_ops(block))
```
"""
if is_chain_of_primitivekrons(block):
try:
return kron(*map(lambda bs: chain(*bs), zip(*block))) # type: ignore[misc]
except Exception as e:
logger.debug(
f"Unable to transpile {block} using chain_single_qubit_ops\
due to {e}. Returning original circuit."
)
return block
elif isinstance(block, CompositeBlock):
return _construct(type(block), tuple(chain_single_qubit_ops(b) for b in block.blocks))
else:
return block

scale_primitive_blocks_only(block, scale=None)

Section titled “ scale_primitive_blocks_only(block, scale=None) ”

Push the scale all the way down into the leaves of the block tree.

When given a scaled CompositeBlock consisting of several PrimitiveBlocks.

PARAMETER DESCRIPTION
block

The block to be transpiled.

TYPE: AbstractBlock

scale

An optional scale parameter. Only to be used for recursive calls internally.

TYPE: Basic DEFAULT: None

RETURNS DESCRIPTION
AbstractBlock

A block of the same type where the scales have been moved into the subblocks.

TYPE: AbstractBlock

Examples:

There are two different cases: ChainBlocks/KronBlocks: Only the first subblock needs to be scaled because chains/krons represent multiplications.

from qadence import chain, X, RX
from qadence.transpile import scale_primitive_blocks_only
b = 2 * chain(X(0), RX(0, "theta"))
print(b)
# After applying scale_primitive_blocks_only
print(scale_primitive_blocks_only(b))
[mul: 2]
└── ChainBlock(0)
├── X(0)
└── RX(0) [params: ['theta']]
ChainBlock(0)
├── [mul: 2]
│ └── X(0)
└── RX(0) [params: ['theta']]

AddBlocks: Consider 2 * add(X(0), RX(0, "theta")). The scale needs to be added to all subblocks. We get add(2 * X(0), 2 * RX(0, "theta")).

from qadence import add, X, RX
from qadence.transpile import scale_primitive_blocks_only
b = 2 * add(X(0), RX(0, "theta"))
print(b)
# After applying scale_primitive_blocks_only
print(scale_primitive_blocks_only(b))
[mul: 2]
└── AddBlock(0)
├── X(0)
└── RX(0) [params: ['theta']]
AddBlock(0)
├── [mul: 2]
│ └── X(0)
└── [mul: 2]
└── RX(0) [params: ['theta']]
Source code in qadence/transpile/block.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223@singledispatch
def scale_primitive_blocks_only(block: AbstractBlock, scale: sympy.Basic = None) -> AbstractBlock:
"""Push the scale all the way down into the leaves of the block tree.
When given a scaled CompositeBlock consisting of several PrimitiveBlocks.
Arguments:
block: The block to be transpiled.
scale: An optional scale parameter. Only to be used for recursive calls internally.
Returns:
AbstractBlock: A block of the same type where the scales have been moved into the subblocks.
Examples:
There are two different cases:
`ChainBlock`s/`KronBlock`s: Only the first subblock needs to be scaled because chains/krons
represent multiplications.
```python exec="on" source="above" result="json"
from qadence import chain, X, RX
from qadence.transpile import scale_primitive_blocks_only
b = 2 * chain(X(0), RX(0, "theta"))
print(b)
# After applying scale_primitive_blocks_only
print(scale_primitive_blocks_only(b))
```
`AddBlock`s: Consider 2 * add(X(0), RX(0, "theta")). The scale needs to be added to all
subblocks. We get add(2 * X(0), 2 * RX(0, "theta")).
```python exec="on" source="above" result="json"
from qadence import add, X, RX
from qadence.transpile import scale_primitive_blocks_only
b = 2 * add(X(0), RX(0, "theta"))
print(b)
# After applying scale_primitive_blocks_only
print(scale_primitive_blocks_only(b))
```
"""
raise NotImplementedError(f"scale_primitive_blocks_only is not implemented for {type(block)}")

set_as_fixed(blocks, restricted_names=list(), inplace=True)

Section titled “ set_as_fixed(blocks, restricted_names=list(), inplace=True) ”

Set parameters in blocks as fixed (non-trainable parameters).

PARAMETER DESCRIPTION
blocks

Block or list of blocks for which to set the trainable attribute

TYPE: AbstractBlock | list[AbstractBlock]

restricted_names

Restricted list of parameters names to set the value.

TYPE: list[str] DEFAULT: list()

inplace

Whether to modify the block(s) in place or not. Currently, only

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
NotImplementedError

if the inplace argument is set to False, the function will raise this exception

RETURNS DESCRIPTION
AbstractBlock | list[AbstractBlock]

AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable attribute set to False

Source code in qadence/transpile/block.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129def set_as_fixed(
blocks: AbstractBlock | list[AbstractBlock],
restricted_names: list[str] = list(),
inplace: bool = True,
) -> AbstractBlock | list[AbstractBlock]:
"""Set parameters in blocks as fixed (non-trainable parameters).
Args:
blocks (AbstractBlock | list[AbstractBlock]): Block or list of blocks for which
to set the trainable attribute
restricted_names (list[str]): Restricted list of parameters names to set the value.
inplace (bool, optional): Whether to modify the block(s) in place or not. Currently, only
Raises:
NotImplementedError: if the `inplace` argument is set to False, the function will
raise this exception
Returns:
AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable
attribute set to False
"""
return set_trainable(blocks, restricted_names=restricted_names, value=False, inplace=inplace)

set_as_variational(blocks, restricted_names=list(), inplace=True)

Section titled “ set_as_variational(blocks, restricted_names=list(), inplace=True) ”

Set parameters in blocks as variational (trainable parameters).

PARAMETER DESCRIPTION
blocks

Block or list of blocks for which to set the trainable attribute

TYPE: AbstractBlock | list[AbstractBlock]

restricted_names

Restricted list of parameters names to set the value.

TYPE: list[str] DEFAULT: list()

inplace

Whether to modify the block(s) in place or not. Currently, only

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
NotImplementedError

if the inplace argument is set to False, the function will raise this exception

RETURNS DESCRIPTION
AbstractBlock | list[AbstractBlock]

AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable attribute set to True

Source code in qadence/transpile/block.py
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105def set_as_variational(
blocks: AbstractBlock | list[AbstractBlock],
restricted_names: list[str] = list(),
inplace: bool = True,
) -> AbstractBlock | list[AbstractBlock]:
"""Set parameters in blocks as variational (trainable parameters).
Args:
blocks (AbstractBlock | list[AbstractBlock]): Block or list of blocks for which
to set the trainable attribute
restricted_names (list[str]): Restricted list of parameters names to set the value.
inplace (bool, optional): Whether to modify the block(s) in place or not. Currently, only
Raises:
NotImplementedError: if the `inplace` argument is set to False, the function will
raise this exception
Returns:
AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable
attribute set to True
"""
return set_trainable(blocks, restricted_names=restricted_names, inplace=inplace)

set_trainable(blocks, restricted_names=list(), value=True, inplace=True)

Section titled “ set_trainable(blocks, restricted_names=list(), value=True, inplace=True) ”

Set the trainability of all parameters in a block to a given value.

PARAMETER DESCRIPTION
blocks

Block or list of blocks for which to set the trainable attribute

TYPE: AbstractBlock | list[AbstractBlock]

restricted_names

Restricted list of parameters names to set the value.

TYPE: list[str] DEFAULT: list()

value

The value of the trainable attribute to assign to the input blocks

TYPE: bool DEFAULT: True

inplace

Whether to modify the block(s) in place or not. Currently, only

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
NotImplementedError

if the inplace argument is set to False, the function will raise this exception

RETURNS DESCRIPTION
AbstractBlock | list[AbstractBlock]

AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable attribute set to the given value

Source code in qadence/transpile/block.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81def set_trainable(
blocks: AbstractBlock | list[AbstractBlock],
restricted_names: list[str] = list(),
value: bool = True,
inplace: bool = True,
) -> AbstractBlock | list[AbstractBlock]:
"""Set the trainability of all parameters in a block to a given value.
Args:
blocks (AbstractBlock | list[AbstractBlock]): Block or list of blocks for which
to set the trainable attribute
restricted_names (list[str]): Restricted list of parameters names to set the value.
value (bool, optional): The value of the trainable attribute to assign to the input blocks
inplace (bool, optional): Whether to modify the block(s) in place or not. Currently, only
Raises:
NotImplementedError: if the `inplace` argument is set to False, the function will
raise this exception
Returns:
AbstractBlock | list[AbstractBlock]: the input block or list of blocks with the trainable
attribute set to the given value
"""
if isinstance(blocks, AbstractBlock):
blocks = [blocks]
if inplace:
for block in blocks:
params: list[sympy.Basic] = list(filter(lambda p: not p.is_number, parameters(block)))
if bool(restricted_names):
params = list(filter(lambda p: p.name in restricted_names, params))
for p in params:
p.trainable = value
else:
raise NotImplementedError("Not inplace set_trainable is not yet available")
return blocks if len(blocks) > 1 else blocks[0]

Moves a block from global to local qubit numbers by adding PutBlocks.

Reassigns qubit locations appropriately.

from qadence.blocks import chain
from qadence.operations import X
from qadence.transpile import validate
x = chain(chain(X(0)), chain(X(1)))
print(x)
print(validate(x))
ChainBlock(0,1)
├── ChainBlock(0)
│ └── X(0)
└── ChainBlock(1)
└── X(1)
ChainBlock(0,1)
├── put on (0)
│ └── ChainBlock(0)
│ └── put on (0)
│ └── X(0)
└── put on (1)
└── ChainBlock(0)
└── put on (0)
└── X(0)
Source code in qadence/transpile/block.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182def validate(block: AbstractBlock) -> AbstractBlock:
"""Moves a block from global to local qubit numbers by adding PutBlocks.
Reassigns qubit locations appropriately.
# Example
```python exec="on" source="above" result="json"
from qadence.blocks import chain
from qadence.operations import X
from qadence.transpile import validate
x = chain(chain(X(0)), chain(X(1)))
print(x)
print(validate(x))
```
"""
vblock: AbstractBlock
from qadence.transpile import reassign
if isinstance(block, ControlBlock):
vblock = deepcopy(block)
b: AbstractBlock
(b,) = block.blocks
b = reassign(b, {i: i - min(b.qubit_support) for i in b.qubit_support})
b = validate(b)
vblock.blocks = (b,) # type: ignore[assignment]
elif isinstance(block, CompositeBlock):
blocks = []
for b in block.blocks:
mi, ma = min(b.qubit_support), max(b.qubit_support)
nb = reassign(b, {i: i - min(b.qubit_support) for i in b.qubit_support})
nb = validate(nb)
nb = PutBlock(nb, tuple(range(mi, ma + 1)))
blocks.append(nb)
try:
vblock = _construct(type(block), tuple(blocks))
except AssertionError as e:
if str(e) == "Make sure blocks act on distinct qubits!":
vblock = chain(*blocks)
else:
raise e
elif isinstance(block, PrimitiveBlock):
vblock = deepcopy(block)
else:
raise NotImplementedError
vblock.tag = block.tag
return vblock