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
Esempio n. 2
0
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)
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
    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")
        )
Esempio n. 6
0
    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]))
Esempio n. 8
0
    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
Esempio n. 10
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 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