def result_collector(self, result: dl.DUInt(dl.DSize(32))) -> bool: """Method to receive the results and update state. Parameters ---------- result : int Result from quantum HAL node. Returns ------- bool Flag to signal to the circuit node to send again. Raises ------ DeltaRuntimeExit Stops the deltaflow program when statistics have been aggregated """ measurement = dl.lib.measurement_unpacker(result, [0]) self._results.append(measurement) self._counter += 1 if self._counter == self._repetitions: print("Expectation value: " + f"{np.round(np.sum(self._results) / self._repetitions, 1)}") raise dl.DeltaRuntimeExit return True
def experiment_stopper(completed: dl.DInt(dl.DSize(8))) -> dl.Void: if completed: if completed == 1: raise dl.DeltaRuntimeExit else: print(f"The experiment returned error code: {completed}") raise RuntimeError("Experiment returned an error", completed)
def testbench(node): data_array = generate_data_vector(C_N_BITS, C_N_INPUTS) # Temporary - needs df.DArray => migen.Array support data_vector = 0 logging.debug(f'data sent to DUT {data_array}') for i in range(C_N_INPUTS): data_vector += data_array[i] << C_N_BITS * i data_vector = dl.DInt( dl.DSize(C_VECTOR_LEN)).from_numpy_object(data_vector) for cmd in range(0x01, 0x06): node.send(TbVals(data=data_vector, cmd=cmd)) result = node.receive('result') error = node.receive('error') logging.debug(f'cmd: {cmd}') exp_err = 0 if cmd == Commands.MIN: exp_res = np.min(data_array) logging.debug(f'result: {result}, expected: {exp_res}') assert result == exp_res elif cmd == Commands.MAX: exp_res = np.max(data_array) logging.debug(f'result: {result}, expected: {exp_res}') assert result == exp_res elif cmd == Commands.SUM: exp_res = np.sum(data_array) logging.debug(f'result: {result}, expected: {exp_res}') assert result == exp_res elif cmd == Commands.AVG: exp_res_low = trunc(np.mean(data_array)) - 1 exp_res_high = int(np.mean(data_array)) + 1 exp_res = np.mean(data_array) logging.debug(f'result: {result}, expected: {exp_res}') assert result >= exp_res_low assert result <= exp_res_high else: exp_err = 1 result = -1 exp_res = -1 assert error == exp_err raise dl.DeltaRuntimeExit
def get_graph(): """Return the experiments graph `DeltaGraph` and data store instances. Note that the aggregator and commanger files can be provided with `vcd_name` which will lead to saving VCD of all signals for further debugging. """ result_storage = dl.lib.StateSaver(int) cmds_storage = dl.lib.StateSaver(dl.DUInt(dl.DSize(32))) hal_template = dl.lib.hal_template with dl.DeltaGraph() as graph: ph_hal_result = dl.placeholder_node_factory() ph_commander = dl.placeholder_node_factory() # aggregator node of HAL results result_aggregator = Aggregator( name="result_aggregator", vcd_name=None).call(hal_result=ph_hal_result, shot_completed=ph_commander.shot_completed) # commander node to send HAL instructions command_sender = Commander( name="command_sender", vcd_name=None).call(angle=result_aggregator.next_angle) hal_result = hal_template.call(hal_command=command_sender.hal_command) # local store for experiment results result_storage.save(result_aggregator.agg_result) cmds_storage.save(command_sender.hal_command) # tie up placeholders ph_hal_result.specify_by_node(hal_result) ph_commander.specify_by_node(command_sender) # listen for flag to stop runtime experiment_stopper(result_aggregator.completed) return graph, result_storage, cmds_storage
def migen_body(self, template): # creation of input/output ports angle = template.add_pa_in_port( 'angle', dl.DOptional(dl.DRaw(dl.DUInt(dl.DSize(ANGLE_MEMORY_WIDTH)))) ) hal_command = template.add_pa_out_port('hal_command', dl.DUInt(dl.DSize(32))) shot_completed = template.add_pa_out_port('shot_completed', dl.DBool()) # set up internal signals _rotation_command = Signal(32) self.comb += ( # declare input/output ports always happy to receive/transmit data angle.ready.eq(1) ) # define finite state machine for triggering HAL command sequences self.submodules.commander_fsm = \ commander_fsm = FSM(reset_state="STATE_PREPARATION") # waits for angle signal before kicking off HAL sequence commander_fsm.act( "STATE_PREPARATION", NextValue(shot_completed.valid, 0), If( angle.valid == 1, NextValue( _rotation_command, dl.lib.command_creator("RX", argument=angle.data) ), NextValue(hal_command.valid, 1), NextValue( hal_command.data, dl.lib.command_creator("STATE_PREPARATION") ), NextState("ROTATION") ).Else( NextValue(hal_command.valid, 0), NextState("STATE_PREPARATION") ) ) # align HAL command to rotation commander_fsm.act( "ROTATION", NextValue(hal_command.valid, 1), NextValue(hal_command.data, _rotation_command), NextValue(shot_completed.valid, 0), NextState("STATE_MEASURE") ) # align HAL command to state measure commander_fsm.act( "STATE_MEASURE", NextValue(hal_command.valid, 1), NextValue(hal_command.data, dl.lib.command_creator("STATE_MEASURE")), NextValue(shot_completed.valid, 1), NextValue(shot_completed.data, 1), NextState("STATE_PREPARATION") )
def migen_body(self, template): # creation of input/output ports shot_completed = template.add_pa_in_port('shot_completed', dl.DOptional(dl.DBool())) hal_result = template.add_pa_in_port( 'hal_result', dl.DOptional(dl.DUInt(dl.DSize(32))) ) agg_result = template.add_pa_out_port('agg_result', dl.DInt(dl.DSize(32))) # Completed is currently returning a simple 0/1 value but we make space # for an error code to be returned e.g. 255, 0b11111111 can be in the # future used to represent an error. completed = template.add_pa_out_port('completed', dl.DInt(dl.DSize(8))) next_angle = template.add_pa_out_port( 'next_angle', dl.DRaw(dl.DUInt(dl.DSize(ANGLE_MEMORY_WIDTH))) ) # generate a ROM of 10-bit angle values angles = generate_angles(RESOLUTION) self.specials.angle_memory = angle_memory = Memory( ANGLE_MEMORY_WIDTH, len(angles), init=angles, name="ANGLE_ROM" ) angle_rom_port = angle_memory.get_port(write_capable=False) self.specials += angle_rom_port # set up internal signals _shots_counter = Signal(32) _high_hal_results = Signal(32) _reset_high_hal = Signal(1) _angle_rom_index = Signal(RESOLUTION+1) self.comb += ( # declare input/output ports always happy to receive/transmit data hal_result.ready.eq(1), shot_completed.ready.eq(1), # align angle ROM address with ROM index signal angle_rom_port.adr.eq(_angle_rom_index), ) # define finite state machine for triggering angle and result signals self.submodules.rabi_aggregator_fsm = \ rabi_aggregator_fsm = FSM(reset_state="IDLE") # Logic to accumulate measurements self.sync += ( If (_reset_high_hal == 1, _high_hal_results.eq(0) ).Else ( If (hal_result.valid == 1, If ((hal_result.data & dl.lib.Masks.MEASUREMENTS.value) == 1, _high_hal_results.eq(_high_hal_results + 1) ) ) ) ) # waits for the experiment to be kicked off rabi_aggregator_fsm.act( "IDLE", NextValue(agg_result.valid, 0), NextValue(next_angle.valid, 0), NextValue(completed.valid, 0), NextValue(_shots_counter, 0), NextValue(_reset_high_hal, 1), NextState("DO_SHOTS") ) rabi_aggregator_fsm.act( "DO_SHOTS", NextValue(agg_result.valid, 0), NextValue(_reset_high_hal, 0), If (_shots_counter == REPETITIONS, NextState("CHECK_IF_COMPLETE"), NextValue(_angle_rom_index, _angle_rom_index + 1), NextValue(agg_result.data, _high_hal_results), NextValue(agg_result.valid, 1), NextValue(_reset_high_hal, 1), ).Else ( NextValue(next_angle.data, angle_rom_port.dat_r), NextValue(next_angle.valid, 1), NextState("WAIT_SHOT") ) ) rabi_aggregator_fsm.act( "WAIT_SHOT", NextValue(next_angle.valid, 0), If ((shot_completed.valid == 1) & (shot_completed.data == 1), NextValue(_shots_counter, _shots_counter + 1), NextState("DO_SHOTS"), ) ) rabi_aggregator_fsm.act( "CHECK_IF_COMPLETE", NextState("IDLE"), NextValue(agg_result.valid, 0), If(_angle_rom_index == 2 ** RESOLUTION, NextValue(completed.data, 1), NextValue(completed.valid, 1), ) )
import numpy as np import deltalanguage as dl ### ---------------------------- CONSTRUCT NODES -------------------------- ### # One node to send circuit to HAL node, another to digest result from HAL node @dl.Interactive([("input_params", dl.DArray(int, dl.DSize(6))), ("repeat", bool)], dl.DUInt(dl.DSize(32))) def send_gate_sequence(node): """Interactive node to define the circuit. Accepts the circuit parameters and sends the HAL commands to the HAL node. Parameters ---------- node : PythonNode The node that sends the HAL command outputs. """ params = node.receive("input_params") repeat = True # send each gate at least once while repeat: node.send(dl.lib.command_creator("STATE_PREPARATION")) node.send(dl.lib.command_creator("RX", argument=params[0])) node.send(dl.lib.command_creator("RZ", argument=params[1])) node.send(dl.lib.command_creator("RY", argument=params[2])) node.send(dl.lib.command_creator("R", qubit=0)) node.send(dl.lib.command_creator("PIXY", argument=params[3])) node.send(dl.lib.command_creator("PIYZ", argument=params[4]))
def migen_body(self, template): # generics N_BITS = template.generics["N_BITS"] # 1-64 N_INPUTS = template.generics["N_INPUTS"] TREE_DEPTH = int(ceil(log2(N_INPUTS))) # inputs self.d_in = template.add_pa_in_port( 'd_in', dl.DOptional(dl.DInt(dl.DSize(N_BITS * N_INPUTS)))) self.cmd = template.add_pa_in_port('cmd', dl.DOptional(dl.DInt())) # outputs self.d_out = template.add_pa_out_port('d_out', dl.DInt()) self.err = template.add_pa_out_port('error', dl.DInt()) # input length correction [need a power of 2 sized tree] N_INPUTS_CORR = pow(2, TREE_DEPTH) # internals # correct the size of the input tree to be a power of 2 # and register the inputs self.d_in_full_reg = Signal(N_INPUTS_CORR * N_BITS) self.d_in_valid_reg = Signal(1) self.cmd_data_reg = Signal(8) self.cmd_valid_reg = Signal(1) # register outputs self.d_out_data_reg = Signal(N_BITS + TREE_DEPTH) self.d_out_valid_reg = Signal(1) self.err_data_reg = Signal(1) self.err_valid_reg = Signal(1) # create the 2D array of data [INPUTS x TREE_DEPTH] to route # all the core units in an iterative way. The number of bits is incremented # at each stage to account for the carry in additions. self.d_pipe = Array( Array(Signal(N_BITS + b) for a in range(N_INPUTS_CORR)) for b in range(TREE_DEPTH + 1)) # create the 2D array of error signals. self.e_pipe = Array( Array(Signal(N_BITS) for a in range(N_INPUTS_CORR)) for b in range(TREE_DEPTH)) ### # correct input vector length to match a power of 2. # fill non-provided inputs with 0's (affects mean and minimum) self.sync += [ self.d_in_full_reg.eq(self.d_in.data), self.d_in_valid_reg.eq(self.d_in.valid), self.cmd_data_reg.eq(self.cmd.data), self.cmd_valid_reg.eq(self.cmd.valid) ] # wiring inputs to the first stage of the tree for i in range(N_INPUTS_CORR): self.comb += [ self.d_pipe[0][i].eq(self.d_in_full_reg[N_BITS * i:N_BITS * (i + 1)]) ] # instantiation of the core units. for j in range(TREE_DEPTH): for i in range(int(N_INPUTS_CORR / (pow(2, j + 1)))): self.submodules += CoreUnit(self.d_pipe[j][2 * i], self.d_pipe[j][2 * i + 1], self.d_pipe[j + 1][i], self.cmd_data_reg, self.e_pipe[j][i], N_BITS) # error signal propagation. If any of the single units have # a high error signal, the error is propagated to the node's output. self.comb += [ If(self.e_pipe[j][i] == 1, self.err_data_reg.eq(1)) ] self.comb += [ self.d_in.ready.eq(1), self.cmd.ready.eq(1), self.d_out_data_reg.eq(self.d_pipe[TREE_DEPTH][0]), If(self.d_in_valid_reg, self.err_valid_reg.eq(1), self.d_out_valid_reg.eq(1)).Else(self.err_valid_reg.eq(0)) ] self.sync += [ self.d_out.data.eq(self.d_out_data_reg), self.d_out.valid.eq(self.d_out_valid_reg), self.err.data.eq(self.err_data_reg), self.err.valid.eq(self.err_valid_reg) ]
def experiment_stopper(completed: dl.DInt(dl.DSize(8))) -> dl.Void: raise dl.DeltaRuntimeExit
self.sync += [ self.d_out.data.eq(self.d_out_data_reg), self.d_out.valid.eq(self.d_out_valid_reg), self.err.data.eq(self.err_data_reg), self.err.valid.eq(self.err_valid_reg) ] def generate_data_vector(N_BITS, N_INPUTS): return np.random.randint(0, pow(2, N_BITS), size=N_INPUTS) TbT, TbVals = dl.make_forked_return({ 'cmd': dl.DInt(), 'data': dl.DInt(dl.DSize(C_VECTOR_LEN)) }) @dl.Interactive([('result', dl.DInt()), ('error', dl.DInt())], TbT) def testbench(node): data_array = generate_data_vector(C_N_BITS, C_N_INPUTS) # Temporary - needs df.DArray => migen.Array support data_vector = 0 logging.debug(f'data sent to DUT {data_array}') for i in range(C_N_INPUTS): data_vector += data_array[i] << C_N_BITS * i data_vector = dl.DInt( dl.DSize(C_VECTOR_LEN)).from_numpy_object(data_vector)
def migen_body(self, template): template.add_pa_in_port('i', dl.DOptional(dl.DInt(dl.DSize(8))))
def method_func_no_output(self, i: dl.DInt(dl.DSize(8))) -> dl.Void: print(i + 1)
def multi_body_no_output(i: dl.DInt(dl.DSize(8))) -> dl.Void: print(i)
def forked_return_output(x: dl.DInt(dl.DSize(8)), y: dl.DInt(dl.DSize(8))) -> ForkedReturnT: return ForkedReturn(a=0, b=1, c=1, d=0)
class Foo(): def __init__(self): pass @dl.DeltaMethodBlock() def method_func_no_output(self, i: dl.DInt(dl.DSize(8))) -> dl.Void: print(i + 1) class MigenFoo(dl.MigenNodeTemplate): def migen_body(self, template): template.add_pa_in_port('i', dl.DOptional(dl.DInt(dl.DSize(8)))) @dl.Interactive([('i', dl.DInt(dl.DSize(8)))], outputs=dl.Void) def interactive_func_no_output(node: dl.RealNode): a = node.receive('i') template_no_output_no_body = dl.NodeTemplate(name="template_no_output_no_body", inputs=[('i', dl.DInt(dl.DSize(8)))], outputs=dl.Void) @dl.DeltaBlock() def experiment_stopper(completed: dl.DInt(dl.DSize(8))) -> dl.Void: raise dl.DeltaRuntimeExit