Beispiel #1
0
    def build_circuit(self, qubit_ops):
        program = Program()
        self.qubits = [program.alloc() for _ in range(len(qubit_ops))]

        program_readout_reg = program.declare("ro",
                                              memory_type="BIT",
                                              memory_size=len(self.qubits))

        for qb, gatestr in zip(self.qubits, qubit_ops):
            if gatestr == "I":
                pass
            elif gatestr == "X":
                program.gate("X", qubits=[qb], params=[])
            elif gatestr == "H":
                program.gate("H", qubits=[qb], params=[])
            elif gatestr == "K":  # our one char CX label
                program.gate(
                    "CNOT",
                    qubits=[self.qubits[self.qubits.index(qb) - 1], qb],
                    params=[])

        program.measure_all(*zip(self.qubits, program_readout_reg))
        program = address_qubits(program)
        program.wrap_in_numshots_loop(shots=10)

        self.program = program
Beispiel #2
0
def generate_single_t1_experiment(qubits: Union[int, List[int]],
                                  time: float,
                                  n_shots: int = 1000) -> Program:
    """
    Return a t1 program in native Quil for a single time point.

    :param qubits: Which qubits to measure.
    :param time: The decay time before measurement.
    :param n_shots: The number of shots to average over for the data point.
    :return: A T1 Program.
    """
    program = Program()

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

    ro = program.declare('ro', 'BIT', len(qubits))
    for q in qubits:
        program += RX(np.pi, q)
        program += Pragma('DELAY', [q], str(time))
    for i in range(len(qubits)):
        program += MEASURE(qubits[i], ro[i])
    program.wrap_in_numshots_loop(n_shots)
    return program
Beispiel #3
0
def generate_single_rabi_experiment(qubits: Union[int, List[int]],
                                    theta: float,
                                    n_shots: int = 1000) -> Program:
    """
    Return a Rabi program in native Quil rotated through the given angle.

    Rabi oscillations are observed by applying successively larger rotations to the same initial
    state.

    :param qubits: Which qubits to measure.
    :param theta: The angle of the Rabi RX rotation.
    :param n_shots: The number of shots to average over for the data point.
    :return: A Program that rotates through a given angle about the X axis.
    """
    program = Program()

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

    ro = program.declare('ro', 'BIT', len(qubits))
    for q in qubits:
        program += RX(theta, q)
    for i in range(len(qubits)):
        program += MEASURE(qubits[i], ro[i])
    program.wrap_in_numshots_loop(n_shots)
    return program
Beispiel #4
0
    def run_and_benchmark_program(self, prog: Program, trials: int,
                                  is_correct: Callable[[List[int]], bool],
                                  separate: bool) -> int:
        n_qubits = len(prog.get_qubits())
        qvm = pyquil.get_qc("{}q-qvm".format(n_qubits))

        # The paramaters are 10x less noisy than the defaults.
        qvm.qam.noise_model = self.noise_model(qvm.device)

        if separate:
            elapsed = []
            results = []
            for i in range(trials):
                start_time = time.time()
                trial_results = qvm.run(prog)
                end_time = time.time()

                elapsed.append(end_time - start_time)
                results.extend(trial_results)
        else:
            prog.wrap_in_numshots_loop(trials)
            start_time = time.time()
            results = qvm.run(prog)
            end_time = time.time()
            elapsed = end_time - start_time

        correct = sum(is_correct(result) for result in results)
        return correct, elapsed
def generate_cz_phase_ramsey_program(qb: int,
                                     other_qb: int,
                                     n_shots: int = 1000) -> Program:
    """
    Generate a single CZ phase Ramsey experiment at a given phase.

    :param qb: The qubit to move around the Bloch sphere and measure the incurred RZ on.
    :param other_qb: The other qubit that constitutes a two-qubit pair along with `qb`.
    :param n_shots: The number of shots to average over for each data point.
    :param phase: The phase kick to supply after playing the CZ pulse on the equator.
    :param num_shots: The number of shots to average over for the data point.
    :return: A parametric Program for performing a CZ Ramsey experiment.
    """
    program = Program()
    # NOTE: only need readout register for `qb` not `other_qb` since `other_qb` is only
    #       needed to identify which CZ gate we're using
    ro = program.declare('ro', 'BIT', 1)
    theta = program.declare('theta', 'REAL')

    # go to the equator
    program += Program(RX(np.pi / 2, qb))
    # apply the CZ gate - note that CZ is symmetric, so the order of qubits doesn't matter
    program += Program(CZ(qb, other_qb))
    # go to |1> after a phase kick
    program += Program(RZ(theta, qb), RX(np.pi / 2, qb))

    program += MEASURE(qb, ro[0])

    program.wrap_in_numshots_loop(n_shots)
    return program
Beispiel #6
0
    def run_and_measure(self, program: Program, trials: int):
        """
        Run the provided state preparation program and measure all qubits.

        .. note::

            In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates
            noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``.
            For faster noise-free simulation, consider
            :py:class:`WavefunctionSimulator.run_and_measure`.

        :param program: The state preparation program to run and then measure.
        :param trials: The number of times to run the program.
        :return: A numpy array of shape (trials, n_device_qubits) that contains 0s and 1s
        """
        program = program.copy()
        for instr in program.instructions:
            if not isinstance(instr, Gate) and not isinstance(instr, Reset):
                raise ValueError(
                    "run_and_measure programs must consist only of quantum gates."
                )
        ro = program.declare('ro', 'BIT',
                             max(self.device.qubit_topology().nodes) + 1)
        for q in sorted(self.device.qubit_topology().nodes):
            program.inst(MEASURE(q, ro[q]))
        program.wrap_in_numshots_loop(trials)
        executable = self.compile(program)
        return self.run(executable=executable)
Beispiel #7
0
def _operator_object_hook(obj: Mapping[str, Any]) -> Union[Mapping[str, Any], Experiment]:
    if "type" in obj and obj["type"] in ["Experiment", "TomographyExperiment"]:
        # I bet this doesn't work for grouped experiment settings
        settings = [[ExperimentSetting.from_str(s) for s in stt] for stt in obj["settings"]]
        p = Program(obj["program"])
        p.wrap_in_numshots_loop(obj["shots"])
        ex = Experiment(settings=settings, program=p, symmetrization=obj["symmetrization"])
        ex.reset = obj["reset"]
        return ex
    return obj
def append_measure_register(program: Program,
                            qubits: List = None,
                            trials: int = 10,
                            ham: PauliSum = None) -> Program:
    """Creates readout register, MEASURE instructions for register and wraps
    in trials trials.

    Parameters
    ----------
    param qubits : list
        List of Qubits to measure. If None, program.get_qubits() is used
    param trials : int
        The number of trials to run.
    param ham : PauliSum
        Hamiltonian to whose basis we need to switch. All terms in it must
        trivially commute!

    Returns
    -------
    Program :
        program with the gate change and measure instructions appended
    """
    base_change_gates = {'X': lambda qubit: H(qubit),
                         'Y': lambda qubit: RX(np.pi / 2, qubit),
                         'Z': lambda qubit: I(qubit)}

    if qubits is None:
        qubits = program.get_qubits()


    def _get_correct_gate(qubit: Union[int, QubitPlaceholder]) -> Program():
        """Correct base change gate on the qubit `qubit` given `ham`"""
        # this is an extra function, because `return` allows us to
        # easily break out of loops
        for term in ham:
            if term[qubit] != 'I':
                return base_change_gates[term[qubit]](qubit)

        raise ValueError(f"PauliSum {ham} doesn't act on qubit {qubit}")

    # append to correct base change gates if ham is specified. Otherwise
    # assume diagonal basis
    if ham is not None:
        for qubit in ham.get_qubits():
            program += Program(_get_correct_gate(qubit))

    # create a read out register
    ro = program.declare('ro', memory_type='BIT', memory_size=len(qubits))

    # add measure instructions to the specified qubits
    for i, qubit in enumerate(qubits):
        program += MEASURE(qubit, ro[i])
    program.wrap_in_numshots_loop(trials)
    return program
    def run_and_measure(self, program: Program,
                        trials: int) -> Dict[int, np.ndarray]:
        """
        Run the provided state preparation program and measure all qubits.

        The returned data is a dictionary keyed by qubit index because qubits for a given
        QuantumComputer may be non-contiguous and non-zero-indexed. To turn this dictionary
        into a 2d numpy array of bitstrings, consider::

            bitstrings = qc.run_and_measure(...)
            bitstring_array = np.vstack([bitstrings[q] for q in qc.qubits()]).T
            bitstring_array.shape  # (trials, len(qc.qubits()))

        .. note::

            If the target :py:class:`QuantumComputer` is a noiseless :py:class:`QVM` then
            only the qubits explicitly used in the program will be measured. Otherwise all
            qubits will be measured. In some circumstances this can exhaust the memory
            available to the simulator, and this may be manifested by the QVM failing to
            respond or timeout.

        .. note::

            In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates
            noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``.
            For faster noise-free simulation, consider
            :py:class:`WavefunctionSimulator.run_and_measure`.

        :param program: The state preparation program to run and then measure.
        :param trials: The number of times to run the program.
        :return: A dictionary keyed by qubit index where the corresponding value is a 1D array of
            measured bits.
        """
        program = program.copy()
        validate_supported_quil(program)
        ro = program.declare("ro", "BIT", len(self.qubits()))
        measure_used = isinstance(self.qam,
                                  QVM) and self.qam.noise_model is None
        qubits_to_measure = set(
            map(qubit_index, program.get_qubits()) if measure_used else self.
            qubits())
        for i, q in enumerate(qubits_to_measure):
            program.inst(MEASURE(q, ro[i]))
        program.wrap_in_numshots_loop(trials)
        executable = self.compile(program)
        bitstring_array = self.run(executable=executable)
        bitstring_dict = {}
        for i, q in enumerate(qubits_to_measure):
            bitstring_dict[q] = bitstring_array[:, i]
        for q in set(self.qubits()) - set(qubits_to_measure):
            bitstring_dict[q] = np.zeros(trials)
        return bitstring_dict
Beispiel #10
0
def compile_parametric_program(qc: QuantumComputer,
                               parametric_prog: Program,
                               num_shots: int = 1000) -> None:
    """
    Compile the parametric program, and transfer the binary to the quantum device.

    :param qc: The QuantumComputer to run the experiment on.
    :param parametric_prog: The parametric program to compile and transfer to the quantum device.
    :param num_shots: The number of shots to average over for each data point.
    :return: The binary from the compiled parametric program.
    """
    parametric_prog.wrap_in_numshots_loop(shots=num_shots)
    binary = qc.compiler.native_quil_to_executable(parametric_prog)
    return binary
Beispiel #11
0
    def run_symmetrized_readout(self, program: Program, trials: int) -> np.ndarray:
        """
        Run a quil program in such a way that the readout error is made collectively symmetric

        This means the probability of a bitstring ``b`` being mistaken for a bitstring ``c`` is
        the same as the probability of ``not(b)`` being mistaken for ``not(c)``

        A more general symmetrization would guarantee that the probability of ``b`` being
        mistaken for ``c`` depends only on which bit of ``c`` are different from ``b``. This
        would require choosing random subsets of bits to flip.

        In a noisy device, the probability of accurately reading the 0 state might be higher
        than that of the 1 state. This makes correcting for readout more difficult. This
        function runs the program normally ``(trials//2)`` times. The other half of the time,
        it will insert an ``X`` gate prior to any ``MEASURE`` instruction and then flip the
        measured classical bit back.

        See :py:func:`run` for this function's parameter descriptions.
        """
        flipped_program = _get_flipped_protoquil_program(program)
        if trials % 2 != 0:
            raise ValueError("Using symmetrized measurement functionality requires that you "
                             "take an even number of trials.")
        half_trials = trials // 2
        flipped_program = flipped_program.wrap_in_numshots_loop(shots=half_trials)
        flipped_executable = self.compile(flipped_program)

        executable = self.compile(program.wrap_in_numshots_loop(half_trials))
        samples = self.run(executable)
        flipped_samples = self.run(flipped_executable)
        double_flipped_samples = np.logical_not(flipped_samples).astype(int)
        results = np.concatenate((samples, double_flipped_samples), axis=0)
        np.random.shuffle(results)
        return results
Beispiel #12
0
    def generate_calibration_experiment(self) -> "Experiment":
        """
        Generate another ``Experiment`` object that can be used to calibrate the various multi-qubit
        observables involved in this ``Experiment``. This is achieved by preparing the plus-one
        (minus-one) eigenstate of each ``out_operator``, and measuring the resulting expectation
        value of the same ``out_operator``. Ideally, this would always give +1 (-1), but when
        symmetric readout error is present the effect is to scale the resultant expectations by some
        constant factor. Determining this scale factor is what we call *readout calibration*, and
        then the readout error in subsequent measurements can then be mitigated by simply dividing
        by the scale factor.

        :return: A new ``Experiment`` that can calibrate the readout error of all the
            observables involved in this experiment.
        """
        if self.calibration != CalibrationMethod.PLUS_EIGENSTATE:
            raise ValueError(
                'We currently only support the "plus eigenstate" calibration method.'
            )

        calibration_settings = []
        for settings in self:
            assert len(settings) == 1
            calibration_settings.append(
                ExperimentSetting(
                    in_state=settings[0].out_operator,
                    out_operator=settings[0].out_operator,
                    additional_expectations=settings[0].
                    additional_expectations,
                ))

        calibration_program = Program()
        if self.reset:
            calibration_program += RESET()
        calibration_program.wrap_in_numshots_loop(self.shots)

        if self.symmetrization != SymmetrizationLevel.EXHAUSTIVE:
            raise ValueError(
                "We currently only support calibration for exhaustive symmetrization"
            )

        return Experiment(
            settings=calibration_settings,
            program=calibration_program,
            symmetrization=SymmetrizationLevel.EXHAUSTIVE,
            calibration=CalibrationMethod.NONE,
        )
Beispiel #13
0
    def run_and_measure(self, program: Program,
                        trials: int) -> Dict[int, np.ndarray]:
        """
        Run the provided state preparation program and measure all qubits.

        This will measure all the qubits on this QuantumComputer, not just qubits
        that are used in the program.

        The returned data is a dictionary keyed by qubit index because qubits for a given
        QuantumComputer may be non-contiguous and non-zero-indexed. To turn this dictionary
        into a 2d numpy array of bitstrings, consider::

            bitstrings = qc.run_and_measure(...)
            bitstring_array = np.vstack(bitstrings[q] for q in sorted(qc.qubits())).T
            bitstring_array.shape  # (trials, len(qc.qubits()))

        .. note::

            In contrast to :py:class:`QVMConnection.run_and_measure`, this method simulates
            noise correctly for noisy QVMs. However, this method is slower for ``trials > 1``.
            For faster noise-free simulation, consider
            :py:class:`WavefunctionSimulator.run_and_measure`.

        :param program: The state preparation program to run and then measure.
        :param trials: The number of times to run the program.
        :return: A dictionary keyed by qubit index where the corresponding value is a 1D array of
            measured bits.
        """
        program = program.copy()
        program = _validate_run_and_measure_program(program)
        ro = program.declare('ro', 'BIT', len(self.qubits()))
        for i, q in enumerate(self.qubits()):
            program.inst(MEASURE(q, ro[i]))
        program.wrap_in_numshots_loop(trials)
        executable = self.compile(program)
        bitstring_array = self.run(executable=executable)
        bitstring_dict = {}
        for i, q in enumerate(self.qubits()):
            bitstring_dict[q] = bitstring_array[:, i]
        return bitstring_dict
Beispiel #14
0
def _run_rpe_program(qc: QuantumComputer, program: Program,
                     measure_qubits: Sequence[Sequence[int]],
                     num_shots: int) -> np.ndarray:
    """
    Simple helper to run a program with appropriate number of shots and return result.

    Note that the program is first compiled with basic_compile.

    :param qc: quantum computer to run program on
    :param program: program to run
    :param measure_qubits: all of the qubits to be measured after the program is run
    :param num_shots: number of shots of results to collect for the program
    :return: the results for all of the measure_qubits after running the program
    """
    prog = Program() + program  # make a copy of program
    meas_qubits = [qubit for qubits in measure_qubits for qubit in qubits]
    ro_bit = prog.declare("ro", "BIT", len(meas_qubits))
    for idx, q in enumerate(meas_qubits):
        prog.measure(q, ro_bit[idx])
    prog.wrap_in_numshots_loop(num_shots)
    executable = qc.compiler.native_quil_to_executable(basic_compile(prog))
    return qc.run(executable)
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 #16
0
    def _compute_loss(self, parameters, history_list, dataset_type, indices=None):
        """
        Computes mean loss for the given data subset (training or test).
        
        :param parameters: (list) Vector of training circuit parameters
        :param history_list: (list) List to store losses
        :param dataset_type: (bool) Indicator for training (0) or testing (1) set
        :param indices: (list) List of indices pointing to state preparation circuits
                            (for training or testing)
        :returns: Average loss value for a given data set (training or test)
        :rtype: float
        """
        losses = []

        # Compute cost function value for each data point
        for i, index in enumerate(indices):

            # Apply compression and recovery maps
            qae_circuit = Program()
            qae_circuit.inst(self.construct_compression_circuit(parameters, index))
            if not self.trash_training:
                qae_circuit.inst(self.construct_recovery_circuit(parameters, index))

            # Apply measurement operations
            ro = qae_circuit.declare('ro', memory_type='BIT', memory_size=self.memory_size)
            for j in range(self.memory_size):
                qae_circuit.inst(MEASURE(self._physical_labels[j], ro[j]))
            
            # Prepare circuit execution
            qae_circuit = qae_circuit.wrap_in_numshots_loop(self.n_shots)
            if self.compile_program:
                qae_circuit = self.forest_cxn.compile(qae_circuit)

            # Compute loss for the data point and store
            single_loss = self._execute_circuit(parameters, qae_circuit, self.memory_size)
            losses.append(single_loss)

        mean_loss = -1. * numpy.mean(losses)
        history_list.append(mean_loss)

        if self.verbose:
            if (len(history_list) - 1) % self.print_interval == 0:
                print("Iter {0:4d} Mean Loss: {1:.7f}".format(self.n_iter, mean_loss))
        self.n_iter += 1
        return mean_loss
Beispiel #17
0
    def compile_tomo_expts(self, pauli_list=None):
        """
        This method compiles the tomography experiment circuits
        and prepares them for simulation.
        Every time the circuits are adjusted,
        re-compiling the tomography experiments
        is required to affect the outcome.
        """
        # use Forest's sorting algo from the Tomography suite
        # to group Pauli measurements together
        settings = []
        if pauli_list is None:
            pauli_list = self.pauli_list

        for term in pauli_list:
            # skip an identity operator,
            if len(term.operations_as_set()) > 0:
                # initial state and term pair.
                settings.append(ExperimentSetting(
                        TensorProductState(),
                        term))
        # group_experiments cannot be directly run
        # because there may be experiment with multiple settings.
        # so here we just use group_experiments to sort out the settings,
        # then reset the experiments with additional measurements.
        experiments = Experiment(settings, Program())
        suite = group_experiments(experiments)
        # we just need the grouped settings.
        grouped_pauil_terms = []
        for setting in suite:
            group = []
            for i, term in enumerate(setting):
                pauil_term = term.out_operator.copy()
                # Coefficients to be multiplied.
                pauil_term.coefficient = complex(1.)
                if i == 0:
                    group.append(pauil_term)
                elif len(pauil_term) > len(group[0]):
                    group.insert(0, pauil_term)
                else:
                    group.append(pauil_term)
            # make sure the longest pauil_term contains all the small
            # pauil_terms. Otherwise, we prepare a bigger one.
            bigger_pauli = group[0]
            if len(group) > 1:
                for term in group[1:]:
                    for iq, op in term.operations_as_set():
                        if op != group[0][iq]:
                            assert(group[0][iq] == "I"), \
                                    (f"{term} and {group[0]}"
                                    " not compatible!")
                            if bigger_pauli is group[0]:
                                bigger_pauli == group[0].copy()
                            bigger_pauli *= PauliTerm(op, iq)
                if bigger_pauli is not group[0]:
                    print(f"new pauli_term generated: {bigger_pauli}")
                    group.insert(0, bigger_pauli)
            grouped_pauil_terms.append(group)
        # group settings with additional_expectations.
        grouped_settings = []
        for pauil_terms in grouped_pauil_terms:
            additional = None
            if len(pauil_terms) > 1:
                additional = []
                for term in pauil_terms[1:]:
                    additional.append(term.get_qubits())
            grouped_settings.append(
                    ExperimentSetting(TensorProductState(),
                            pauil_terms[0],
                            additional_expectations=additional))

        # get the uccsd program
        prog = Program()
        prog += RESET()
        prog += self.ref_state + self.ansatz
        prog.wrap_in_numshots_loop(shots=self.shotN)
        self.experiment_list = Experiment(grouped_settings, prog)
        print('Number of tomography experiments: ',
                len(self.experiment_list))
        # calibration expreimental results.
        self.calibrations = self.qc.calibrate(self.experiment_list)
Beispiel #18
0
    def generate_experiment_program(self) -> Program:
        """
        Generate a parameterized program containing the main body program along with some additions
        to support the various state preparation, measurement, and symmetrization specifications of
        this ``Experiment``.

        State preparation and measurement are achieved via ZXZXZ-decomposed single-qubit gates,
        where the angles of each ``RZ`` rotation are declared parameters that can be assigned at
        runtime. Symmetrization is achieved by putting an ``RX`` gate (also parameterized by a
        declared value) before each ``MEASURE`` operation. In addition, a ``RESET`` operation
        is prepended to the ``Program`` if the experiment has active qubit reset enabled. Finally,
        each qubit specified in the settings is measured, and the number of shots is added.

        :return: Parameterized ``Program`` that is capable of collecting statistics for every
            ``ExperimentSetting`` in this ``Experiment``.
        """
        meas_qubits = self.get_meas_qubits()

        p = Program()

        if self.reset:
            if any(
                    isinstance(instr, (Reset, ResetQubit))
                    for instr in self.program):
                raise ValueError("RESET already added to program")
            p += RESET()

        for settings in self:
            assert len(settings) == 1
            if ("X" in str(settings[0].in_state)) or ("Y" in str(
                    settings[0].in_state)):
                if f"DECLARE preparation_alpha" in self.program.out():
                    raise ValueError(
                        f'Memory "preparation_alpha" has been declared already.'
                    )
                if f"DECLARE preparation_beta" in self.program.out():
                    raise ValueError(
                        f'Memory "preparation_beta" has been declared already.'
                    )
                if f"DECLARE preparation_gamma" in self.program.out():
                    raise ValueError(
                        f'Memory "preparation_gamma" has been declared already.'
                    )
                p += parameterized_single_qubit_state_preparation(meas_qubits)
                break

        p += self.program

        for settings in self:
            assert len(settings) == 1
            if ("X" in str(settings[0].out_operator)) or ("Y" in str(
                    settings[0].out_operator)):
                if f"DECLARE measurement_alpha" in self.program.out():
                    raise ValueError(
                        f'Memory "measurement_alpha" has been declared already.'
                    )
                if f"DECLARE measurement_beta" in self.program.out():
                    raise ValueError(
                        f'Memory "measurement_beta" has been declared already.'
                    )
                if f"DECLARE measurement_gamma" in self.program.out():
                    raise ValueError(
                        f'Memory "measurement_gamma" has been declared already.'
                    )
                p += parameterized_single_qubit_measurement_basis(meas_qubits)
                break

        if self.symmetrization != 0:
            if f"DECLARE symmetrization" in self.program.out():
                raise ValueError(
                    f'Memory "symmetrization" has been declared already.')
            p += parameterized_readout_symmetrization(meas_qubits)

        if "DECLARE ro" in self.program.out():
            raise ValueError(
                'Memory "ro" has already been declared for this program.')
        p += measure_qubits(meas_qubits)

        p.wrap_in_numshots_loop(self.shots)

        return p
    def __init__(self, qc, file_prefix, num_bits, hamil,
                all_var_nums, fun_name_to_fun,
                do_resets=True, **kwargs):
        """
        Constructor

        Do in constructor as much hamil indep stuff as possible so don't
        have to redo it with every call to cost fun. Also,
        when self.num_samples !=0,  we store a dict called term_to_exec
        mapping an executable (output of Rigetti compile() function) to a
        term,  for each term in the hamiltonian hamil. When num_samples=0,
        term_to_exec={}

        Parameters
        ----------
        qc : QuantumComputer
        file_prefix : str
        num_bits : int
        hamil : QubitOperator
        all_var_nums : list[int]
        fun_name_to_fun : dict[str, function]
        do_resets : bool
        kwargs : dict
            key-words args of MeanHamilMinimizer constructor

        Returns
        -------

        """

        MeanHamil.__init__(self, file_prefix, num_bits, hamil,
                           all_var_nums, fun_name_to_fun, **kwargs)
        self.qc = qc
        self.do_resets = do_resets

        # this creates a file with all PyQuil gates that
        # are independent of hamil. Gates may contain free parameters
        self.translator = Qubiter_to_RigettiPyQuil(
            self.file_prefix, self.num_bits,
            aqasm_name='RigPyQuil', prelude_str='', ending_str='')
        with open(self.translator.aqasm_path, 'r') as fi:
            self.translation_line_list = fi.readlines()

        pg = Program()
        self.pg = pg
        if self.num_samples:

            # pg prelude
            pg += Pragma('INITIAL_REWIRING', ['"PARTIAL"'])
            if self.do_resets:
                pg += RESET()
            ro = pg.declare('ro', 'BIT', self.num_bits)
            s = ''
            for var_num in self.all_var_nums:
                vname = self.translator.vprefix + str(var_num)
                s += vname
                s += ' = pg.declare("'
                s += vname
                s += '", memory_type="REAL")\n'
            exec(s)

            # add to pg the operations that are independent of hamil
            for line in self.translation_line_list:
                line = line.strip('\n')
                if line:
                    exec(line)

            len_pg_in = len(pg)

            # hamil loop to store executables for each term in hamil
            self.term_to_exec = {}
            for term, coef in self.hamil.terms.items():

                # reset pg to initial length.
                # Temporary work-around to bug
                # in PyQuil ver 2.5.0.
                # Slicing was changing
                # pg from type Program to type list
                pg = Program(pg[:len_pg_in])
                self.pg = pg

                # add xy measurements coda to pg
                bit_pos_to_xy_str =\
                    {bit: action for bit, action in term if action != 'Z'}
                RigettiTools.add_xy_meas_coda_to_program(
                    pg, bit_pos_to_xy_str)

                # request measurements
                for i in range(self.num_bits):
                    pg += MEASURE(i, ro[i])

                pg.wrap_in_numshots_loop(shots=self.num_samples)

                executable = self.qc.compile(pg)
                # print(",,,...", executable)
                self.term_to_exec[term] = executable
Beispiel #20
0
    def simulate(self, amplitudes):
        """Perform the simulation for the molecule.

        Args:
            amplitudes (list): The initial amplitudes (float64).
        Returns:
            float64: The total energy (energy).
        Raise:
            ValueError: If the dimension of the amplitude list is incorrect.
        """
        if len(amplitudes) != self.amplitude_dimension:
            raise ValueError("Incorrect dimension for amplitude list.")

        #Anti-hermitian operator and its qubit form
        generator = uccsd_singlet_generator(amplitudes, self.of_mole.n_qubits,
                                            self.of_mole.n_electrons)
        jw_generator = jordan_wigner(generator)
        pyquil_generator = qubitop_to_pyquilpauli(jw_generator)

        p = Program(Pragma('INITIAL_REWIRING', ['"GREEDY"']))
        # Set initial wavefunction (Hartree-Fock)
        for i in range(self.of_mole.n_electrons):
            p.inst(X(i))
        # Trotterization (unitary for UCCSD state preparation)
        for term in pyquil_generator.terms:
            term.coefficient = np.imag(term.coefficient)
            p += exponentiate(term)
        p.wrap_in_numshots_loop(self.backend_options["n_shots"])

        #  Do not simulate if no operator was passed
        if len(self.qubit_hamiltonian.terms) == 0:
            return 0.
        else:
            # Run computation using the right backend
            if isinstance(self.backend_options["backend"],
                          WavefunctionSimulator):
                energy = self.backend_options["backend"].expectation(
                    prep_prog=p, pauli_terms=self.forest_qubit_hamiltonian)
            else:
                # Set up experiment, each setting corresponds to a particular measurement basis
                settings = [
                    ExperimentSetting(in_state=TensorProductState(),
                                      out_operator=forest_term)
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                experiment = TomographyExperiment(settings=settings, program=p)
                print(experiment, "\n")
                results = self.backend_options["backend"].experiment(
                    experiment)

                energy = 0.
                coefficients = [
                    forest_term.coefficient
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                for i in range(len(results)):
                    energy += results[i].expectation * coefficients[i]

            energy = np.real(energy)

        # Save the amplitudes so we have the optimal ones for RDM calculation
        self.optimized_amplitudes = amplitudes

        return energy
Beispiel #21
0
 def run(qc: QuantumComputer, exp: Program, n_trials: int) -> np.ndarray:
     exp.wrap_in_numshots_loop(n_trials)
     executable = qc.compiler.native_quil_to_executable(basic_compile(exp))
     return qc.run(executable)
Beispiel #22
0
    def __init__(self,
                 qc,
                 file_prefix,
                 num_bits,
                 hamil,
                 all_var_nums,
                 fun_name_to_fun,
                 do_resets=True,
                 **kwargs):
        """
        Constructor

        Do in constructor as much hamil indep stuff as possible so don't
        have to redo it with every call to cost fun. Also,
        when self.num_samples !=0,  we store a dict called term_to_exec
        mapping an executable (output of Rigetti compile() function) to a
        term,  for each term in the hamiltonian hamil. When num_samples=0,
        term_to_exec={}

        Parameters
        ----------
        qc : QuantumComputer
        file_prefix : str
        num_bits : int
        hamil : QubitOperator
        all_var_nums : list[int]
        fun_name_to_fun : dict[str, function]
        do_resets : bool
        kwargs : dict
            key-words args of MeanHamilMinimizer constructor

        Returns
        -------

        """

        MeanHamil.__init__(self, file_prefix, num_bits, hamil, all_var_nums,
                           fun_name_to_fun, **kwargs)
        self.qc = qc
        self.do_resets = do_resets

        # this creates a file with all PyQuil gates that
        # are independent of hamil. Gates may contain free parameters
        self.translator = Qubiter_to_RigettiPyQuil(self.file_prefix,
                                                   self.num_bits,
                                                   aqasm_name='RigPyQuil',
                                                   prelude_str='',
                                                   ending_str='')
        with open(self.translator.aqasm_path, 'r') as fi:
            self.translation_line_list = fi.readlines()

        pg = Program()
        self.pg = pg
        if self.num_samples:

            # pg prelude
            pg += Pragma('INITIAL_REWIRING', ['"PARTIAL"'])
            if self.do_resets:
                pg += RESET()
            ro = pg.declare('ro', 'BIT', self.num_bits)
            s = ''
            for var_num in self.all_var_nums:
                vname = self.translator.vprefix + str(var_num)
                s += vname
                s += ' = pg.declare("'
                s += vname
                s += '", memory_type="REAL")\n'
            exec(s)

            # add to pg the operations that are independent of hamil
            for line in self.translation_line_list:
                line = line.strip('\n')
                if line:
                    exec(line)

            len_pg_in = len(pg)

            # hamil loop to store executables for each term in hamil
            self.term_to_exec = {}
            for term, coef in self.hamil.terms.items():

                # reset pg to initial length.
                # Temporary work-around to bug
                # in PyQuil ver 2.5.0.
                # Slicing was changing
                # pg from type Program to type list
                pg = Program(pg[:len_pg_in])
                self.pg = pg

                # add xy measurements coda to pg
                bit_pos_to_xy_str =\
                    {bit: action for bit, action in term if action != 'Z'}
                RigettiTools.add_xy_meas_coda_to_program(pg, bit_pos_to_xy_str)

                # request measurements
                for i in range(self.num_bits):
                    pg += MEASURE(i, ro[i])

                pg.wrap_in_numshots_loop(shots=self.num_samples)

                executable = self.qc.compile(pg)
                # print(",,,...", executable)
                self.term_to_exec[term] = executable