def create_graph_state(graph: nx.Graph, use_pragmas=True):
    """Write a program to create a graph state according to the specified graph

    A graph state involves Hadamarding all your qubits and then applying a CZ for each
    edge in the graph. A graph state and the ability to measure it however you want gives
    you universal quantum computation. Some good references are

    [MBQC] A One-Way Quantum Computer
           Raussendorf et al.,
           Phys. Rev. Lett. 86, 5188 (2001)
           https://doi.org/10.1103/PhysRevLett.86.5188
           https://arxiv.org/abs/quant-ph/0010033

    [MBCS] Measurement-based quantum computation with cluster states
           Raussendorf et al.,
           Phys. Rev. A 68, 022312 (2003)
           https://dx.doi.org/10.1103/PhysRevA.68.022312
           https://arxiv.org/abs/quant-ph/0301052

    Similar to a Bell state / GHZ state, we can try to prepare a graph state and measure
    how well we've done according to expected parities.

    :param graph: The graph. Nodes are used as arguments to gates, so they should be qubit-like.
    :param use_pragmas: Use COMMUTING_BLOCKS pragmas to hint at the compiler
    :return: A program that constructs a graph state.
    """
    program = Program()
    for q in graph.nodes:
        program += H(q)

    if use_pragmas:
        program += Pragma('COMMUTING_BLOCKS')
    for a, b in graph.edges:
        if use_pragmas:
            program += Pragma('BLOCK')
        program += CZ(a, b)
        if use_pragmas:
            program += Pragma('END_BLOCK')
    if use_pragmas:
        program += Pragma('END_COMMUTING_BLOCKS')

    return program
Beispiel #2
0
 def pragma_freeform_string(self, name, *pragma_names_and_string):
     if len(pragma_names_and_string) == 1:
         freeform_string = pragma_names_and_string[0]
         args = ()
     else:
         *args_identifiers, freeform_string = pragma_names_and_string
         args = list(map(str, args_identifiers))
     # Strip the quotes from start/end of string which are matched
     # by the Lark grammar
     freeform_string = freeform_string[1:-1]
     p = Pragma(str(name), args=args, freeform_string=freeform_string)
     return p
Beispiel #3
0
def basis_state_preps(*qubits):
    """
    Generate a sequence of programs that prepares the measurement
    basis states of some set of qubits in the order such that the qubit
    with highest index is iterated over the most quickly:
    E.g., for ``qubits=(0, 1)``, it returns the circuits::

        I_0 I_1
        I_0 X_1
        X_0 I_1
        X_0 X_1

    :param list qubits: Each qubit to include in the basis state preparation.
    :return: Yields programs for each basis state preparation.
    :rtype: Program
    """
    for prep in cartesian_product([I, X], repeat=len(qubits)):
        basis_prep = Program(Pragma("PRESERVE_BLOCK"))
        for gate, qubit in zip(prep, qubits):
            basis_prep.inst(gate(qubit))
        basis_prep.inst(Pragma("END_PRESERVE_BLOCK"))
        yield basis_prep
Beispiel #4
0
def _do_tomography(target_program, nsamples, cxn, qubits, max_num_qubits,
                   tomography_class, program_generator, settings):
    """

    :param Program target_program: The program to run to generate the state or process.
    :param int nsamples: The number of samples to take per measurement.
    :param QPUConnection|QVMConnection cxn: The connection to Forest.
    :param lists qubits: The list of qubits to perform tomography on.
    :param int max_num_qubits: The maximum allowed number of qubits.
    :param type tomography_class: The type of tomography to perform.
    :param function program_generator: The function that yields the tomography experiments.
    :param TomographySettings settings: The settings for running the optimizer.
    :return: The tomography result, the assignment probabilities, and the histograms of counts
     measured.
    :rtype: tuple
    """
    if qubits is None:
        qubits = sorted(target_program.get_qubits())

    num_qubits = len(qubits)
    dimension = 2**num_qubits

    if num_qubits > max_num_qubits:
        raise ValueError("Too many qubits!")

    assignment_probs = sample_assignment_probs(qubits, nsamples, cxn)
    tomo_seq = list(program_generator(target_program, qubits))
    histograms = np.zeros((len(tomo_seq), dimension))
    for i, tomo_prog in izip(ut.TRANGE(len(tomo_seq)), tomo_seq):
        tomo_prog = Program(Pragma("PRESERVE_BLOCK")) + tomo_prog
        results = cxn.run_and_measure(tomo_prog, qubits, nsamples)
        idxs = list(map(bitlist_to_int, results))
        histograms[i] = ut.make_histogram(idxs, dimension)

    povm = o_ut.make_diagonal_povm(
        grove.tomography.operator_utils.POVM_PI_BASIS**num_qubits,
        assignment_probs)
    channel_ops = list(default_channel_ops(num_qubits))

    # Currently the analysis pathways are slightly different, so we branch on which type of
    # tomography is being done.
    if tomography_class.__tomography_type__ == "PROCESS":
        histograms = histograms.reshape(
            (len(channel_ops), len(channel_ops), dimension))
        tomo_result = tomography_class.estimate_from_ssr(
            histograms, povm, channel_ops, channel_ops, settings)
    else:
        tomo_result = tomography_class.estimate_from_ssr(
            histograms, povm, channel_ops, settings)
    return tomo_result, assignment_probs, histograms
Beispiel #5
0
def test_transform_with_post_transformation_hooks(
    bell_circuit_with_qids: Tuple[cirq.Circuit, List[cirq.Qid]],
) -> None:
    """test that a user can transform a `cirq.Circuit` to a `pyquil.Program`
    functionally with explicit physical qubit address mapping.
    """
    bell_circuit, qubits = bell_circuit_with_qids

    def reset_hook(program, measurement_id_map):
        program._instructions.insert(0, Reset())
        return program, measurement_id_map

    reset_hook_spec = create_autospec(
        reset_hook,
        side_effect=reset_hook,
    )

    pragma = Pragma('INTIAL_REWIRING', freeform_string='GREEDY')

    def rewire_hook(program, measurement_id_map):
        program._instructions.insert(0, pragma)
        return program, measurement_id_map

    rewire_hook_spec = create_autospec(
        rewire_hook,
        side_effect=rewire_hook,
    )
    transformer = transformers.build(
        qubits=tuple(qubits),
        post_transformation_hooks=[reset_hook_spec, rewire_hook_spec],
    )
    program, _ = transformer(circuit=bell_circuit)

    assert 1 == reset_hook_spec.call_count
    assert Reset() in program.instructions, "hook should add reset"

    assert 1 == rewire_hook_spec.call_count
    assert pragma in program.instructions, "hook should add pragma"

    assert H(0) in program.instructions, "bell circuit should include Hadamard"
    assert CNOT(0, 1) in program.instructions, "bell circuit should include CNOT"
    assert (
        DECLARE("m0", memory_size=2) in program.instructions
    ), "executable should declare a read out bit"
    assert (
        MEASURE(0, ("m0", 0)) in program.instructions
    ), "executable should measure the first qubit to the first read out bit"
    assert (
        MEASURE(1, ("m0", 1)) in program.instructions
    ), "executable should measure the second qubit to the second read out bit"
Beispiel #6
0
def state_tomography_programs(state_prep,
                              qubits=None,
                              rotation_generator=tomography.default_rotations):
    """
    Yield tomographic sequences that prepare a state with Quil program `state_prep` and then append
    tomographic rotations on the specified `qubits`. If `qubits is None`, it assumes all qubits in
    the program should be tomographically rotated.

    :param Program state_prep: The program to prepare the state to be tomographed.
    :param list|NoneType qubits: A list of Qubits or Numbers, to perform the tomography on. If
    `None`, performs it on all in state_prep.
    :param generator rotation_generator: A generator that yields tomography rotations to perform.
    :return: Program for state tomography.
    :rtype: Program
    """
    if qubits is None:
        qubits = state_prep.get_qubits()
    for tomography_program in rotation_generator(*qubits):
        state_tomography_program = Program(Pragma("PRESERVE_BLOCK"))
        state_tomography_program.inst(state_prep)
        state_tomography_program.inst(tomography_program)
        state_tomography_program.inst(Pragma("END_PRESERVE_BLOCK"))
        yield state_tomography_program
def generate_single_t2_echo_experiment(qubits: Union[int, List[int]],
                                       time: float,
                                       detuning: float,
                                       n_shots: int = 1000) -> Program:
    """
    Return a T2 echo program in native Quil for a single time point.

    :param qubits: Which qubits to measure.
    :param time: The decay time before measurement.
    :param detuning: The additional detuning frequency about the z axis.
    :param n_shots: The number of shots to average over for the data point.
    :return: A T2 Program.
    """
    program = Program()

    try:
        len(qubits)
    except TypeError:
        qubits = [qubits]

    ro = program.declare('ro', 'BIT', len(qubits))
    for q in qubits:
        # prepare plus state |+>
        program += RX(np.pi / 2, q)
        # wait half of the delay
        program += Pragma('DELAY', [q], str(time / 2))
        # apply an X gate compiled out of RX(90)
        program += RX(np.pi / 2, q)
        program += RX(np.pi / 2, q)
        # wait the other half of the delay
        program += Pragma('DELAY', [q], str(time / 2))
        program += RZ(2 * np.pi * time * detuning, q)
        program += RX(np.pi / 2, q)
    for i in range(len(qubits)):
        program += MEASURE(qubits[i], ro[i])
    program.wrap_in_numshots_loop(n_shots)
    return program
Beispiel #8
0
def _get_flipped_protoquil_program(program: Program) -> Program:
    """For symmetrization, generate a program where X gates are added before measurement.

    Forest is picky about where the measure instructions happen. It has to be at the end!
    """
    program = program.copy()
    to_measure = []
    while len(program) > 0:
        inst = program.instructions[-1]
        if isinstance(inst, Measurement):
            program.pop()
            to_measure.append((inst.qubit, inst.classical_reg))
        else:
            break

    program += Pragma('PRESERVE_BLOCK')
    for qu, addr in to_measure[::-1]:
        program += RX(pi, qu)
    program += Pragma('END_PRESERVE_BLOCK')

    for qu, addr in to_measure[::-1]:
        program += Measurement(qubit=qu, classical_reg=addr)

    return program
Beispiel #9
0
def _create_kraus_pragmas(name, qubit_indices, kraus_ops):
    """
    Generate the pragmas to define a Kraus map for a specific gate on some qubits.

    :param str name: The name of the gate.
    :param list|tuple qubit_indices: The qubits
    :param list|tuple kraus_ops: The Kraus operators as matrices.
    :return: A QUIL string with PRAGMA ADD-KRAUS ... statements.
    :rtype: str
    """

    pragmas = [Pragma("ADD-KRAUS",
                      [name] + list(qubit_indices),
                      "({})".format(" ".join(map(format_parameter, np.ravel(k)))))
               for k in kraus_ops]
    return pragmas
Beispiel #10
0
def generate_t2_echo_experiments(
        qubits: Sequence[int],
        times: Sequence[float],
        detuning: float = 1e6) -> List[ObservablesExperiment]:
    """
    Return ObservablesExperiments containing programs which constitute a T2 echo experiment to
    measure the T2 echo coherence decay time.

    For each delay time in times a single program will be generated in which all qubits are
    initialized to the minusY state and later simultaneously measured along the Y axis. Unlike in
    the t2_star experiment above there is a 'echo' applied in the middle of the delay in which
    the qubit is rotated by pi radians around the Y axis.

    Similarly to t2_star, if the qubit frequency is perfectly calibrated then the Y expectation
    will oscillate at the given detuning frequency as the qubit is rotated about the Z axis (with
    respect to the lab frame, which by hypothesis matches the natural qubit frame). Unlike in a
    t2_star experiment, even if the qubit frequency is off such that there is some spurious
    rotation about the Z axis during the DELAY, the effect of an ideal echo is to cancel the
    effect of this rotation so that the qubit returns to the initial state minusY before the
    detuning rotation is applied.

    :param qubits: list of qubits to measure.
    :param times: the times at which to measure, given in seconds. Each time is rounded to the
        nearest .1 microseconds.
    :param detuning: The additional detuning frequency about the z axis.
    :return: ObservablesExperiments which can be run to acquire an estimate of T2 for each qubit.
    """
    expts = []
    for t in times:
        half_time = round(t / 2, 7)  # enforce 100ns boundaries
        t = round(t, 7)
        program = Program()
        settings = []
        for q in qubits:
            half_delay = Pragma('DELAY', [q], str(half_time))
            # echo
            program += [half_delay, RY(pi, q), half_delay]
            # apply detuning
            program += RZ(2 * pi * t * detuning, q)
            settings.append(ExperimentSetting(minusY(q), PauliTerm('Y', q)))

        expts.append(ObservablesExperiment(settings, program))

    return expts
Beispiel #11
0
def _create_kraus_pragmas(name: str, qubit_indices: Sequence[int],
                          kraus_ops: Sequence[np.ndarray]) -> List[Pragma]:
    """
    Generate the pragmas to define a Kraus map for a specific gate on some qubits.

    :param name: The name of the gate.
    :param qubit_indices: The qubits
    :param kraus_ops: The Kraus operators as matrices.
    :return: A QUIL string with PRAGMA ADD-KRAUS ... statements.
    """

    pragmas = [
        Pragma(
            "ADD-KRAUS",
            (name, ) + tuple(qubit_indices),
            "({})".format(" ".join(map(format_parameter, np.ravel(k)))),
        ) for k in kraus_ops
    ]
    return pragmas
Beispiel #12
0
def test_pragma():
    _test('PRAGMA gate_time H "10 ns"', Pragma('gate_time', ['H'], '10 ns'))
    _test('PRAGMA qubit 0', Pragma('qubit', [0]))
    _test('PRAGMA NO-NOISE', Pragma('NO-NOISE'))
Beispiel #13
0
 def pragma(self, name, *pragma_names_and_string):
     args = list(map(str, pragma_names_and_string))
     p = Pragma(str(name), args=args)
     return p
Beispiel #14
0
def test_pragma():
    parse_equals('PRAGMA gate_time H "10 ns"',
                 Pragma('gate_time', ['H'], '10 ns'))
    parse_equals('PRAGMA qubit 0', Pragma('qubit', [0]))
    parse_equals('PRAGMA NO-NOISE', Pragma('NO-NOISE'))
Beispiel #15
0
from unittest.mock import patch

from pyquil.api import QVMConnection, QPUConnection, CompilerConnection
from pyquil.api._base_connection import validate_noise_probabilities, validate_run_items
from pyquil.api.qpu import append_measures_to_program
from pyquil.quil import Program
from pyquil.paulis import PauliTerm
from pyquil.gates import CNOT, H, MEASURE, PHASE, Z, RZ, RX, CZ
from pyquil.device import ISA
from pyquil.quilbase import Pragma

EMPTY_PROGRAM = Program()
BELL_STATE = Program(H(0), CNOT(0, 1))
BELL_STATE_MEASURE = Program(H(0), CNOT(0, 1), MEASURE(0, 0), MEASURE(1, 1))
COMPILED_BELL_STATE = Program([
    Pragma("EXPECTED_REWIRING", ('"#(0 1 2 3)"',)),
    RZ(pi / 2, 0),
    RX(pi / 2, 0),
    RZ(-pi / 2, 1),
    RX(pi / 2, 1),
    CZ(1, 0),
    RZ(-pi / 2, 0),
    RX(-pi / 2, 1),
    RZ(pi / 2, 1),
    Pragma("CURRENT_REWIRING", ('"#(0 1 2 3)"',)),
    Pragma("EXPECTED_REWIRING", ('"#(0 1 2 3)"',)),
    Pragma("CURRENT_REWIRING", ('"#(0 1 2 3)"',)),
])
DUMMY_ISA_DICT = {"1Q": {"0": {}, "1": {}}, "2Q": {"0-1": {}}}
DUMMY_ISA = ISA.from_dict(DUMMY_ISA_DICT)
Beispiel #16
0
def address_qubits(program, qubit_mapping=None):
    """
    Takes a program which contains placeholders and assigns them all defined values.

    Either all qubits must be defined or all undefined. If qubits are
    undefined, you may provide a qubit mapping to specify how placeholders get mapped
    to actual qubits. If a mapping is not provided, integers 0 through N are used.

    This function will also instantiate any label placeholders.

    :param program: The program.
    :param qubit_mapping: A dictionary-like object that maps from :py:class:`QubitPlaceholder`
        to :py:class:`Qubit` or ``int`` (but not both).
    :return: A new Program with all qubit and label placeholders assigned to real qubits and labels.
    """
    fake_qubits, real_qubits, qubits = _what_type_of_qubit_does_it_use(program)
    if real_qubits:
        if qubit_mapping is not None:
            warnings.warn(
                "A qubit mapping was provided but the program does not "
                "contain any placeholders to map!")
        return program

    if qubit_mapping is None:
        qubit_mapping = {qp: Qubit(i) for i, qp in enumerate(qubits)}
    else:
        if all(isinstance(v, Qubit) for v in qubit_mapping.values()):
            pass  # we good
        elif all(isinstance(v, int) for v in qubit_mapping.values()):
            qubit_mapping = {k: Qubit(v) for k, v in qubit_mapping.items()}
        else:
            raise ValueError(
                "Qubit mapping must map to type Qubit or int (but not both)")

    result = []
    for instr in program:
        # Remap qubits on Gate, Measurement, and ResetQubit instructions
        if isinstance(instr, Gate):
            remapped_qubits = [qubit_mapping[q] for q in instr.qubits]
            gate = Gate(instr.name, instr.params, remapped_qubits)
            gate.modifiers = instr.modifiers
            result.append(gate)
        elif isinstance(instr, Measurement):
            result.append(
                Measurement(qubit_mapping[instr.qubit], instr.classical_reg))
        elif isinstance(instr, ResetQubit):
            result.append(ResetQubit(qubit_mapping[instr.qubit]))
        elif isinstance(instr, Pragma):
            new_args = []
            for arg in instr.args:
                # Pragmas can have arguments that represent things besides qubits, so here we
                # make sure to just look up the QubitPlaceholders.
                if isinstance(arg, QubitPlaceholder):
                    new_args.append(qubit_mapping[arg])
                else:
                    new_args.append(arg)
            result.append(
                Pragma(instr.command, new_args, instr.freeform_string))
        # Otherwise simply add it to the result
        else:
            result.append(instr)

    new_program = program.copy()
    new_program._instructions = result

    return new_program
Beispiel #17
0
def test_pragma():
    parse_equals('PRAGMA gate_time H "10 ns"',
                 Pragma("gate_time", ["H"], "10 ns"))
    parse_equals("PRAGMA qubit 0", Pragma("qubit", [0]))
    parse_equals("PRAGMA NO-NOISE", Pragma("NO-NOISE"))
Beispiel #18
0
def fill_placeholders(obj, placeholder_values: Dict[Union[FormalArgument,
                                                          Parameter], Any]):
    """Update Parameter and FormalArgument references in objects with
    their corresponding definitions.

    It is an error if the object has a Parameter or FormalArgument reference without
    a corresponding definition in placeholder_values.

    :param obj: A Quil AST object.
    :param placeholder_values: A dictionary mapping placeholders to their values.
    :returns: The updated AST object.
    """
    try:
        if obj is None or isinstance(
                obj, (int, float, complex, Qubit, MemoryReference)):
            return obj
        elif isinstance(obj, Expression):
            # defer to the usual PyQuil substitution
            return substitute(
                obj, {
                    k: v
                    for k, v in placeholder_values.items()
                    if isinstance(k, Parameter)
                })
        elif isinstance(obj, FormalArgument):
            return placeholder_values[obj]
        elif isinstance(obj, Frame):
            return Frame(fill_placeholders(obj.qubits, placeholder_values),
                         obj.name)
        elif isinstance(obj, WaveformReference):
            return obj
        elif isinstance(obj, TemplateWaveform):
            return obj.__class__(
                **fill_placeholders(obj.__dict__, placeholder_values))
        elif isinstance(obj, list):
            return [fill_placeholders(elt, placeholder_values) for elt in obj]
        elif isinstance(obj, dict):
            return {
                k: fill_placeholders(v, placeholder_values)
                for (k, v) in obj.items()
            }
        elif isinstance(obj, tuple):
            return tuple(
                [fill_placeholders(item, placeholder_values) for item in obj])
        elif isinstance(obj, Pragma) and obj.command == "LOAD-MEMORY":
            (source, ) = obj.args
            arg = FormalArgument(obj.freeform_string)
            if arg in placeholder_values:
                return Pragma("LOAD-MEMORY", [source],
                              str(placeholder_values[arg]))
            else:
                return obj
        else:
            specs = {
                Gate: ["params", "qubits"],
                Measurement: ["qubit", "classical_reg"],
                ResetQubit: ["qubit"],
                Pulse: ["frame", "waveform"],
                SetFrequency: ["frame", "freq"],
                ShiftFrequency: ["frame", "freq"],
                SetPhase: ["frame", "phase"],
                ShiftPhase: ["frame", "phase"],
                SwapPhase: ["frameA", "frameB"],
                SetScale: ["frame", "scale"],
                Capture: ["frame", "kernel", "memory_region"],
                RawCapture: ["frame", "duration", "memory_region"],
                DelayQubits: ["qubits", "duration"],
                DelayFrames: ["frames", "duration"],
                Fence: ["qubits"],
                FenceAll: [],
                Declare: [],
                Pragma: [],
            }
            if type(obj) in specs:
                attrs = specs[type(obj)]
                updated = copy(obj)
                for attr in attrs:
                    setattr(
                        updated, attr,
                        fill_placeholders(getattr(updated, attr),
                                          placeholder_values))
                return updated
            else:
                raise CalibrationError(
                    f"Unable to fill placeholders in object {obj}.")
    except Exception as e:
        raise e