HierarQcal Core¶
This module contains the core classes for the hierarqcal package. Qmotif
is the base class for all primitives, it’s a directed graph functioning as the building block for higher level motifs. Qhierarchy
is the full compute graph / architecture of the quantum circuit, it manages the interaction between motifs, their execution and symbol distribuition.
Create a hierarchical circuit as follows:
from hierarqcal import Qinit, Qcycle, Qmask
hierq = Qinit(8) + (Qcycle(1) + Qmask("right")) * 3
The above creates a circuit that resembles a reverse binary tree architecture. There are 8 initial qubits and then a cycle-masking unit is repeated three times.
- class hierarqcal.core.CircuitInstruction(gate_name, symbol_info, sub_bits)¶
Bases:
tuple
- gate_name¶
Alias for field number 0
- sub_bits¶
Alias for field number 2
- symbol_info¶
Alias for field number 1
- class hierarqcal.core.Default_Mappings(value)¶
Bases:
Enum
Enum for default mappings
- BASE_MOTIF = None¶
- CYCLE = <hierarqcal.core.Qunitary object>¶
- MASK = None¶
- PERMUTE = <hierarqcal.core.Qunitary object>¶
- PIVOT = <hierarqcal.core.Qunitary object>¶
- SPLIT = None¶
- class hierarqcal.core.Primitive_Types(value)¶
Bases:
Enum
Enum for primitive types.
- BASE_MOTIF = 'base_motif'¶
- CYCLE = 'cycle'¶
- INIT = 'init'¶
- MASK = 'mask'¶
- PERMUTE = 'permute'¶
- PIVOT = 'pivot'¶
- SPLIT = 'split'¶
- class hierarqcal.core.Qcycle(stride=1, step=1, offset=0, boundary='periodic', **kwargs)¶
Bases:
Qmotif
A cycle motif, spreads unitaries in a ladder structure across the circuit TODO implement open boundary for dense and pooling also + tests
- __call__(Qc_l, *args, **kwargs)¶
Call the motif, this generates the edges and qubits of the motif (directed graph) based on it’s available qubits. Each time a motif in the stack changes, a loop runs through the stack from the beginning and calls each motif to update the graph (the available qubits, the edges etc).
- Parameters
Qc_l (list) – List of available qubits.
*args – Variable length argument list.
**kwargs – Arbitrary keyword arguments, such as:
- mapping (tuple(function, int)): TODO update this docstring, it’s not a tuple anymore.
Function mapping is specified as a tuple, where the first argument is a function and the second is the number of symbols it uses. A symbol here refers to an variational paramater for a quantum circuit, i.e. crz(theta, q0, q1) <- theta is a symbol for the gate.
- Returns
Returns the updated version of itself, with correct nodes and edges.
- Return type
Qconv
- class hierarqcal.core.Qhierarchy(qubits, function_mappings={})¶
Bases:
object
The main class that manages the “stack” of motifs, it handles the interaction between successive motifs and when a motifs are added to the stack, it updates all the others accordingly. An object of this class fully captures the architectural information of a hierarchical quantum circuit. It also handles function (unitary operation) mappings.
- __add__(other)¶
Add a motif, motifs or hierarchy to the stack.
- Parameters
other (Qmotif, Qhierarchy, Sequence(Qmotif)) – The motif, motifs or hierarchy to add to the stack.
- Returns
A new Qhierarchy object with the motif(s) added to the stack.
- Return type
- __call__(symbols=None, backend=None, **kwargs)¶
Call self as a function.
- __mul__(other)¶
Repeat the hierarchy a number of times. If a motif(s) is provided, it is added to the stack.
- Parameters
other (int, Qmotif, Sequence(Qmotif)) – The number of times to repeat the hierarchy or the motif(s) to add to the stack.
- Returns
A new Qhierarchy object with the motif(s) added to the stack.
- Return type
- append(motif)¶
Add a motif to the stack of motifs and update it (call to generate nodes and edges) according to the action of the previous motifs in the stack.
- Parameters
motif (Qmotif) – The motif to add to the stack.
- Returns
A new Qhierarchy object with the new motif added to the stack.
- Return type
- copy()¶
- Returns
A copy of the current Qhierarchy object.
- Return type
- extend(motifs)¶
Add and update a list of motifs to the current stack of motifs call each to generate their nodes and edges according to the action of the previous motifs in the stack.
- Parameters
motifs (Qmotifs) – A tuple of motifs to add to the stack.
- Returns
A new Qhierarchy object with the motifs added to the stack.
- Return type
- extmerge(hierarchies)¶
Merge a list of Qhierarchy objects by adding the head of each one to the tail of the previous one. All are copied to ensure immutability.
- Parameters
hierarchies (Qhierarchy) – A list of Qhierarchy objects to merge with the current one.
- Returns
A new Qhierarchy object with the list merged.
- Return type
- get_symbols()¶
- get_unitary_function(**kwargs)¶
Convert the Qhierarchy into a function that can be called.
- merge(hierarchy)¶
Merge two Qhierarchy objects by adding the head of the second one to the tail of the first one. Both are copied to ensure immutability.
- Parameters
hierarchy (Qhierarchy) – The Qhierarchy object to merge with the current one.
- Returns
A new Qhierarchy object with the two merged.
- Return type
- set_symbols(symbols)¶
- update_Q(Q, start_idx=0)¶
Update the number of available qubits for the hierarchy and update the rest of the stack accordingly.
- Parameters
Q (list(int or string)) – The list of available qubits.
- class hierarqcal.core.Qinit(Q, state=None, tensors=None, **kwargs)¶
Bases:
Qmotif
Qinit motif, represents a freeing up qubit for the QCNN, that is making qubits available for future operations. All Qhierarchy objects start with a Qinit motif. It is a special motif that has no edges and is not an operation.
- __add__(other)¶
Add a motif, motifs or hierarchy to the stack with self.Qinit available qubits.
- __call__(Q, *args, **kwargs)¶
Calling TODO add explanation just returns the object. Kwargs and Args are ignored, it just ensures that Qinit can be called the same way operational motifs can.
- class hierarqcal.core.Qmask(global_pattern='1*', merge_within='1*', merge_between=None, strides=[1, 1, 0], steps=[1, 1, 1], offsets=[0, 0, 0], boundaries=['open', 'open', 'periodic'], **kwargs)¶
Bases:
Qsplit
A masking motif, it masks qubits based on some pattern TODO some controlled operation where the control is not used for the rest of the circuit). This motif changes the available qubits for the next motif in the stack.
- __call__(Qp_l, *args, **kwargs)¶
Call the motif, this is used to generate the edges and qubits of the motif (directed graph) based on it’s available qubits. Each time a motif in the stack changes, a loop runs through the stack from the beginning and calls each motif to update the graph (the available qubits, the edges etc).
- Parameters
Qp_l (list) – List of available qubits.
*args – Variable length argument list.
**kwargs – Arbitrary keyword arguments, such as:
- mapping (tuple(function, int)):
Function mapping is specified as a tuple, where the first argument is a function and the second is the number of symbols it uses. A symbol here refers to an variational paramater for a quantum circuit, i.e. crz(theta, q0, q1) <- theta is a symbol for the gate.
- Returns
Returns the updated version of itself, with correct nodes and edges.
- Return type
Qpool
- class hierarqcal.core.Qmotif(Q=[], E=[], Q_avail=[], edge_order=[1], next=None, prev=None, mapping=None, symbol_fn=<function Qmotif.<lambda>>, is_default_mapping=True, is_operation=True, share_weights=True, type=Primitive_Types.BASE_MOTIF)¶
Bases:
object
Hierarchical circuit architectures are created by stacking motifs, the lowest level motifs (primitives) are building blocks for higher level ones. Examples of primitives are cycles (
Qcycle
), masks (Qmask_Base
), and permutations (Qpermute
). Each motif is a directed graph with nodes Q representing qubits and edges E unitary operations applied between them. The direction of an edge is the order of interaction for the unitary. Each instance has pointers to its predecessor and successor.- Variables
Q (list, optional) – Qubit labels of the motif. Defaults to [].
E (list, optional) – Edges of the motif. Defaults to [].
Q_avail (list, optional) – Available qubits of the motif. This is calculated by the previous motif in the stack. Defaults to [].
edge_order (list, optional) – Order of unitaries applied. Defaults to [1], [-1] will reverse the order, [5,6] does the 5th and 6th edge first and then the rest in the normal order.
next (
Qmotif
, optional) – Next motif in the stack. Defaults to None.prev (Qmotif, optional) – Previous motif in the stack. Defaults to None.
mapping (
Qunitary
orQhierarchy
, optional) – Either aQunitary
instance or aQhierarchy
that will be converted to anQunitary
object. Defaults to None.symbol_fn (lambda) – TODO
is_default_mapping (bool, optional) – Flag to determine if default mapping is used. Defaults to True.
is_operation (bool, optional) – Flag to determine if the motif is an operation. Defaults to True.
share_weights (bool, optional) – Flag to determine if weights are shared within a motif. Defaults to True.
- __add__(other)¶
Append an other motif to the current one: self + other = (self, other).
- Parameters
other (Qmotif) – Motif to append to current motif (self).
- Returns
A 2-tuple of motifs: (self, other) in the order they were added. The tuples contain copies of the original motifs.
- Return type
- __call__(Q, E=[], remaining_q=None, is_operation=True, **kwargs)¶
Call self as a function.
- __mul__(other)¶
Repeat motif “other” times: self * other = (self, self, …, self). Other is an int.
- Parameters
other (int) – Number of times to repeat motif.
- Returns
A tuple of motifs: (self, self, …, self), where each is a new object copied from the original.
- Return type
- append(other)¶
Append an other motif to the current one: self + other = (self, other).
- Parameters
other (Qmotif) – Motif to append to current motif (self).
- Returns
A 2-tuple of motifs: (self, other) in the order they were added. The tuples contain copies of the original motifs.
- Return type
- cycle(Q, stride=1, step=1, offset=0, boundary='periodic', arity=2)¶
The cycle pattern
- get_symbols()¶
Get the symbols of the motif. If share_weights is True, then symbols are obtained from the first edge_mapping. Otherwise, symbols are obtained from each edge_mapping.
- Yields
symbols (List) or None – List of symbols or None if no edge_mapping.
- set_E(E)¶
Set the edges E of the motif.
- Parameters
E (list(tuples)) – List of edges, where each edge is a tuple of qubit labels (self.Q).
- set_Q(Q)¶
Set the qubit labels Q of the motif.
- Parameters
Q (list(int or string)) – List of qubit labels.
- set_Qavail(Q_avail)¶
Set the number of available qubits Q_avail for the motif. This gets calculated by the previous motif in the stack, for example the :py:class:Qmask motif would mask qubits and use this function to update the available qubits after it’s action. Example: if Q = [1,2,3,4] and Qmask(“right”) is applied then Q_avail = [1,2].
- Parameters
Q_avail (list) – List of available qubits.
- set_arity(arity)¶
Set the arity of the motif (qubits per unitary/ nodes per edge).
- Parameters
arity (int) – Number of qubits per unitary
- set_edge_mapping(unitary_function)¶
Maps each edge to a unitary function.
- Parameters
edge_mapping (function) – function for each edge.
- set_edge_order(edge_order)¶
Set the edge order of the motif (order of unitaries applied). [1] means first edge comes first [2,8] means second edge comes first, then 8th edge comes second. For example, if self.E = [(1,2),(7,3),(5,2)] then self.edge_order = [1,2,3] means the unitaries are applied in the order (1,2),(7,3),(5,2). If self.edge_order = [2,1,3] then the unitaries are applied in the order (7,3),(1,2),(5,2). If self.edge_order = [3] then the unitaries are applied in the order (5,2),(1,2),(7,3),.
- Parameters
edge_order (list(int)) – List of edge orders.
- set_is_operation(is_operation)¶
Set the is_operation flag.
- Parameters
is_operation (bool) – Whether the motif is an operation.
- set_mapping(mapping)¶
Specify the unitary operations applied according to the type of motif.
- Parameters
mapping (Qhierarchy or Qunitary) – Unitary operation applied to the motif.
- set_next(next)¶
Set the next motif in the stack.
- Parameters
next (Qmotif) – Next motif in the stack.
- set_prev(prev)¶
Set the previous motif in the stack.
- Parameters
prev (Qmotif) – Previous motif in the stack.
Set the share_weights flag.
- Parameters
share_weights (bool) – Whether to share weights within a motif.
- set_symbols(symbols=None, start_idx=0)¶
Set the symbol’s.
- Parameters
symbols (list) – List of symbols to set.
start_idx (int) – Starting index of symbols, this is used when :py:class:Qhierarchy updates the stack, it loops through each motif counting symbols, at each motif it updates the symbols (inderectly calls this function) and send the current count as starting inde so that correct sympy symbol indices are used.
- class hierarqcal.core.Qmotifs(iterable=(), /)¶
Bases:
tuple
A tuple of motifs, this is the data structure for storing sequences motifs. It subclasses tuple, so all tuple methods are available.
- __add__(other)¶
Add two tuples of motifs together.
- Parameters
other (Qmotifs or Qmotif) – Multiple motifs or singe motif to add to current sequence of motifs.
- Returns
A single tuple of the motifs that were added, These are copies of the original motifs, since tuples are immutable.
- Return type
Qmotifs(tuple)
- class hierarqcal.core.Qpermute(combinations=True, **kwargs)¶
Bases:
Qmotif
A dense motif, it connects unitaries to all possible combinations of qubits (all possible edges given Q) in the quantum circuit.
- __call__(Qc_l, *args, **kwargs)¶
Call the motif, this is used to generate the edges and qubits of the motif (directed graph) based on it’s available qubits. Each time a motif in the stack changes, a loop runs through the stack from the beginning and calls each motif to update the graph (the available qubits, the edges etc).
- Parameters
Qc_l (list) – List of available qubits.
*args – Variable length argument list.
**kwargs – Arbitrary keyword arguments, such as:
- mapping (tuple(function, int)):
Function mapping is specified as a tuple, where the first argument is a function and the second is the number of symbols it uses. A symbol here refers to an variational paramater for a quantum circuit, i.e. crz(theta, q0, q1) <- theta is a symbol for the gate.
- Returns
Returns the updated version of itself, with correct nodes and edges.
- Return type
Qdense
- class hierarqcal.core.Qpivot(global_pattern='1*', merge_within='1*', merge_between=None, strides=[1, 1, 0], steps=[1, 1, 1], offsets=[0, 0, 0], boundaries=['open', 'open', 'periodic'], **kwargs)¶
Bases:
Qsplit
The pivot connects the set of available qubits sequentially to a fixed set of qubits. The global pattern determine the pivot points while the merge pattern determines how the qubits are passed to the mapping.
- __call__(Qp_l, *args, **kwargs)¶
- Parameters
Qp_l (list) – List of available qubits.
*args – Variable length argument list.
**kwargs – Arbitrary keyword arguments, such as: * mapping (tuple(function, int)):
Function mapping is specified as a tuple, where the first argument is a function and the second is the number of symbols it uses. A symbol here refers to an variational paramater for a quantum circuit, i.e. crz(theta, q0, q1) <- theta is a symbol for the gate.
Returns:
- cycle_between_splits(E_a, E_b, stride=0, step=1, offset=0, boundary='open')¶
- class hierarqcal.core.Qsplit(global_pattern='1*', merge_within='1*', merge_between=None, mask=False, strides=[1, 1, 0], steps=[1, 1, 1], offsets=[0, 0, 0], boundaries=['open', 'open', 'periodic'], type=Primitive_Types.SPLIT, **kwargs)¶
Bases:
Qmotif
- __call__(Q, E=[], remaining_q=None, is_operation=True, **kwargs)¶
Call self as a function.
- cycle_between_splits(E_a, E_b, stride=0, step=1, offset=0, boundary='open')¶
- get_pattern_fn(pattern, length)¶
- get_predefined_pattern_fn(pattern)¶
- merge_within_splits(E, merge_pattern)¶
- wildcard_populate(pattern, length)¶
- class hierarqcal.core.Qunitary(function=None, n_symbols=0, arity=2, symbols=None)¶
Bases:
object
Base class for all unitary operations, the main purpose is to store the operation, its arity and parameters (symbols) for later use. # TODO add support for function as matrix # TODO add share_weights parameter
- __call__(*args, **kwargs)¶
Call self as a function.
- get_circ_info_from_string(input_str)¶
Takes a string that represents a circuit function, and breaks down the string into a set of circuit instructions.
- Parameters
`input_str` (str)
- Returns
a list of circuit instructions, where each entry represents a distinct gate operation. Each entry is a list of three components: [gate_name,symbol_info, sub_bits]
gate_name (str) is the name of the Qiskit gate being implemented.
- symbol_info (list) keeps track of whether the gate is parametrized, and
if so, whether it is the same parameter as another gate.
sub_bits (list of ints) keeps track of the bits the gates are applied on.
unique_bits (list of ints): set of qubits unique_params (list of strs): set of gate parameters
- Return type
substr_list (list)
- Workflow:
- Step 1: partition the string into lists of individual gate instructions
in the form {gate_string}(parameters)^{bits}
- Step 2: split each substring into the gate string, the relevant
parameters, and the bits it acts on
- Step 3: convert the bits, the gate string, and the relevant parameters
into integers/functions
- get_symbols()¶
Get symbols for this unitary.
Returns: List of symbols
- set_edge(edge)¶
- set_symbols(symbols=None)¶
Set symbols for this unitary.
- Parameters
symbols (list) – List of symbols