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 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): _TIME_RES = 32 # Node inputs self.time_in = template.add_pa_in_port('time_in', dl.DOptional(dl.DUInt())) # Node outputs self.time_out = template.add_pa_out_port('time_out', dl.DUInt()) self.counter_reset = template.add_pa_out_port('counter_reset', dl.DInt()) # Internal signals self.pmt_reg = Signal(_TIME_RES) self.rf_reg = Signal(_TIME_RES) self.pmt_trig = Signal(1) self.rf_trig = Signal(1) self.submodules.fsm = FSM(reset_state="RESET_COUNTER") self.sync += [ If( self.pmt_trig, self.pmt_reg.eq(self.time_in.data), ).Elif(self.fsm.ongoing("RESET_COUNTER"), self.pmt_reg.eq(0)).Else(self.pmt_reg.eq(self.pmt_reg)), If( self.rf_trig, self.rf_reg.eq(self.time_in.data), ).Elif(self.fsm.ongoing("RESET_COUNTER"), self.rf_reg.eq(0)).Else(self.rf_reg.eq(self.rf_reg)) ] """FSM The FSM is used to control the readouts from the HPTDC chip and generate a time signal for the accumulator RESET_COUNTER This is the dinitial state of the FSM at the start of the experiment. It resets the "coarse counter" of the HPTDC chip to establish a TO time reference. WAIT_FOR_PMT This state holds until the PMT timestamp is available at the HPTDC chip readout (first data_ready sync pulse) WAIT_FOR_RF This state holds until the RMT timestamp is available at the HPTDC chip readout (second data_ready sync pulse) SEND_TIME In this state, the difference between t_PMT and t_RF is derived and sent to the accumulator. WAIT_ACC_LATENCY This state is used to wait for any delays on inter-node communication """ self.fsm.act( "RESET_COUNTER", self.pmt_trig.eq(0), self.rf_trig.eq(0), self.time_in.ready.eq(1), self.counter_reset.data.eq(1), # reset counters self.counter_reset.valid.eq(1), NextState("WAIT_FOR_PMT")) self.fsm.act( "WAIT_FOR_PMT", self.counter_reset.data.eq(0), self.time_in.ready.eq(1), If(self.time_in.valid, self.pmt_trig.eq(1), NextState("WAIT_FOR_RF"))) self.fsm.act( "WAIT_FOR_RF", self.time_in.ready.eq(1), If(self.time_in.valid, self.rf_trig.eq(1), NextState("SEND_TIME"))) self.fsm.act("SEND_TIME", self.time_in.ready.eq(1), self.time_out.data.eq(self.rf_reg - self.pmt_reg), self.time_out.valid.eq(1), NextState("WAIT_ACC_LATENCY")) self.fsm.act("WAIT_ACC_LATENCY", If(self.time_in.valid == 0, NextState("RESET_COUNTER")))
If(self.time_in.valid, self.pmt_trig.eq(1), NextState("WAIT_FOR_RF"))) self.fsm.act( "WAIT_FOR_RF", self.time_in.ready.eq(1), If(self.time_in.valid, self.rf_trig.eq(1), NextState("SEND_TIME"))) self.fsm.act("SEND_TIME", self.time_in.ready.eq(1), self.time_out.data.eq(self.rf_reg - self.pmt_reg), self.time_out.valid.eq(1), NextState("WAIT_ACC_LATENCY")) self.fsm.act("WAIT_ACC_LATENCY", If(self.time_in.valid == 0, NextState("RESET_COUNTER"))) @dl.Interactive([('time_out', dl.DUInt()), ('reset', dl.DInt())], dl.DUInt()) def testbench(node): """ Testbench for Timestamper interface node. Starts with random testing and ends with corner cases """ _ITER = 10 for i in range(_ITER): logging.debug(f'---Testbench iter {i}---') time_pmt = random.randint(0, 100) time_rf = random.randint(0, 100) do_test(node, time_pmt, time_rf) raise dl.DeltaRuntimeExit def do_test(node, time_pmt, time_rf):
import numpy as np import logging import deltalanguage as dl AccumT, AccumC = dl.make_forked_return({ 'DAC_command': int, 'DAC_param': int, 'photon': int, 'reset': int }) TIME_RES = 30 @dl.Interactive([('new_time', dl.DUInt()), ('DAC_status', int), ('DAC_voltage', int), ('experiment_start', dl.DOptional(bool))], AccumT) def accumulator(node): """ Accumulator Node This node collects times sent from the counter FPGA node and issues commands to the DAC controller. This allows the node to collect data, fit to the data and feedback to the experiment. The process can loop until some minimum threshold is reached, allowing full automation of micromotion compensation. Inputs: - new_time: Time from Counter node - DAC_status: DAC status from DAC node