示例#1
0
def generate_t2_star_experiments(
        qubits: Sequence[int],
        times: Sequence[float],
        detuning: float = 1e6) -> List[ObservablesExperiment]:
    """
    Return ObservablesExperiments containing programs which constitute a T2 star experiment to
    measure the T2 star coherence decay time for each qubit in qubits.

    For each delay time in times a single program will be generated in which all qubits are
    initialized to the minusY state and simultaneously measured along the Y axis after the given
    delay and Z rotation. 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).

    :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 in Hz.
    :return: ObservablesExperiments which can be run to acquire an estimate of T2* for each qubit
    """
    expts = []
    for t in times:
        t = round(t, 7)  # enforce 100ns boundaries
        program = Program()
        settings = []
        for q in qubits:
            program += Pragma('DELAY', [q], str(t))
            program += RZ(2 * pi * t * detuning, q)
            settings.append(ExperimentSetting(minusY(q), PauliTerm('Y', q)))

        expts.append(ObservablesExperiment([settings], program))

    return expts
示例#2
0
def test_survival_statistics():
    # setup
    p0 = 0.98  # p(q0 = 0)
    p1 = 0.5  # p(q1 = 0)
    p_joint = p0 * p1 + (1 - p0) * (1 - p1)
    expectations = [2 * p0 - 1, 2 * p1 - 1, 2 * p_joint - 1]
    variances = np.asarray(
        [p0 * (1 - p0), p1 * (1 - p1), p_joint * (1 - p_joint)]) * 2**2
    n = 10000
    qubits = (0, 1)

    settings = [
        ExperimentSetting(zeros_state(qubits), op)
        for op in all_traceless_pauli_z_terms(qubits)
    ]
    results = (ExperimentResult(setting, exp, n, std_err=np.sqrt(v / n))
               for setting, exp, v in zip(settings, expectations, variances))
    stats = get_stats_by_qubit_group([qubits], [results])[qubits]
    exps = stats['expectation'][0]
    errs = stats['std_err'][0]

    np.testing.assert_allclose(exps, expectations)
    np.testing.assert_allclose(errs, np.sqrt(variances / n))

    survival_prob, survival_var = z_obs_stats_to_survival_statistics(
        exps, errs, n)

    np.testing.assert_allclose(survival_prob, p0 * p1)
    np.testing.assert_allclose(np.sqrt(survival_var), .004999, rtol=1e-4)
def generate_monte_carlo_state_dfe_experiment(benchmarker: BenchmarkConnection, program: Program,
                                              qubits: List[int], n_terms=200) \
        -> ObservablesExperiment:
    """
    Estimate state fidelity by sampled direct fidelity estimation.

    This leads to constant overhead (w.r.t. number of qubits) fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param program: A program comprised of clifford gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :param benchmarker: The `BenchmarkConnection` object used to design experiments
    :param n_terms: Number of randomly chosen observables to measure. This number should be a
        constant less than ``2**len(qubits)``, otherwise ``exhaustive_state_dfe`` is more efficient.
    :return: an ObservablesExperiment that constitutes a state DFE experiment.
    """
    # pick n_terms different random combinations of I and Z on the qubits
    rand_iz_paulis = np.random.choice(['I', 'Z'], size=(n_terms, len(qubits)))

    settings = []
    for iz_pauli in rand_iz_paulis:
        # sample a new state if this one is all identity
        while 'Z' not in iz_pauli:
            iz_pauli = np.random.choice(['I', 'Z'], size=len(qubits))
        # conjugate the non-trivial iz Pauli by the ideal state prep program
        obs = benchmarker.apply_clifford_to_pauli(
            program, str_to_pauli_term(''.join(iz_pauli)))
        settings.append(ExperimentSetting(zeros_state(qubits), obs))

    return ObservablesExperiment(settings, program=program)
示例#4
0
def test_survival_statistics_2():
    # p0 is probability qubit 0 is 0
    for p0 in [1.0, .99]:
        exp = 2 * p0 - 1
        variance = p0 * (1 - p0) * 2**2
        n = 100
        qubits = (0, )
        setting = [
            ExperimentSetting(zeros_state(qubits), op)
            for op in all_traceless_pauli_z_terms(qubits)
        ][0]
        results = (ExperimentResult(setting,
                                    exp,
                                    n,
                                    std_err=np.sqrt(variance / n)), )

        stats = get_stats_by_qubit_group([qubits], [results])[qubits]
        exps = stats['expectation'][0]
        errs = stats['std_err'][0]

        np.testing.assert_allclose(exps, [exp])
        np.testing.assert_allclose(errs, [np.sqrt(variance / n)])

        survival_prob, survival_var = z_obs_stats_to_survival_statistics(
            exps, errs)

        assert survival_prob == p0
def generate_exhaustive_state_dfe_experiment(
        benchmarker: BenchmarkConnection, program: Program,
        qubits: list) -> ObservablesExperiment:
    """
    Estimate state fidelity by exhaustive direct fidelity estimation.

    This leads to a quadratic reduction in overhead w.r.t. state tomography for
    fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param benchmarker: object returned from pyquil.api.get_benchmarker() used to conjugate each
        Pauli by the Clifford program
    :param program: A program comprised of Clifford group gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :return: an ObservablesExperiment that constitutes a state DFE experiment.
    """
    # measure all of the traceless combinations of I and Z on the qubits conjugated by the ideal
    # Clifford state preparation program. The in_state is all the all zero state since this is
    # the assumed initialization of the state preparation.
    settings = [
        ExperimentSetting(in_state=zeros_state(qubits),
                          observable=benchmarker.apply_clifford_to_pauli(
                              program, iz_pauli))
        for iz_pauli in all_traceless_pauli_z_terms(qubits)
    ]
    return ObservablesExperiment(settings, program=program)
示例#6
0
def pick_two_eigenvecs_prep_meas_settings(
        fix_qubit: Tuple[int, int],
        rotate_qubit: int,
        change_of_basis: Optional[Program] = None):
    prep_prog = Program()
    if change_of_basis is not None:
        prep_prog += change_of_basis

    if fix_qubit[1] == 1:
        fixed_q_state = minusZ(
            fix_qubit[0])  # initialize fixed qubit to |1> state
    else:
        fixed_q_state = plusZ(
            fix_qubit[0])  # initialize fixed qubit to |0> state

    init_state = fixed_q_state * plusX(rotate_qubit)

    fixed_q_ops = [PauliTerm('I', fix_qubit[0]), PauliTerm('Z', fix_qubit[0])]
    rot_q_ops = [PauliTerm('X', rotate_qubit), PauliTerm('Y', rotate_qubit)]

    settings = [
        ExperimentSetting(init_state, term1 * term2) for term1 in fixed_q_ops
        for term2 in rot_q_ops
    ]

    # prepare superposition, return to z basis, then do the measurement settings.
    return prep_prog, Program(prep_prog).dagger(), settings
示例#7
0
def test_survival_statistics_3():
    # p0 is probability qubit 0 is 0
    # p1 is probability qubit 1 is 0

    for p0, p1 in zip([1.0, .99, .99], [1.0, 1.0, .99]):
        # setup
        p_joint = p0 * p1 + (1 - p0) * (1 - p1)
        expectations = [2 * p0 - 1, 2 * p1 - 1, 2 * p_joint - 1]
        variances = np.asarray(
            [p0 * (1 - p0), p1 * (1 - p1), p_joint * (1 - p_joint)]) * 2**2
        n = 100
        qubits = (0, 1)
        settings = [
            ExperimentSetting(zeros_state(qubits), op)
            for op in all_traceless_pauli_z_terms(qubits)
        ]
        results = (ExperimentResult(setting, exp, n, std_err=np.sqrt(
            v /
            n)) for setting, exp, v in zip(settings, expectations, variances))

        stats = get_stats_by_qubit_group([qubits], [results])[qubits]
        exps = stats['expectation'][0]
        errs = stats['std_err'][0]

        np.testing.assert_allclose(exps, expectations)
        np.testing.assert_allclose(errs, np.sqrt(variances / n))

        survival_prob, survival_var = z_obs_stats_to_survival_statistics(
            exps, errs, n)
        assert survival_prob == p0 * p1
示例#8
0
def generate_t1_experiments(qubits: Sequence[int], times: Sequence[float]) \
        -> List[ObservablesExperiment]:
    """
    Return a ObservablesExperiment containing programs which constitute a t1 experiment to
    measure the decay time from the excited state to ground state for each qubit in qubits.

    For each delay time in times a single program will be generated in which all qubits are
    initialized to the excited state (`|1>`) and simultaneously measured after the given delay.

    :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.
    :return: ObservablesExperiments which will measure the decay of each qubit after
        initialization to the 1 state and delay of t seconds for each t in times.
    """
    expts = []
    for t in times:
        t = round(t, 7)  # enforce 100ns boundaries
        program = Program()
        settings = []
        for q in qubits:
            program += Pragma('DELAY', [q], str(t))
            settings.append(ExperimentSetting(minusZ(q), PauliTerm('Z', q)))

        expts.append(ObservablesExperiment([settings], program))

    return expts
示例#9
0
def generate_cz_phase_ramsey_experiments(
        cz_qubits: Sequence[int], measure_qubit: int,
        angles: Sequence[float]) -> List[ObservablesExperiment]:
    """
    Return ObservablesExperiments containing programs that constitute a CZ phase ramsey experiment.

    :param cz_qubits: the qubits participating in the cz gate
    :param measure_qubit: Which qubit to measure.
    :param angles: A list of angles at which to make a measurement
    :return: ObservablesExperiments which can be run to estimate the effective RZ rotation
        applied to a single qubit during the application of a CZ gate.
    """
    expts = []
    for angle in angles:
        settings = []
        program = Program()
        # apply CZ, possibly inducing an effective RZ on measure qubit by some angle
        program += CZ(*cz_qubits)
        # apply phase to measure_qubit akin to T2 experiment
        program += RZ(angle, measure_qubit)
        settings = [
            ExperimentSetting(minusY(measure_qubit),
                              PauliTerm('Y', measure_qubit))
        ]

        expts.append(ObservablesExperiment([settings], program))

    return expts
def test_R_operator_fixed_point_2_qubit():
    # Check fixed point of operator. See Eq. 5 in Řeháček et al., PRA 75, 042108 (2007).
    qubits = [0, 1]
    id_setting = ExperimentSetting(in_state=zeros_state(qubits), observable=sI(qubits[0])*sI(
        qubits[1]))
    zz_setting = ExperimentSetting(in_state=zeros_state(qubits), observable=sZ(qubits[0])*sI(
        qubits[1]))

    id_result = ExperimentResult(setting=id_setting, expectation=1, total_counts=1)
    zzplus_result = ExperimentResult(setting=zz_setting, expectation=1, total_counts=1)

    zz_results = [id_result, zzplus_result]

    # Z basis test
    r = _R(P00, zz_results, qubits)
    actual = r @ P00 @ r
    np.testing.assert_allclose(actual, P00, atol=1e-12)
def generate_exhaustive_process_dfe_experiment(
        benchmarker: BenchmarkConnection, program: Program,
        qubits: list) -> ObservablesExperiment:
    """
    Estimate process fidelity by exhaustive direct fidelity estimation (DFE).

    This leads to a quadratic reduction in overhead w.r.t. process tomography for
    fidelity estimation.

    The algorithm is due to:

    .. [DFE1]  Practical Characterization of Quantum Devices without Tomography.
            Silva et al.
            PRL 107, 210404 (2011).
            https://doi.org/10.1103/PhysRevLett.107.210404
            https://arxiv.org/abs/1104.3835

    .. [DFE2]  Direct Fidelity Estimation from Few Pauli Measurements.
            Flammia and Liu.
            PRL 106, 230501 (2011).
            https://doi.org/10.1103/PhysRevLett.106.230501
            https://arxiv.org/abs/1104.4695

    :param benchmarker: object returned from pyquil.api.get_benchmarker() used to conjugate each
        Pauli by the Clifford program
    :param program: A program comprised of Clifford group gates that defines the process for
        which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :return: an ObservablesExperiment that constitutes a process DFE experiment.
    """
    settings = []
    # generate all n-qubit pauli strings but skip the first all identity term
    for pauli_labels in [
            ''.join(x) for x in itertools.product('IXYZ', repeat=len(qubits))
    ][1:]:
        # calculate the appropriate output pauli from applying the ideal program to the Pauli
        observable = benchmarker.apply_clifford_to_pauli(
            program, str_to_pauli_term(pauli_labels, qubits))
        # keep track of non-identity terms that may have a sign contribution
        non_identity_idx = [0 if label == 'I' else 1 for label in pauli_labels]
        # now replace the identities with Z terms, so they can be decomposed into Z eigenstates
        state_labels = [
            'Z' if label == 'I' else label for label in pauli_labels
        ]

        # loop over the ±1 eigenstates of each Pauli
        for eigenstate in itertools.product([0, 1], repeat=len(qubits)):
            in_state = TensorProductState(
                _OneQState(l, s, q)
                for l, s, q in zip(state_labels, eigenstate, qubits))
            # make the observable negative if the in_state is a negative eigenstate
            sign_contribution = (-1)**np.dot(eigenstate, non_identity_idx)
            settings.append(
                ExperimentSetting(in_state=in_state,
                                  observable=observable * sign_contribution))
    return ObservablesExperiment(settings, program=program)
def generate_monte_carlo_process_dfe_experiment(benchmarker: BenchmarkConnection, program: Program,
                                                qubits: List[int], n_terms: int = 200) \
        -> ObservablesExperiment:
    """
    Estimate process fidelity by randomly sampled direct fidelity estimation.

    This leads to constant overhead (w.r.t. number of qubits) fidelity estimation.

    The algorithm is due to [DFE1]_ and [DFE2]_.

    :param program: A program comprised of Clifford group gates that constructs a state
        for which we estimate the fidelity.
    :param qubits: The qubits to perform DFE on. This can be a superset of the qubits
        used in ``program``, in which case it is assumed the identity acts on these qubits.
        Note that we assume qubits are initialized to the ``|0>`` state.
    :param benchmarker: The `BenchmarkConnection` object used to design experiments
    :param n_terms: Number of randomly chosen observables to measure. This number should be a
        constant less than ``2**len(qubits)``, otherwise ``exhaustive_process_dfe`` is more
        efficient.
    :return: an ObservablesExperiment that constitutes a process DFE experiment.
    """
    single_q_paulis = ['I', 'X', 'Y', 'Z']
    # pick n_terms different random combinations of I, X, Y, Z on the qubits
    rand_paulis = np.random.randint(len(single_q_paulis),
                                    size=(n_terms, len(qubits)))

    settings = []
    for pauli_idxs in rand_paulis:
        # sample a new state if this one is all identity
        while sum(pauli_idxs) == 0:
            pauli_idxs = np.random.randint(len(single_q_paulis),
                                           size=len(qubits))
        # convert from indices to string
        pauli_str = ''.join([single_q_paulis[idx] for idx in pauli_idxs])
        # convert from string to PauliTerm on appropriate qubits
        pauli = str_to_pauli_term(pauli_str, qubits)
        # calculate the appropriate output pauli from applying the ideal program to the Pauli
        observable = benchmarker.apply_clifford_to_pauli(program, pauli)

        # now replace the identities with Z terms, so they can be decomposed into Z eigenstates
        state_labels = ['Z' if label == 'I' else label for label in pauli_str]

        # randomly pick between ±1 eigenstates of each Pauli
        eigenstate = np.random.randint(2, size=len(qubits))
        in_state = TensorProductState(
            _OneQState(l, s, q)
            for l, s, q in zip(state_labels, eigenstate, qubits))
        # make the observable negative if the in_state is a negative eigenstate
        # only keep track of minus eigenstates associated to non-identity Paulis, i.e. idx >= 1
        sign_contribution = (-1)**np.dot(eigenstate,
                                         [min(1, idx) for idx in pauli_idxs])
        settings.append(
            ExperimentSetting(in_state=in_state,
                              observable=observable * sign_contribution))

    return ObservablesExperiment(settings, program=program)
def _state_tomo_settings(qubits: Sequence[int]):
    """
    Yield settings over every non-identity observable in the Pauli basis over the qubits.

    Used as a helper function for generate_state_tomography_experiment

    :param qubits: The qubits to tomographize.
    """
    for obs in all_traceless_pauli_terms(qubits):
        yield ExperimentSetting(
            in_state=zeros_state(qubits),
            observable=obs,
        )
def _sic_process_tomo_settings(qubits: Sequence[int]):
    """
    Yield settings over SIC basis across I, X, Y, Z operators

    Used as a helper function for generate_process_tomography_experiment

    :param qubits: The qubits to tomographize.
    """
    for in_sics in itertools.product([SIC0, SIC1, SIC2, SIC3], repeat=len(qubits)):
        i_state = functools.reduce(mul, (state(q) for state, q in zip(in_sics, qubits)),
                                   TensorProductState())
        for obs in all_traceless_pauli_terms(qubits):
            yield ExperimentSetting(
                in_state=i_state,
                observable=obs,
            )
def group_sequences_into_parallel_experiments(parallel_expts_seqs: Sequence[List[List[Program]]],
                                              qubit_groups: Sequence[Sequence[int]],
                                              is_unitarity_expt: bool = False) \
        -> List[ObservablesExperiment]:
    """
    Consolidates randomized benchmarking sequences on separate groups of qubits into a flat list
    of ObservablesExperiments which merge parallel sets of distinct sequences.

    Each returned ObservablesExperiment constitutes a single 'parallel RB sequence' where all of
    the qubits are acted upon and measured. Running all of these ObservablesExperiments in series
    constitutes a 'parallel RB' experiment from which you can determine a decay constant for each
    group of qubits. Note that there is an important physical distinction (e.g. due to
    cross-talk) between running separate RB experiments on different groups of qubits and running
    a 'parallel RB' experiment on the collection of those groups. For this reason one should not
    expect in general that the rb decay for a particular group of qubits is comparable between
    the individual and parallel modes of rb experiment.

    :param parallel_expts_seqs: the outer Sequence is indexed by disjoint groups of qubits;
        Clifford sequences from each of these different groups (which should be of the same depth
        across qubit groups) will be merged together into a single program. The intended use-case
        is that each List[List[program]] of sequences of Cliffords is an output of
        generate_rb_experiment_sequences for disjoint groups of qubits but with identical
        depths input (see generate_rb_experiments for example). If sequences of different depth are
        merged into a Program then some qubits may be sitting idle while the sequences of greater
        depth continue running. Measurement occurs only when all sequences have terminated.
    :param qubit_groups: The partition of the qubits into groups for each of which you would like to
        estimate an rb decay. Typically this grouping of qubits should match the qubits that are
        acted on by each sequence in the corresponding List[List[Program]] of the input
        parallel_expts_seqs.
    :param is_unitarity_expt: True if the desired experiment is a unitarity experiment, in which
        case additional settings are required to estimate the purity of the sequence output.
    :return: a list of ObservablesExperiments constituting a parallel rb experiment.
    """
    expts = []
    for parallel_sequence_group in zip(*parallel_expts_seqs):
        program = merge_programs(merge_sequences(parallel_sequence_group))

        if is_unitarity_expt:
            settings = [sett for group in qubit_groups for sett in _state_tomo_settings(group)]
            expt = group_settings(ObservablesExperiment(settings, program))
        else:
            # measure observables of products of I and Z on qubits in the group, excluding all I
            settings = [ExperimentSetting(zeros_state(group), op)
                        for group in qubit_groups for op in all_traceless_pauli_z_terms(group)]
            expt = ObservablesExperiment([settings], program)
        expts.append(expt)
    return expts
def _pauli_process_tomo_settings(qubits):
    """
    Yield settings over +-XYZ basis across I, X, Y, Z operators

    Used as a helper function for generate_process_tomography_experiment

    :param qubits: The qubits to tomographize.
    """
    for states in itertools.product([plusX, minusX, plusY, minusY, plusZ, minusZ],
                                    repeat=len(qubits)):
        i_state = functools.reduce(mul, (state(q) for state, q in zip(states, qubits)),
                                   TensorProductState())
        for obs in all_traceless_pauli_terms(qubits):
            yield ExperimentSetting(
                in_state=i_state,
                observable=obs,
            )
示例#17
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
示例#18
0
def all_eigenvector_prep_meas_settings(qubits: Sequence[int],
                                       change_of_basis: Program):
    # experiment settings put initial state in superposition of computational basis
    # the prep and pre_meas programs simply convert between this and superposition of eigenvectors
    prep_prog = Program(change_of_basis)
    pre_meas_prog = Program(change_of_basis).dagger()
    init_state = reduce(mul, [plusX(q) for q in qubits], TensorProductState())

    settings = []
    for xy_q in qubits:
        z_qubits = [q for q in qubits if q != xy_q]
        xy_terms = [PauliTerm('X', xy_q), PauliTerm('Y', xy_q)]
        iz_terms = [PauliTerm('I', xy_q)]
        iz_terms += [PauliTerm('Z', q) for q in z_qubits]
        settings += [
            ExperimentSetting(init_state, xy_term * term)
            for xy_term in xy_terms for term in iz_terms
        ]
    return prep_prog, pre_meas_prog, settings
示例#19
0
def generate_rabi_experiments(qubits: Sequence[int], angles: Sequence[float]) \
        -> List[ObservablesExperiment]:
    """
    Return ObservablesExperiments containing programs which constitute a Rabi experiment.

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

    :param qubits: list of qubits to measure.
    :param angles: A list of angles at which to make a measurement
    :return: ObservablesExperiments which can be run to verify the  RX(angle, q) calibration
        for each qubit
    """
    expts = []
    for angle in angles:
        program = Program()
        settings = []
        for q in qubits:
            program += RX(angle, q)
            settings.append(ExperimentSetting(plusZ(q), PauliTerm('Z', q)))

        expts.append(ObservablesExperiment([settings], program))

    return expts
    ExperimentSetting, zeros_state, calibrate_observable_estimates
from pyquil.paulis import sI, sZ, sX
from pyquil.quil import Program

from forest.benchmarking import distance_measures as dm

# Single qubit defs
Q0 = 0
PROJ_ZERO = np.array([[1, 0], [0, 0]])
PROJ_ONE = np.array([[0, 0], [0, 1]])
ID = PROJ_ZERO + PROJ_ONE
PLUS = np.array([[1], [1]]) / np.sqrt(2)
PROJ_PLUS = PLUS @ PLUS.T.conj()
PROJ_MINUS = ID - PROJ_PLUS

ID_SETTING = ExperimentSetting(in_state=zeros_state([Q0]), observable=sI(Q0))
Z_SETTING = ExperimentSetting(in_state=zeros_state([Q0]), observable=sZ(Q0))
X_SETTING = ExperimentSetting(in_state=zeros_state([Q0]), observable=sX(Q0))

# Two qubit defs
P00 = np.kron(PROJ_ZERO, PROJ_ZERO)
P01 = np.kron(PROJ_ZERO, PROJ_ONE)
P10 = np.kron(PROJ_ONE, PROJ_ZERO)
P11 = np.kron(PROJ_ONE, PROJ_ONE)


def test_generate_1q_state_tomography_experiment():
    qubits = [0]
    prog = Program(I(qubits[0]))
    one_q_exp = generate_state_tomography_experiment(prog, qubits=qubits)
    dimension = 2 ** len(qubits)