Transpilation
Contains functions that operate on blocks and circuits to transpile them to new blocks/circuits.
transpile(*fs)
Section titled “
transpile(*fs)
”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
(
TYPE:
|
| 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 blockt = 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_circfndef 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
2425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879def 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)
chain_single_qubit_ops(block)
Section titled “
chain_single_qubit_ops(block)
”Transpile a chain of krons into a kron of chains of single qubit operations.
Examples:
from qadence import heafrom qadence.transpile.block import chain_single_qubit_ops
# Consider a single HEA layerblock = 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
388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417def 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:
|
scale
|
An optional scale parameter. Only to be used for recursive calls internally.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AbstractBlock
|
A block of the same type where the scales have been moved into the subblocks.
TYPE:
|
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, RXfrom qadence.transpile import scale_primitive_blocks_onlyb = 2 * chain(X(0), RX(0, "theta"))print(b)# After applying scale_primitive_blocks_onlyprint(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, RXfrom qadence.transpile import scale_primitive_blocks_onlyb = 2 * add(X(0), RX(0, "theta"))print(b)# After applying scale_primitive_blocks_onlyprint(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
185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223@singledispatchdef 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:
|
restricted_names
|
Restricted list of parameters names to set the value.
TYPE:
|
inplace
|
Whether to modify the block(s) in place or not. Currently, only
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
if the |
| 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
108109110111112113114115116117118119120121122123124125126127128129def 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:
|
restricted_names
|
Restricted list of parameters names to set the value.
TYPE:
|
inplace
|
Whether to modify the block(s) in place or not. Currently, only
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
if the |
| 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 99100101102103104105def 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:
|
restricted_names
|
Restricted list of parameters names to set the value.
TYPE:
|
value
|
The value of the trainable attribute to assign to the input blocks
TYPE:
|
inplace
|
Whether to modify the block(s) in place or not. Currently, only
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
if the |
| 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
4445464748495051525354555657585960616263646566676869707172737475767778798081def 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]
validate(block)
Section titled “
validate(block)
”Moves a block from global to local qubit numbers by adding PutBlocks.
Reassigns qubit locations appropriately.
Example
Section titled “Example”from qadence.blocks import chainfrom qadence.operations import Xfrom 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
132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182def 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