Source code for vulqano.quantumdynamics

# 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.


"""
Here we define the function that performs optimization based on quantum dynamics
of a state that encodes a superposition of circuits (e.g. quantum annealing).

The dynamics is simulated using qtealeaves.
"""


import os
import shutil
import math
from time import perf_counter as tictoc
import numpy as np
import qtealeaves as qtl
from qtealeaves.tooling.hilbert_curvature import HilbertCurveMap
from vulqano.version import __version__
from vulqano.utils import check_circuit_equivalence
from vulqano.states.abstractcircuitstate import AbstractCircuitState
from vulqano.rules.sym4rules import SYM_4_RULES_GENERATORS
from vulqano.quantummodels.quantummodel import get_quantum_compilation_model
from vulqano.quantummodels.collapsedquantummodel import (
    CollapseMap,
    get_collapsed_quantum_compilation_model,
)


__all__ = [
    "CircuitsFromTN",
    "quantum_circuit_dynamics",
]


[docs] class CircuitsFromTN: """ Class to read a distribution of quantum circuit from a TN state of qtealeaves. **Arguments** filename : str Name of the source file where the TTN is stored. gates : list of strings sorted alphabetically If the circuit is not collapsed, list of gates involved in the circuit, with idle and busy excluded. If the circuit is collapsed, list of supergates representing the possible local states. shape : tuple of ints Shape of the lattice where the circuit is encoded, including fictitious sites added to fit in a TTN. real_shape : tuple of ints Shape of the circuit encoded in the lattice, excluding fictitious sites added to fit in a TTN. collapse_map : None or quantummodels.collapsedquantummodel.CollapseMap None if the circuit is not collapsed, else CollapseMap object encoding the supergate to time-step translation. max_data_site : int, optional Maximum number of distribution elements to be stored. Default is 1000. min_prob : int, optional Minimal probability for a sampled circuit to be stored. Default is 0.0001. **Attributes** circuits_and_probabilities : list of (AbstractCircuitState, float) Sampled circuits with the associated probabilities. sampled_probability : float Total probability sampled. If the stored sampling is complete, this value is 1. """ def __init__( self, filename, gates, shape, real_shape, collapse_map, num_samples=1000, max_data_size=1000, min_prob=10 ** (-4), ): if "ttn" in filename: psi = qtl.emulator.TTN.read(filename, qtl.tensors.TensorBackend()) elif "mps" in filename: psi = qtl.emulator.MPS.read(filename, qtl.tensors.TensorBackend()) else: raise IOError(f"File {filename} is not a readable tensor network file") probs = psi.meas_unbiased_probabilities( num_samples, do_return_samples=False, precision=15 ) circuits_and_probabilities = [] if collapse_map is not None: supergatesgates = collapse_map.collapsed_gates for measured_state, upper_lower in probs.items(): lower, upper = upper_lower probability = float(upper - lower) if "," in measured_state: measured_state = measured_state.split(",") supergates_list = [supergatesgates[int(ii)] for ii in measured_state] state = np.array(collapse_map.decollapse_state(supergates_list)) state = AbstractCircuitState( state[: real_shape[0], : real_shape[1]], "Circuit from ttn - probability = " + str(probability), ) circuits_and_probabilities.append((state, probability)) else: gates = ["idle"] + gates + ["busy"] backmapping_vector_observable = HilbertCurveMap( 2, shape ).backmapping_vector_observable for measured_state, upper_lower in probs.items(): lower, upper = upper_lower probability = float(upper - lower) if probability > min_prob: gates_list = [gates[int(ii)] for ii in measured_state] state = backmapping_vector_observable(np.array(gates_list)) state = AbstractCircuitState( state[: real_shape[0], : real_shape[1]], "Circuit from ttn - probability = " + str(probability), ) circuits_and_probabilities.append((state, probability)) circuits_and_probabilities.sort(key=lambda x: -x[1]) self.circuits_and_probabilities = circuits_and_probabilities[:max_data_size] self.sampled_probability = sum( circuit_and_probability[1] for circuit_and_probability in circuits_and_probabilities )
[docs] def get_classical_energy_statistics(self, hamiltonian): """ Returns the statistics associated to a classical observable (energy), in terms of values vs probabilities. **Parameters** 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] 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_vs_probability : dictionary A dictionary that associates to each energy a probability of being measured. """ energy_vs_probability = {} for circuit, probability in self.circuits_and_probabilities: energy = float(circuit.get_energy(hamiltonian)) if energy in energy_vs_probability: energy_vs_probability[energy] += probability else: energy_vs_probability[energy] = probability return energy_vs_probability
[docs] def get_equivalence_ratio_and_best_state( self, input_circuit, hamiltonian, max_iter=100 ): """ Generates the probability of sampling a circuit that is equivalent to the input circuit, and the sapled equivalent circuit qith minimum energy. **Parameters** input_circuit : AbstractCircuitState Input circuit that must be equivalent to the sampled circuits. 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] 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]. max_iter : int, optional Max number of circuit for which the equivalence has to be checked. The default is 100. **Returns** equivalent_probability : float Sampled probability of getting an equivalent circuit. not_equivalent_probability : float Sampled probability of getting a non equivalent circuit. best_state : AbstractCircuitState Equivalent sampled circuit that minimizes the energy. The associated probability and energy are encoded in best_state.name . """ equivalent_probability = 0 not_equivalent_probability = 0 iter_num = 0 best = { "circuit": None, "energy": 10 ** (12), "probability": 0, } for circuit, probability in self.circuits_and_probabilities: if iter_num < max_iter: if check_circuit_equivalence(circuit, input_circuit): equivalent_probability += probability energy = circuit.get_energy(hamiltonian) if energy < best["energy"]: best = { "circuit": circuit, "energy": energy, "probability": probability, } else: not_equivalent_probability += probability iter_num += 1 best_state = best["circuit"] best_state.name = ( "QA lowest energy equivalent state - E = " + str(np.round(best["energy"], 5)) + " - P = " + str(np.round(best["probability"], 5)) ) return equivalent_probability, not_equivalent_probability, best_state
[docs] def quantum_circuit_dynamics( input_circuit, machine, qcd_instructions, simulation_instructions, max_results_size=100, del_data=False, ): """ Evolve a state representing a quantum circuit qith a quantum dynamics. The dynamics is simualted with QuantumTEA. **Arguments** input_circuit : AbstractCircuitState Classical circuit state, the initial state of the dynamics. machine : dictionary 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] 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]. gates : set Gates enabled on the machine (virtual gates included). qcd_instructions : dictionary annealing_T : float Annealing time. rules_classes : str or list of ints, optional A list of the ints identifying the rule classes that we want to generate. Default is "all" and generates all the rule classes. generators : "std" or list of generators, optional The list of generators producing the rules to be used. Default is "std", in this case a standard list of rules is used. annealing_schedule : char, optional Pulse schedule for the annealing process. Default is A. max_rules_volume : int, optional Maximum volume allowed for the rules to be included in the driving Hamiltonian. Default is 100. simulation_instructions : dictionary simulation_name : str Name identifiung the folder of simulation input and output. delta_t : float Time step duration for the simualtion. max_bond_dimension : int Max bond dimension for TN simulation. svd_mode : char, optional Singular value decomposition mode. Default is "V". See Quantum Tea Leaves for more informations. time_evolution_mode : int, optional Time evolution mode. Default is 5. See Quantum Tea Leaves for more informations. mpo_mode : int, optional Matrix product operators mode. Default is 4. See Quantum Tea Leaves for more informations. gs_search_iter : int, optional Number of iterations for the initial GS search. Default is 3. collapsed : bool, optional If true, each possible configuration of qubit lattice is represented by the state of a single qudit. Default is False. reflection_sym : bool, optional If true, enable reflection symmetry on the qubit axis. Default is False. max_results_size : int, optional Max size of the observable evolution in output. Default is 100. del_data ; bool, optional If true, simulation data are deleted at the end of the simulation. Default is false. **Returns** output_dictionary : dictionary qcd_parameters : dictionary software_version : str Version of the software used for the simulation. initial_state : CircuitState See Parameters machine : dictionary See Parameters qcd_instructions : dictionary See Parameters simulation_instructions : dictionary See Parameters qcd_results : dictionary computational_time : float total computational time of the simulated annealing. initial_energy : float Initial energy best_state : AbstractCircuitState Describes the lowest infidelity equivalent state at the end of the simulation. final_equivalence_ratio : float, float Salpled probability of a final circuit equivalent to the initial one, sampled probability of a final circuit not equivalent to the initial one. energy_evolution : numpy.array A matrix that associate to each time the expectation value of the energy at that time. inf_energy_evolution : list of (float, dictionary) A list of tuples, the first element of each tuple is the time, the second element is a dictionary that associates to each possible enrgy a probability. norm_evolution : list of (float, float) A matrix that associate to each time the total sampled probability of the state at that time. """ if input_circuit.dim != 2: raise ValueError("Quantum dinamics only implemented for 1d qubit lattices") if input_circuit.is_continuous: raise ValueError("Quantum dinamics only implemented for discrete gates set.") if "annealing_schedule" not in qcd_instructions: qcd_instructions["annealing_schedule"] = "A" if "max_rules_volume" not in qcd_instructions: qcd_instructions["max_rules_volume"] = 100 if "rules_classes" not in qcd_instructions: qcd_instructions["rules_classes"] = "all" if "generators" not in qcd_instructions: qcd_instructions["generators"] = "std" if "svd_mode" not in simulation_instructions: simulation_instructions["svd_mode"] = "V" if "gs_search_iter" not in simulation_instructions: simulation_instructions["gs_search_iter"] = 3 if "time_evolution_mode" not in simulation_instructions: simulation_instructions["time_evolution_mode"] = 5 if "mpo_mode" not in simulation_instructions: simulation_instructions["mpo_mode"] = 4 if "collapsed" not in simulation_instructions: simulation_instructions["collapsed"] = False if "reflection_sym" not in simulation_instructions: simulation_instructions["reflection_sym"] = False if "simulation_name" not in simulation_instructions: simulation_instructions["simulation_name"] = "unnamed" if "has_checkpoints" not in simulation_instructions: simulation_instructions["has_checkpoints"] = True machine["gates"] = sorted(list(machine["gates"])) # ------------ Build quantum model --------------- times = 2 ** math.ceil(math.log2(input_circuit.times)) if simulation_instructions["collapsed"]: tn_type = 6 qubits = input_circuit.qubits[0] shape = (times, input_circuit.qubits[0]) collapse_map = CollapseMap( machine["gates"], input_circuit.times, times, qubits, reflection_sym=simulation_instructions["reflection_sym"], ) (shape, my_ops, my_obs, model,) = get_collapsed_quantum_compilation_model( input_circuit, machine, qcd_instructions, shape, collapse_map, ) else: tn_type = 5 if simulation_instructions["reflection_sym"]: raise NotImplementedError( "Reflection symmetry not yet implemented in non-collapsed symulation." ) qubits = 2 ** math.ceil(math.log2(input_circuit.qubits[0])) shape = (times, qubits) collapse_map = None shape, my_ops, my_obs, model = get_quantum_compilation_model( input_circuit, machine, qcd_instructions, shape ) input_folder = ( "simulations_data/input_qcd_" + simulation_instructions["simulation_name"] ) output_folder = ( "simulations_data/output_qcd_" + simulation_instructions["simulation_name"] ) print( "The symulation involves ", len(my_ops.ops), " local operators with local dimension", len(my_ops.ops["id"]), ".", "Output folder: ", output_folder, ) if del_data: input_folder = "tmp/" + input_folder output_folder = "tmp/" + output_folder my_obs += qtl.observables.TNState2File(output_folder + "/system_state", "F") # ---------------- Define the dynamics ---------------- annealing_t = qcd_instructions["annealing_T"] delta_t = simulation_instructions["delta_t"] steps = int(annealing_t / delta_t) measurement_period = int(annealing_t / (delta_t * max_results_size)) if measurement_period == 0: measurement_period = 1 quench = qtl.DynamicsQuench( [delta_t] * steps, measurement_period=measurement_period, time_evolution_mode=simulation_instructions["time_evolution_mode"], ) if qcd_instructions["annealing_schedule"] == "A": quench["alpha"] = lambda tt, params: 1 - tt / params["annealing_T"] quench["beta"] = ( lambda tt, params: 2 * tt / params["annealing_T"] if (tt < params["annealing_T"] / 2) else 2 - 2 * tt / params["annealing_T"] ) quench["gamma"] = lambda tt, params: tt / params["annealing_T"] elif qcd_instructions["annealing_schedule"] == "B": quench["alpha"] = ( lambda tt, params: 1 - 2 * tt / params["annealing_T"] if (tt < params["annealing_T"] / 2) else 0 ) quench["beta"] = ( lambda tt, params: 2 * tt / params["annealing_T"] if (tt < params["annealing_T"] / 2) else 2 - 2 * tt / params["annealing_T"] ) quench["gamma"] = ( lambda tt, params: 0 if (tt < params["annealing_T"] / 2) else -1 + 2 * tt / params["annealing_T"] ) else: raise NotImplementedError("This annealing schedule option is not implemented.") params = { "L": shape, "alpha": 1.0, "beta": 0.0, "gamma": 0.0, "annealing_T": annealing_t, "annealing_schedule": qcd_instructions["annealing_schedule"], "Quenches": [quench], "exclude_from_hash": ["Quenches", "energies"], } # ---------------- Running or loading the simulation ---------------- my_conv = qtl.convergence_parameters.TNConvergenceParameters( max_iter=simulation_instructions["gs_search_iter"], max_bond_dimension=simulation_instructions["max_bond_dimension"], data_type="C", svd_ctrl=simulation_instructions["svd_mode"], ) simulation = qtl.QuantumGreenTeaSimulation( model, my_ops, my_conv, my_obs, tensor_backend=2, tn_type=tn_type, mpo_mode=simulation_instructions["mpo_mode"], folder_name_input=input_folder, folder_name_output=output_folder, store_checkpoints=simulation_instructions["has_checkpoints"], verbosity=0, ) sim_status = simulation.status([params]) if os.path.isfile(output_folder + "/SIM_TIME"): print( "\nThis simulation has been runned in past and is stored in " + output_folder + ". Postprocessing data...\n" ) with open(output_folder + "/SIM_TIME", "r") as file: computational_time = float(file.read()) elif sim_status[0] == 1: print("\nRunning simulation...\n") tic = tictoc() simulation.run( [params], delete_existing_folder=not simulation_instructions["has_checkpoints"], ) toc = tictoc() computational_time = toc - tic with open(output_folder + "/SIM_TIME", "a") as file: file.write(str(computational_time)) elif sim_status[1] == 1: print("\nRestarting crashed simulation...") print("WARNING: the computational time will restart!\n") tic = tictoc() simulation.run( [params], delete_existing_folder=not simulation_instructions["has_checkpoints"], ) toc = tictoc() computational_time = 0 with open(output_folder + "/SIM_TIME", "a") as file: file.write(str(computational_time)) else: raise ValueError( "Simulation status is " + str(sim_status) + " and " + output_folder + "/SIM_TIME does not exist." ) # ---------------- Postprocessing simulation results ---------------- real_circuit_shape = (input_circuit.times, input_circuit.qubits[0]) qcd_parameters = { "software_version": "vulqano version " + str(__version__), "input_circuit": input_circuit, "machine": machine, "qcd_instructions": qcd_instructions, "simulation_instructions": simulation_instructions, } qcd_results = {} qcd_results["computational_time"] = computational_time # Initial state static_obs = simulation.get_static_obs(params) initial_ttn_state = static_obs[output_folder + "/system_state"] circuits_and_probabilities = CircuitsFromTN( initial_ttn_state, machine["gates"], shape, real_circuit_shape, collapse_map ).circuits_and_probabilities probabilities = [ circuit_and_probability[1] for circuit_and_probability in circuits_and_probabilities ] max_index = probabilities.index(max(probabilities)) initial_circuit, max_prob = circuits_and_probabilities[max_index] if max_prob < 0.99: raise ValueError( "The state at the beginning of the annealing process " + "seems not to be a state of the computational basis." ) if not np.array_equal(initial_circuit.vector, input_circuit.vector): raise ValueError( "The state at the beginning of the annealing process " + "does not encode the input circuit." ) qcd_results["initial_energy"] = initial_circuit.get_energy(machine["hamiltonian"]) # Energy evolution dynamics_obs = [ xxx for xxx in simulation.get_dynamic_obs(params)[0] if xxx is not None ] energies = [float(entry["energy"]) for entry in dynamics_obs] time_steps = np.cumsum(params["Quenches"][0].get_dt_grid(params))[ ::measurement_period ][: len(dynamics_obs)] qcd_results["energy_evolution"] = list(zip(time_steps, energies)) # normalization, infidelity energy, infidelity energy std evolution ttn_states = [ dynamics_obs[ii][output_folder + "/system_state"] for ii in range(len(dynamics_obs)) ] norms = [] infidelities = [] for ttn_state in ttn_states: circuits_from_tn = CircuitsFromTN( ttn_state, machine["gates"], shape, real_circuit_shape, collapse_map ) infidelities.append( circuits_from_tn.get_classical_energy_statistics(machine["hamiltonian"]) ) norms.append(circuits_from_tn.sampled_probability) qcd_results["norm_evolution"] = list(zip(time_steps, norms)) qcd_results["inf_energy_evolution"] = list(zip(time_steps, infidelities)) # Final circuit states final_ttn_state = dynamics_obs[-1][output_folder + "/system_state"] equivalent_probability, not_equivalent_probability, best_state = CircuitsFromTN( final_ttn_state, machine["gates"], shape, real_circuit_shape, collapse_map ).get_equivalence_ratio_and_best_state( input_circuit, machine["hamiltonian"], ) qcd_results["final_equivalence_ratio"] = ( equivalent_probability, not_equivalent_probability, ) qcd_results["best_state"] = best_state # If the simulation data durectory is tmp, remove it if os.path.exists("tmp") and os.path.isdir("tmp"): shutil.rmtree("tmp") output_dictionary = {"qcd_parameters": qcd_parameters, "qcd_results": qcd_results} return output_dictionary
def base_unit_test(input_circuit, machine, qcd_instructions, simulation_instructions): """ Define simple general scheme for unit tests of the quantum annealing. **Arguments** input_circuit : vulqano.states.AbstractCircuitState The circuit state (many-body classical state) representing te circuit to optimize. machine : dictionary 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] 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]. gates : set Gates enabled on the machine (virtual gates included). qcd_instructions : dictionary See .quantum_circuit_dynamics . simulation_instructions : dictionary See .quantum_circuit_dynamics . **Returns** bool True if each possible final circuit is equivalent to the inital one. """ simulation_instructions["has_checkpoints"] = False output_dictionary = quantum_circuit_dynamics( input_circuit, machine, qcd_instructions, simulation_instructions, del_data=True ) qcd_results = output_dictionary["qcd_results"] print("\n\nComputational time: ", qcd_results["computational_time"]) print( "Probability of equivalent final circuit >= ", qcd_results["final_equivalence_ratio"][0], ) return qcd_results["final_equivalence_ratio"][0] > 1 - 10 ** (-2) def unit_test(): """ Perform a quantum annealing with a small circuit and check for the final circuits infidelity. **Returns** bool True if each possible final circuit is equivalent to the inital one. """ # ---------------- Set the optimiziation problem ---------------- input_circuit = AbstractCircuitState( np.array( [ ["Z", "Z", "CZ", "busy"], ["SWAP", "busy", "Z", "idle"], ["Z", "idle", "CZ", "busy"], ] ), "input_circuit", ) n_q = input_circuit.qubits[0] n_t = input_circuit.times - 1 circuit_area_mask = np.concatenate( ( np.full((0, n_q), False), np.full((n_t, n_q), True), np.full((1, n_q), False), ) ) machine = { "gates": {"Z", "CZ", "SWAP"}, "hamiltonian": ( (np.array([["Z"]]), 1, circuit_area_mask), (np.array([["idle"]]), 1, circuit_area_mask), (np.array([["CZ"]]), 5, circuit_area_mask), (np.array([["SWAP"]]), 50, circuit_area_mask), (np.array([["CZ", "any", "CZ"]]), 50, circuit_area_mask), (np.full((1, n_q), "idle"), -n_q * 1, circuit_area_mask), (np.array([["Z"]]), 50, np.logical_not(circuit_area_mask)), (np.array([["CZ"]]), 50, np.logical_not(circuit_area_mask)), ), } qcd_instructions = { "annealing_T": 0.25, } simulation_instructions = { "delta_t": 0.05, "max_bond_dimension": 5, "simulation_name": "unit_test", } return base_unit_test( input_circuit, machine, qcd_instructions, simulation_instructions ) def unit_test_collapsed(): """ Perform a collapsed quantum annealing with a small circuit and check for the final circuits infidelity. **Returns** bool True if each possible final circuit is equivalent to the inital one. """ input_circuit = AbstractCircuitState( np.array( [ ["CZ", "busy"], ["SWAP", "busy"], ["CZ", "busy"], ["CZ", "busy"], ["CZ", "busy"], ["SWAP", "busy"], ["CZ", "busy"], ] ), "input_circuit", ) n_q = input_circuit.qubits[0] n_t = input_circuit.times - 1 circuit_area_mask = np.concatenate( ( np.full((0, n_q), False), np.full((n_t, n_q), True), np.full((1, n_q), False), ) ) machine = { "gates": {"H", "CZ", "SWAP"}, "hamiltonian": ( (np.array([["idle"]]), 1, circuit_area_mask), (np.array([["CZ"]]), 5, circuit_area_mask), (np.array([["SWAP"]]), 50, circuit_area_mask), (np.array([["CZ", "any", "CZ"]]), 50, circuit_area_mask), (np.full((1, n_q), "idle"), -n_q * 1, circuit_area_mask), (np.array([["CZ"]]), 50, np.logical_not(circuit_area_mask)), ), } qcd_instructions = { "annealing_T": 0.25, } simulation_instructions = { "delta_t": 0.05, "max_bond_dimension": 25, "collapsed": True, "simulation_name": "unit_test_collapsed", } return base_unit_test( input_circuit, machine, qcd_instructions, simulation_instructions ) def unit_test_collapsed_sym(): """ Perform a collapsed quantum annealing with a small circuit and check for the final circuits infidelity. Enable reflection symmetry over qubit axis. **Returns** bool True if each possible final circuit is equivalent to the inital one. """ input_circuit = AbstractCircuitState( np.array( [ ["idle", "CZ", "busy", "idle"], ["CZ", "busy", "CZ", "busy"], ["idle", "CZ", "busy", "idle"], ["idle", "CZ", "busy", "idle"], ["H", "CZ", "busy", "H"], ["SWAP", "busy", "SWAP", "busy"], ["idle", "CZ", "busy", "idle"], ] ), "input_circuit", ) n_q = input_circuit.qubits[0] n_t = input_circuit.times - 1 circuit_area_mask = np.concatenate( ( np.full((0, n_q), False), np.full((n_t, n_q), True), np.full((1, n_q), False), ) ) machine = { "gates": {"H", "CZ", "SWAP"}, "hamiltonian": ( (np.array([["idle", "any", "any", "idle"]]), 2, circuit_area_mask), (np.array([["any", "idle", "idle", "any"]]), 2, circuit_area_mask), (np.array([["any", "CZ", "busy", "any"]]), 5, circuit_area_mask), (np.array([["CZ", "busy", "CZ", "busy"]]), 35, circuit_area_mask), (np.array([["any", "SWAP", "busy", "any"]]), 25, circuit_area_mask), (np.array([["SWAP", "busy", "SWAP", "busy"]]), 50, circuit_area_mask), (np.full((1, n_q), "idle"), -n_q * 1, circuit_area_mask), ( np.array([["idle", "any", "any", "idle"]]), 50, np.logical_not(circuit_area_mask), ), ( np.array([["any", "idle", "idle", "any"]]), 50, np.logical_not(circuit_area_mask), ), ( np.array([["any", "CZ", "busy", "any"]]), 50, np.logical_not(circuit_area_mask), ), ( np.array([["CZ", "busy", "CZ", "busy"]]), 50, np.logical_not(circuit_area_mask), ), ), } qcd_instructions = { "annealing_T": 0.25, "generators": SYM_4_RULES_GENERATORS, } simulation_instructions = { "delta_t": 0.05, "max_bond_dimension": 25, "collapsed": True, "reflection_sym": True, "simulation_name": "unit_test_collapsed_sym", } return base_unit_test( input_circuit, machine, qcd_instructions, simulation_instructions ) if __name__ == "__main__": print(unit_test()) print(unit_test_collapsed()) print(unit_test_collapsed_sym())