Source code for vulqano.states.abstractcircuitstate

# This code is part of vulqano.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.


"""
Define a class for abstract many-body representation of quantum circuits.

A ciruits is represented by a n-dimensional array of strings, where the first
index labels the time-step, and the other indices label the position of the qubit
in the lattice. Each string denote the name of the corresponding gate
(see vulqano.gates.discretegates and vulqano.gates.discretegates).
"""

import math
from math import pi as PI
from itertools import product
from inspect import getsource
import numpy as np
from numpy import ma
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib import colors
from qtealeaves.tooling.hilbert_curvature import hilbert_curve_2d
from vulqano.gates.discretegates import (
    GATES_DICTIONARY as DISCRETE_GATES_DICTIONARY,
)
from vulqano.gates.continuousgates import (
    GATES_DICTIONARY as CONTINUOUS_GATES_DICTIONARY,
)


__all__ = [
    "AbstractCircuitState",
]

plt.ioff()


def hilbert_displacements_2d(n_x, n_y):
    """
    Hilber corve mapping functions for embedding circuits on 2d qubits lattices.
    Returns displacements generating the Hilbert curve for a 2d grid with both
    sides being a power of two.

    **Arguments**

    n_x : int
        Number of sites in x-direction

    n_y : int
        Number of sites in y-direction
    """
    hilbert_map = hilbert_curve_2d(n_x, n_y)
    inverse_hilbert_map = np.zeros((n_x * n_y, 2), dtype=int)
    for idx, linear_index in np.ndenumerate(hilbert_map):
        inverse_hilbert_map[linear_index] = idx
    displacements = inverse_hilbert_map - np.roll(inverse_hilbert_map, 1, axis=0)
    displacements[0] = [0, 0]
    return displacements


def compact_hamiltonian(hamiltonian):
    """
    Transform the Hamiltonian representation.

    **Arguments**

    hamiltonian : list of (np.array of strings, float, mask)
        Abstract description of the Hamiltonian. The energy is obtained by
        counting how many times each subcircuit hamiltonian_operator[i][0]
        appears on a region A of the circuit suck that that
        hamiltonian_operator[i][2] is True for all (t,q) in A.
        The counted number is multiplied by the weight hamiltonian_operator[i][1].

    **Returns**

    hamiltonian_operator_compact :  : list of (np.array of ints, np.array of floats)
        Abstract description of the Hamiltonian. The energy is obtained as
        E = sum_i sum_{t,q} IS(hamiltonian_operator[i][0](t,q))*
                            hamiltonian_operator[i][1](t,q)
        Where IS(hamiltonian_operator[i][0](t,q)) checks if the subcircuit
        hamiltonian_operator[i][0] is appears on the site (t,q) of the circuit
        state.

    """
    hamiltonian_operator_compact = []
    for operator, coupling, mask in hamiltonian:
        first_appearance = True
        for op, couplings_array in hamiltonian_operator_compact:
            if np.array_equal(op, operator):
                couplings_array += coupling * mask
                first_appearance = False
        if first_appearance:
            operator_new = np.empty_like(operator, dtype=object)
            operator_new[:] = operator
            hamiltonian_operator_compact.append((operator_new, coupling * mask))
    return hamiltonian_operator_compact


[docs] class AbstractCircuitState: """ Abstract representation of the state of the quantum circuit. **Arguments** vector : numpy.array of strings Array of gates applied at each time and on each qubit. The first index correspond to the time step, the other indices label the qubit in the lattice. name : str Circuit name. rot_amplitudes_array : np array of float or None. A numpy array of float with the same shape of the circuit, where at each entry a parameter is specified for the corresponding continuous gate. If none the state is a discrete state that can only contain discrete gates. Default is None. **Attributes** vector : numpy.array of strings Array of gates applied at each time and on each qubit. The first index correspond to the time step, the other indices label the qubit in the lattice. name : str Circuit name. times : int Number of time steps of the circuit. qubits : touple of ints Number of qubits in each direction of the lattice. is_continuous : bool If True the circuit contains continouous gates, False if it contains only discrete gates. dim : int Number of dimension of the circuit state, i.e. 1 + number of dimensions of the qubits lattice. rot_amplitudes : np array of float. A numpy array of float with the same shape of the circuit, where at each entry a parameter is specified for the corresponding continuous gate. All entries are set to 0 for discrete gates set. gates_dictionary : dictionary vulqano.gates.discretegates.GATES_DICTIONARY or vulqano.gates.continuousgates.GATES_DICTIONARY """ def __init__(self, vector, name, rot_amplitudes_array=None): self.vector = vector self.name = name self.times = vector.shape[0] self.qubits = vector.shape[1:] self.dim = len(vector.shape) if self.dim not in [2, 3]: raise ValueError("Only (1+1)d and (1+2)d circuit states are implemented.") self.is_continuous = rot_amplitudes_array is not None if self.is_continuous: if rot_amplitudes_array.shape != vector.shape: raise ValueError( "vector and rot_amplitudes_array must have the same array shape." ) self.rot_amplitudes = rot_amplitudes_array % (2 * PI) self.gates_dictionary = CONTINUOUS_GATES_DICTIONARY else: self.rot_amplitudes = np.full(self.vector.shape, 0.0) self.gates_dictionary = DISCRETE_GATES_DICTIONARY def __str__(self): """ Generates a pictorial representation of the circuit. **Returns** : str A pictorial representation of the circuit. """ if self.is_continuous: return ( "\n" + self.name + ":\n\n" + str(self.vector) + ":\n\nRotation amplitudes:\n" + str(self.rot_amplitudes) ) return "\n" + self.name + ":\n\n" + str(self.vector)
[docs] def add_swap_area(self, swap_l, swap_r): """ Adds layers of idle gates at the first and last time-steps of the circuit. **Arguments** swap_l : int Number of idle time steps at the beginning of the circuit. swap_r : int Number of idle time steps at the end of the circuit. **Returns** self : AbstractCircuitState Modified circuit. """ if swap_l <= 0 or swap_r <= 0: raise ValueError("swap_l and swap_r must be positive integers.") idle_region_l = np.full(np.append(swap_l, self.qubits), "idle") idle_region_r = np.full(np.append(swap_r, self.qubits), "idle") self.vector = np.concatenate((idle_region_l, self.vector, idle_region_r)) idle_region_l = np.full(np.append(swap_l, self.qubits), 0) idle_region_r = np.full(np.append(swap_r, self.qubits), 0) self.rot_amplitudes = np.concatenate( (idle_region_l, self.rot_amplitudes, idle_region_r) ) self.times = self.vector.shape[0] return self
[docs] def expand(self, distance, idle_block_length): """ Expand the circuit by adding unifomly distrubited blocks of idle time-steps at a fixed distance. **Arguments** distance : int Distance between blocks of idle time-steps. idle_block_length : int Number of time steps in the idle blocks. **Returns** self : AbstractCircuitState Modified circuit. """ if distance <= 0: raise ValueError("distance must be a positive.") if idle_block_length < 0: raise ValueError("idle_block_length must be 0 or a positive.") subcirc_list = [] t_ii = 0 while t_ii < self.times: subcirc_list.append(self.vector[t_ii : t_ii + distance]) if t_ii + distance < self.times: subcirc_list.append( np.full(np.append(idle_block_length, self.qubits), "idle") ) t_ii += distance self.vector = np.concatenate(subcirc_list) subcirc_list = [] t_ii = 0 while t_ii < self.times: subcirc_list.append(self.rot_amplitudes[t_ii : t_ii + distance]) if t_ii + distance < self.times: subcirc_list.append( np.full(np.append(idle_block_length, self.qubits), 0) ) t_ii += distance self.rot_amplitudes = np.concatenate(subcirc_list) self.times += self.vector.shape[0] return self
[docs] def get_energy(self, hamiltonian_operator): """ Returns the energy associated to the circuit state by an Hamiltonian operator. **Arguments** hamiltonian_operator : list of (np.array of strings, float, mask) Abstract description of the Hamiltonian. The energy is obtained by counting how many times each subcircuit hamiltonian_operator[i][0] is applied on a site of the circuit state such that hamiltonian_operator[i][2](t,q) is True. The counted number is multiplied by the weight hamiltonian_operator[i][1]. **Returns** energy : float Energy associated to the circuit state by the Hamiltonian operator. """ hamiltonian_operator = compact_hamiltonian(hamiltonian_operator) tmp_ham = [] for operator, coupling in hamiltonian_operator: tmp_ham.append((ma.masked_equal(operator, "any"), coupling)) hamiltonian_operator = tmp_ham energy = 0 vector_shape = np.array(self.vector.shape) for operator, coupling in hamiltonian_operator: op_shape = np.array(operator.shape) for site in product(*[range(s) for s in vector_shape - op_shape + 1]): slices = tuple( slice(site[i], (site + op_shape)[i]) for i in range(self.dim) ) if np.ma.allequal( operator, self.vector[slices], ): energy += coupling[site] return energy
[docs] def make_3d(self): """ Extends the qubit lattice dimension a (1+1)d circuit via Hilbert mapping and returns an equivalent (1+2)d circuit. **Returns** AbstractCircuitState (1+2)d circuit """ if self.dim != 2: raise ValueError("Only (1+1)d circuits can be extended to (1+2)d circuits.") q_base = int(np.log2(self.qubits[0])) if abs(np.log2(self.qubits[0]) - q_base) > 1e-14: raise ValueError( "Number of qubits must be a power of 2 for Hilbert mapping." ) displacements = hilbert_displacements_2d( 2 ** round(q_base / 2), 2 ** (q_base - round(q_base / 2)) ) gates_array_out = np.full( (self.times, 2 ** round(q_base / 2), 2 ** (q_base - round(q_base / 2))), "idle", dtype=object, ) parameters_array_out = np.zeros( (self.times, 2 ** round(q_base / 2), 2 ** (q_base - round(q_base / 2))) ) for time in range(self.times): site = np.array([0, 0]) for ii, (d_x, d_y) in enumerate(displacements): site += (d_x, d_y) gates_array_out[time][site[0], site[1]] = self.vector[time][ii] parameters_array_out[time][site[0], site[1]] = self.rot_amplitudes[ time ][ii] if self.vector[time][ii] == "busy": if (d_x, d_y) == (1, 0): gates_array_out[time][site[0] - d_x, site[1] - d_y] += "_r" if (d_x, d_y) == (0, -1): ( gates_array_out[time][site[0] - d_x, site[1] - d_y], gates_array_out[time][site[0], site[1]], ) = ( gates_array_out[time][site[0], site[1]], gates_array_out[time][site[0] - d_x, site[1] - d_y], ) ( parameters_array_out[time][site[0] - d_x, site[1] - d_y], parameters_array_out[time][site[0], site[1]], ) = ( parameters_array_out[time][site[0], site[1]], parameters_array_out[time][site[0] - d_x, site[1] - d_y], ) if (d_x, d_y) == (-1, 0): gates_array_out[time][site[0] - d_x, site[1] - d_y] += "_r" ( gates_array_out[time][site[0] - d_x, site[1] - d_y], gates_array_out[time][site[0], site[1]], ) = ( gates_array_out[time][site[0], site[1]], gates_array_out[time][site[0] - d_x, site[1] - d_y], ) ( parameters_array_out[time][site[0] - d_x, site[1] - d_y], parameters_array_out[time][site[0], site[1]], ) = ( parameters_array_out[time][site[0], site[1]], parameters_array_out[time][site[0] - d_x, site[1] - d_y], ) if not self.is_continuous: parameters_array_out = None return AbstractCircuitState( gates_array_out, self.name + "_to_3d", rot_amplitudes_array=parameters_array_out, )
[docs] def to_qasm(self): """ Returns the OPENQASM code for the circuit. **Returns** openqasm_string : str OPENQASM code for the circuit. """ openqasm_string = ( 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[' + str(np.prod(self.qubits)) + "];\n" ) nd_to_1d_map = np.arange(np.prod(self.qubits)).reshape(self.qubits) for time in range(self.times): if "\n\n" != openqasm_string[-2:]: openqasm_string += "\n" for qubit_position in product(*[range(size) for size in self.qubits]): gate = self.vector[time][qubit_position] if gate in self.gates_dictionary: if self.gates_dictionary[gate]["Qasm"] is not None: control_qubit = nd_to_1d_map[qubit_position] connectivity = self.gates_dictionary[gate]["Connectivity"] connectivity = [ 0 for ii in range(self.dim - len(connectivity) - 1) ] + connectivity target_qubit = nd_to_1d_map[ tuple( qubit_position[ii] + connectivity[ii] for ii in range(self.dim - 1) ) ] openqasm_string += self.gates_dictionary[gate]["Qasm"]( self.rot_amplitudes[time][qubit_position], control_qubit, target_qubit, ) else: raise ValueError( "Gate " + str(gate) + " not defined in gates dictionary" ) return openqasm_string
[docs] def draw( self, filename, ncols=1, ): """ Draw a figure representing the circuit as the state of a lattice. **Arguments** filename : str Name of the file, path and extension included. ncols : int, optional Number of columns in kegend. If 0, no legend is plotted. **Returns** None. """ circuit_state_vector = np.copy(self.vector) rot_amplitudes_vector = np.copy(self.rot_amplitudes) # Add empty time steps to fit subplots into a rectangle if self.dim == 3: rows = int(circuit_state_vector.shape[0] ** (0.5)) cols = math.ceil(circuit_state_vector.shape[0] / rows) new_steps = rows * cols - circuit_state_vector.shape[0] circuit_state_vector = np.concatenate( ( circuit_state_vector, np.full(np.append(new_steps, self.qubits), "idle"), ) ) rot_amplitudes_vector = np.concatenate( (rot_amplitudes_vector, np.full(np.append(new_steps, self.qubits), 0)) ) rgb_array = np.zeros(np.append(circuit_state_vector.shape, 3)) for site in product(*[range(s) for s in circuit_state_vector.shape]): rgb_array[site] = self.gates_dictionary[circuit_state_vector[site]][ "Color" ](rot_amplitudes_vector[site]) plt.rcParams["figure.dpi"] = 500 if self.dim == 2: fig, ax_1 = plt.subplots() ax_1.imshow(np.transpose(rgb_array, (1, 0, 2))) ax_1.set_xlabel("$t$") ax_1.set_ylabel("$q$") ax_1.set_xticks(range(0, rgb_array.shape[0], 5)) ax_1.set_xticklabels(range(0, rgb_array.shape[0], 5), fontsize=6) ax_1.set_yticks(range(0, rgb_array.shape[1])) ax_1.set_yticklabels(range(0, rgb_array.shape[1]), fontsize=6) elif self.dim == 3: rows = int(circuit_state_vector.shape[0] ** (0.5)) cols = math.ceil(circuit_state_vector.shape[0] / rows) fig, axs = plt.subplots(rows, cols) for ii, jj in product(range(rows), range(cols)): nn = ii + rows * jj axs[ii, jj].imshow(np.transpose(rgb_array[nn], (1, 0, 2))) axs[ii, jj].set_title("t=" + str(nn), fontsize=6) axs[ii, jj].xaxis.set_tick_params(labelbottom=False) axs[ii, jj].yaxis.set_tick_params(labelleft=False) axs[ii, jj].set_xticks([]) axs[ii, jj].set_yticks([]) else: raise ValueError( "Circuit drawing implemented only for (1+1)d and (2+1)d circuits." ) if ncols > 0: involved_gates = set() for gate in self.gates_dictionary: if np.count_nonzero(self.vector == gate) > 0: involved_gates.add(gate) gate_colors = [] gate_labels = [] for gate in involved_gates: if ( not self.is_continuous or not self.gates_dictionary[gate]["is_parametric"] ): gate_colors.append(self.gates_dictionary[gate]["Color"](0)) gate_labels.append(("$" + gate + "$").replace("dg", r"^\dag")) sorter = np.array(gate_labels).argsort() gate_colors = np.array(gate_colors)[sorter] gate_labels = np.array(gate_labels)[sorter] plt.figlegend( handles=[ mpatches.Patch(label=label, edgecolor="black", facecolor=color) for color, label in zip(gate_colors, gate_labels) ], handlelength=1, handleheight=1, bbox_to_anchor=[0.9, 0.5], loc="center left", ncol=ncols, ) if self.is_continuous: parametric_legend_instructions = [] for gate in involved_gates: if self.gates_dictionary[gate]["is_parametric"]: # Extract colormap name from lambda function lambda_source = getsource(self.gates_dictionary[gate]["Color"]) sub1 = 'get_cmap("' sub2 = '")' cmap_name = lambda_source[ lambda_source.index(sub1) + len(sub1) : lambda_source.index(sub2) ] parametric_legend_instructions.append( ("$" + gate + "$", cmap_name) ) fig.subplots_adjust(bottom=0.1) delta = 0.9 / (6 * len(parametric_legend_instructions)) n_ticks = 3 norm = mpl.colors.Normalize(vmin=0, vmax=2 * PI) for ii, (gate_name, cmap_name) in enumerate( parametric_legend_instructions ): cbar_ax_1 = fig.add_axes( [0.05 + delta + ii * 6 * delta, 0, 5 * delta, 0.05] ) cmap = plt.get_cmap(cmap_name, 100 * n_ticks) cmap = colors.LinearSegmentedColormap.from_list( cmap.name, cmap( np.concatenate( (np.linspace(0.25, 1, 100), np.linspace(1, 0.25, 100)) ) ), ) scalar_mappable = plt.cm.ScalarMappable(cmap=cmap, norm=norm) scalar_mappable.set_array([]) cbar = fig.colorbar( scalar_mappable, ticks=np.round(np.linspace(0, 2 * PI, n_ticks), 2), boundaries=np.linspace(0, 2 * PI, 100 * n_ticks), orientation="horizontal", cax=cbar_ax_1, ) cbar.ax.set_title(gate_name, fontsize=10) cbar.ax.tick_params(labelsize=6) cbar.ax.set_xticklabels(["$0$", r"$\pi$", r"$2\pi$"]) fig.suptitle(self.name) plt.savefig(filename + ".pdf", bbox_inches="tight") plt.close(fig)
def test_discrete(): """ Test state creation, energy calculation, qasm generation, and drawing of a non-paramateric circuit. Returns ------- None. """ input_circuit = AbstractCircuitState( np.array( [ ["SWAP", "busy", "CZ", "busy"], ["Z", "Z", "Z", "idle"], ["CZ", "busy", "CZ", "busy"], ["idle", "idle", "Z", "idle"], ] ), "input_circuit", ) print(input_circuit) swap_left, swap_right = (3, 2) input_circuit.expand(2, 1) input_circuit.add_swap_area(swap_left, swap_right) print(input_circuit) circuit_area_mask = np.concatenate( ( np.full(np.append(swap_left, input_circuit.qubits), False), np.full( np.append( input_circuit.times - swap_right - swap_left, input_circuit.qubits ), True, ), np.full(np.append(swap_right, input_circuit.qubits), False), ) ) machine = { "gates": {"Z", "H", "CZ", "SWAP"}, "hamiltonian": ( (np.array([["Z"]]), 0.001, circuit_area_mask), (np.array([["H"]]), 0.001, circuit_area_mask), (np.array([["idle"]]), 0.001, circuit_area_mask), (np.array([["CZ"]]), 0.005, circuit_area_mask), (np.array([["SWAP"]]), 1, circuit_area_mask), (np.array([["CZ", "any", "CZ"]]), 0.5, circuit_area_mask), ( np.array([["CZ", "any", "any", "CZ"]]), 0.05, circuit_area_mask, ), ( np.full(np.append(1, input_circuit.qubits), "idle"), -np.prod(input_circuit.qubits) * 0.001, circuit_area_mask, ), ), } print("\nEnergy -> ", input_circuit.get_energy(machine["hamiltonian"]), "\n") print(input_circuit.to_qasm()) input_circuit.draw("discerete_circuit_draw_test") def test_discrete_2d(): """ Test state creation, energy calculation, qasm generation, and drawing of a non-paramateric circuit. Returns ------- None. """ input_circuit = AbstractCircuitState( np.array( [ [["SWAP", "busy"], ["idle", "idle"], ["CZ", "busy"], ["idle", "H"]], [["Z", "idle"], ["Z", "idle"], ["CZ_r", "idle"], ["busy", "Z"]], [["CZ", "busy"], ["Z", "idle"], ["CZ", "busy"], ["Z", "idle"]], [["idle", "idle"], ["idle", "Z"], ["Z", "idle"], ["idle", "idle"]], ] ), "input_circuit", ) print(input_circuit) swap_left, swap_right = (3, 2) input_circuit.expand(2, 1) input_circuit.add_swap_area(swap_left, swap_right) print(input_circuit) circuit_area_mask = np.concatenate( ( np.full(np.append(swap_left, input_circuit.qubits), False), np.full( np.append( input_circuit.times - swap_right - swap_left, input_circuit.qubits ), True, ), np.full(np.append(swap_right, input_circuit.qubits), False), ) ) machine = { "gates": {"Z", "H", "CZ", "SWAP"}, "hamiltonian": ( (np.array([[["Z"]]]), 0.001, circuit_area_mask), (np.array([[["H"]]]), 0.001, circuit_area_mask), (np.array([[["idle"]]]), 0.001, circuit_area_mask), (np.array([[["CZ"]]]), 0.005, circuit_area_mask), (np.array([[["SWAP"]]]), 1, circuit_area_mask), (np.array([[["CZ", "any", "CZ"]]]), 0.5, circuit_area_mask), (np.array([[["CZ"], ["any"], ["CZ"]]]), 0.5, circuit_area_mask), ( np.array([[["CZ", "any", "any", "CZ"]]]), 0.05, circuit_area_mask, ), ( np.array([[["CZ"], ["any"], ["any"], ["CZ"]]]), 0.05, circuit_area_mask, ), ( np.full(np.append(1, input_circuit.qubits), "idle"), -np.prod(input_circuit.qubits) * 0.001, circuit_area_mask, ), ), } print("\nEnergy -> ", input_circuit.get_energy(machine["hamiltonian"]), "\n") print(input_circuit.to_qasm()) input_circuit.draw("discerete_2d_circuit_draw_test") def test_continuous(): """ Test state creation, energy calculation, qasm generation, and drawing of a paramateric circuit. Returns ------- None. """ input_circuit = AbstractCircuitState( np.array( [ ["SWAP", "busy", "CP", "busy"], ["RX", "RZ", "RX", "idle"], ["CP", "busy", "CP", "busy"], ["idle", "idle", "RZ", "idle"], ] ), "input_circuit", rot_amplitudes_array=np.array( [ [0, 0, 2, 0], [0.5, 1, 7, 0], [5, 0, 2, 0], [0, 0, 1, 0], ] ), ) print(input_circuit) swap_left, swap_right = (3, 2) input_circuit.expand(2, 1) input_circuit.add_swap_area(swap_left, swap_right) print(input_circuit) circuit_area_mask = np.concatenate( ( np.full(np.append(swap_left, input_circuit.qubits), False), np.full( np.append( input_circuit.times - swap_right - swap_left, input_circuit.qubits ), True, ), np.full(np.append(swap_right, input_circuit.qubits), False), ) ) machine = { "gates": {"RX", "RZ", "CP", "SWAP"}, "hamiltonian": ( (np.array([["RX"]]), 0.001, circuit_area_mask), (np.array([["RZ"]]), 0.001, circuit_area_mask), (np.array([["idle"]]), 0.001, circuit_area_mask), (np.array([["CP"]]), 0.005, circuit_area_mask), (np.array([["CP"]]), 0.005, circuit_area_mask), (np.array([["SWAP"]]), 1, circuit_area_mask), (np.array([["CP", "any", "CP"]]), 0.5, circuit_area_mask), (np.array([["CP", "any", "any", "CP"]]), 0.05, circuit_area_mask), ( np.full(np.append(1, input_circuit.qubits), "idle"), -np.prod(input_circuit.qubits) * 0.001, circuit_area_mask, ), ), } print("\nEnergy -> ", input_circuit.get_energy(machine["hamiltonian"]), "\n") print(input_circuit.to_qasm()) input_circuit.draw("continuous_circuit_draw_test") def test_continuous_2d(): """ Test state creation, energy calculation, qasm generation, and drawing of a paramateric circuit. Returns ------- None. """ input_circuit = AbstractCircuitState( np.array( [ [["SWAP", "busy"], ["idle", "idle"], ["CP", "busy"], ["idle", "idle"]], [["RX", "idle"], ["RZ", "idle"], ["RX", "idle"], ["idle", "idle"]], [["CP", "busy"], ["idle", "idle"], ["CP_r", "idle"], ["busy", "idle"]], [["idle", "idle"], ["idle", "idle"], ["RZ", "idle"], ["idle", "idle"]], ] ), "input_circuit", rot_amplitudes_array=np.array( [ [[0, 0], [0, 0], [2, 0], [0, 0]], [[2, 0], [1, 0], [7, 0], [0, 0]], [[5, 0], [0, 0], [2, 0], [0, 0]], [[0, 0], [0, 0], [1, 0], [0, 0]], ] ), ) print(input_circuit) swap_left, swap_right = (3, 2) input_circuit.expand(2, 1) input_circuit.add_swap_area(swap_left, swap_right) print(input_circuit) circuit_area_mask = np.concatenate( ( np.full(np.append(swap_left, input_circuit.qubits), False), np.full( np.append( input_circuit.times - swap_right - swap_left, input_circuit.qubits ), True, ), np.full(np.append(swap_right, input_circuit.qubits), False), ) ) machine = { "gates": {"RX", "RZ", "CP", "SWAP"}, "hamiltonian": ( (np.array([[["RX"]]]), 0.001, circuit_area_mask), (np.array([[["RZ"]]]), 0.001, circuit_area_mask), (np.array([[["idle"]]]), 0.001, circuit_area_mask), (np.array([[["CP"]]]), 0.005, circuit_area_mask), (np.array([[["CP"]]]), 0.005, circuit_area_mask), (np.array([[["SWAP"]]]), 1, circuit_area_mask), (np.array([[["CP", "any", "CP"]]]), 0.5, circuit_area_mask), (np.array([[["CP", "any", "any", "CP"]]]), 0.05, circuit_area_mask), ( np.full(np.append(1, input_circuit.qubits), "idle"), -np.prod(input_circuit.qubits) * 0.001, circuit_area_mask, ), ), } print("\nEnergy -> ", input_circuit.get_energy(machine["hamiltonian"]), "\n") print(input_circuit.to_qasm()) input_circuit.draw("continuous_circuit_2d_draw_test") def test_hilbert_mapping(): """ Test the dimensional extension via Hilbert mapping. Returns ------- None. """ input_circuit = AbstractCircuitState( np.array( [ ["SWAP", "busy", "CP", "busy", "SWAP", "busy", "CP", "busy"], ["RX", "RZ", "RX", "idle", "RX", "RZ", "RX", "idle"], ["idle", "CP", "busy", "CP", "busy", "CP", "busy", "idle"], ["idle", "SWAP", "busy", "CP", "busy", "SWAP", "busy", "RZ"], ] ), "input_circuit", rot_amplitudes_array=np.array( [ [0, 0, 2, 0, 0, 0, 2, 0], [0.2, 0.1, 7, 0, 0.2, 0.1, 7, 0], [0, 5, 0, 2, 0, 5, 0, 0], [0, 0, 0, 2, 0, 0, 0, 1], ] ), ) input_circuit.draw("input_2d") output_circuit = input_circuit.make_3d() for idx, gate in np.ndenumerate(output_circuit.vector): if not CONTINUOUS_GATES_DICTIONARY[gate]["is_parametric"]: if output_circuit.rot_amplitudes[idx] != 0: print(output_circuit) raise ValueError( "Non zero parameter associated to a non-parametric gate at size " + str(idx) + " of the circuit state:" + str(output_circuit) ) print(output_circuit.to_qasm()) output_circuit.draw("output_3d") if __name__ == "__main__": test_discrete() test_continuous() test_discrete_2d() test_continuous_2d() test_hilbert_mapping()