Example #1
0
def test_sample_circuit_pyquil():
    circuit = pyquil.Program(pyquil.gates.H(0), pyquil.gates.CNOT(0, 1))

    h_rep = OperationRepresentation(
        ideal=circuit[:1],
        basis_expansion={
            NoisyOperation(pyquil.Program(pyquil.gates.X(0))): 0.6,
            NoisyOperation(pyquil.Program(pyquil.gates.Z(0))): -0.6,
        },
    )

    cnot_rep = OperationRepresentation(
        ideal=circuit[1:],
        basis_expansion={
            NoisyOperation(pyquil.Program(pyquil.gates.CNOT(0, 1))): 0.7,
            NoisyOperation(pyquil.Program(pyquil.gates.CZ(0, 1))): -0.7,
        },
    )

    for _ in range(50):
        sampled_circuits, signs, norm = sample_circuit(
            circuit, representations=[h_rep, cnot_rep])

        assert isinstance(sampled_circuits[0], pyquil.Program)
        assert len(sampled_circuits[0]) == 2
        assert signs[0] in (-1, 1)
        assert norm >= 1
Example #2
0
def test_sample_circuit_cirq(measure):
    circuit = cirq.Circuit(
        cirq.ops.H.on(cirq.LineQubit(0)),
        cirq.ops.CNOT.on(*cirq.LineQubit.range(2)),
    )
    if measure:
        circuit.append(cirq.measure_each(*cirq.LineQubit.range(2)))

    h_rep = OperationRepresentation(
        ideal=circuit[:1],
        basis_expansion={
            NoisyOperation.from_cirq(circuit=cirq.X): 0.6,
            NoisyOperation.from_cirq(circuit=cirq.Z): -0.6,
        },
    )

    cnot_rep = OperationRepresentation(
        ideal=circuit[1:2],
        basis_expansion={
            NoisyOperation.from_cirq(circuit=cirq.CNOT): 0.7,
            NoisyOperation.from_cirq(circuit=cirq.CZ): -0.7,
        },
    )

    for _ in range(50):
        sampled_circuits, signs, norm = sample_circuit(
            circuit, representations=[h_rep, cnot_rep])

        assert isinstance(sampled_circuits[0], cirq.Circuit)
        assert len(sampled_circuits[0]) == 2
        assert signs[0] in (-1, 1)
        assert norm >= 1
Example #3
0
def test_sample_circuit_trivial_decomposition():
    circuit = cirq.Circuit(cirq.ops.H.on(cirq.NamedQubit("Q")))
    rep = OperationRepresentation(
        ideal=circuit, basis_expansion={NoisyOperation(circuit): 1.0})

    sampled_circuits, signs, norm = sample_circuit(circuit, [rep],
                                                   random_state=1)
    assert _equal(sampled_circuits[0], circuit)
    assert signs[0] == 1
    assert np.isclose(norm, 1)
Example #4
0
def test_sample_circuit_with_seed():
    circ = cirq.Circuit([cirq.X.on(cirq.LineQubit(0)) for _ in range(10)])
    rep = OperationRepresentation(
        ideal=cirq.Circuit(cirq.X.on(cirq.LineQubit(0))),
        basis_expansion={
            NoisyOperation.from_cirq(cirq.Z): 1.0,
            NoisyOperation.from_cirq(cirq.X): -1.0,
        },
    )

    expected_circuits, expected_signs, expected_norm = sample_circuit(
        circ, [rep], random_state=4)

    # Check we're not sampling the same operation every call to sample_sequence
    assert len(set(expected_circuits[0].all_operations())) > 1

    for _ in range(10):
        sampled_circuits, sampled_signs, sampled_norm = sample_circuit(
            circ, [rep], random_state=4)
        assert _equal(sampled_circuits[0], expected_circuits[0])
        assert sampled_signs[0] == expected_signs[0]
        assert sampled_norm == expected_norm
Example #5
0
def test_sample_circuit_partial_representations():
    circuit = cirq.Circuit(
        cirq.ops.H.on(cirq.LineQubit(0)),
        cirq.ops.CNOT.on(*cirq.LineQubit.range(2)),
    )

    cnot_rep = OperationRepresentation(
        ideal=circuit[1:2],
        basis_expansion={
            NoisyOperation.from_cirq(circuit=cirq.CNOT): 0.7,
            NoisyOperation.from_cirq(circuit=cirq.CZ): -0.7,
        },
    )

    for _ in range(10):
        with pytest.warns(UserWarning, match="No representation found for"):
            sampled_circuits, signs, norm = sample_circuit(
                circuit, representations=[cnot_rep])
            assert isinstance(sampled_circuits[0], cirq.Circuit)
            assert len(sampled_circuits[0]) == 2
            assert signs[0] in (-1, 1)
            assert norm >= 1
Example #6
0
def test_sample_circuit_choi():
    """Tests the sample_circuit by comparing the exact Choi matrices."""
    # A simple 2-qubit circuit
    qreg = cirq.LineQubit.range(2)
    ideal_circ = cirq.Circuit(
        cirq.X.on(qreg[0]),
        cirq.I.on(qreg[1]),
        cirq.CNOT.on(*qreg),
    )

    noisy_circuit = ideal_circ.with_noise(cirq.depolarize(BASE_NOISE))

    ideal_choi = _circuit_to_choi(ideal_circ)
    noisy_choi = _operation_to_choi(noisy_circuit)

    rep_list = []
    for op in ideal_circ.all_operations():
        rep_list.append(
            represent_operation_with_local_depolarizing_noise(
                cirq.Circuit(op),
                BASE_NOISE,
            ))

    choi_unbiased_estimates = []
    rng = np.random.RandomState(1)
    for _ in range(500):
        imp_circ, sign, norm = sample_circuit(ideal_circ,
                                              rep_list,
                                              random_state=rng)
        noisy_imp_circ = imp_circ.with_noise(cirq.depolarize(BASE_NOISE))
        sequence_choi = _circuit_to_choi(noisy_imp_circ)
        choi_unbiased_estimates.append(norm * sign * sequence_choi)

    choi_pec_estimate = np.average(choi_unbiased_estimates, axis=0)
    noise_error = np.linalg.norm(ideal_choi - noisy_choi)
    pec_error = np.linalg.norm(ideal_choi - choi_pec_estimate)

    assert pec_error < noise_error
    assert np.allclose(ideal_choi, choi_pec_estimate, atol=0.05)
Example #7
0
def execute_with_pec(
    circuit: QPROGRAM,
    executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
    observable: Optional[Observable] = None,
    *,
    representations: Sequence[OperationRepresentation],
    precision: float = 0.03,
    num_samples: Optional[int] = None,
    force_run_all: bool = True,
    random_state: Optional[Union[int, np.random.RandomState]] = None,
    full_output: bool = False,
) -> Union[float, Tuple[float, Dict[str, Any]]]:
    r"""Estimates the error-mitigated expectation value associated to the
    input circuit, via the application of probabilistic error cancellation
    (PEC). [Temme2017]_ [Endo2018]_.

    This function implements PEC by:

    1. Sampling different implementable circuits from the quasi-probability
       representation of the input circuit;
    2. Evaluating the noisy expectation values associated to the sampled
       circuits (through the "executor" function provided by the user);
    3. Estimating the ideal expectation value from a suitable linear
       combination of the noisy ones.

    Args:
        circuit: The input circuit to execute with error-mitigation.
        executor: A Mitiq executor that executes a circuit and returns the
            unmitigated ``QuantumResult`` (e.g. an expectation value).
        observable: Observable to compute the expectation value of. If None,
            the `executor` must return an expectation value. Otherwise,
            the `QuantumResult` returned by `executor` is used to compute the
            expectation of the observable.
        representations: Representations (basis expansions) of each operation
            in the input circuit.
        precision: The desired estimation precision (assuming the observable
            is bounded by 1). The number of samples is deduced according
            to the formula (one_norm / precision) ** 2, where 'one_norm'
            is related to the negativity of the quasi-probability
            representation [Temme2017]_. If 'num_samples' is explicitly set
            by the user, 'precision' is ignored and has no effect.
        num_samples: The number of noisy circuits to be sampled for PEC.
            If not given, this is deduced from the argument 'precision'.
        force_run_all: If True, all sampled circuits are executed regardless of
            uniqueness, else a minimal unique set is executed.
        random_state: Seed for sampling circuits.
        full_output: If False only the average PEC value is returned.
            If True a dictionary containing all PEC data is returned too.

    Returns:
        The tuple ``(pec_value, pec_data)`` where ``pec_value`` is the
        expectation value estimated with PEC and ``pec_data`` is a dictionary
        which contains all the raw data involved in the PEC process (including
        the PEC estimation error).
        The error is estimated as ``pec_std / sqrt(num_samples)``, where
        ``pec_std`` is the standard deviation of the PEC samples, i.e., the
        square root of the mean squared deviation of the sampled values from
        ``pec_value``. If ``full_output`` is ``True``, only ``pec_value`` is
        returned.

    .. [Endo2018] : Suguru Endo, Simon C. Benjamin, Ying Li,
        "Practical Quantum Error Mitigation for Near-Future Applications"
        *Phys. Rev. **X 8**, 031027 (2018),
        (https://arxiv.org/abs/1712.09271).

    .. [Takagi2020] : Ryuji Takagi,
        "Optimal resource cost for error mitigation,"
        (https://arxiv.org/abs/2006.12509).
    """
    if isinstance(random_state, int):
        random_state = np.random.RandomState(random_state)

    if not (0 < precision <= 1):
        raise ValueError(
            "The value of 'precision' should be within the interval (0, 1],"
            f" but precision is {precision}.")

    converted_circuit, input_type = convert_to_mitiq(circuit)

    # Get the 1-norm of the circuit quasi-probability representation
    _, _, norm = sample_circuit(
        converted_circuit,
        representations,
        num_samples=1,
    )

    # Deduce the number of samples (if not given by the user)
    if not isinstance(num_samples, int):
        num_samples = int((norm / precision)**2)

    # Issue warning for very large sample size
    if num_samples > 10**5:
        warnings.warn(_LARGE_SAMPLE_WARN, LargeSampleWarning)

    # Sample all the circuits
    sampled_circuits, signs, _ = sample_circuit(
        converted_circuit,
        representations,
        random_state=random_state,
        num_samples=num_samples,
    )

    # Convert back to the original type
    sampled_circuits = [
        convert_from_mitiq(cast(CirqCircuit, c), input_type)
        for c in sampled_circuits
    ]

    # Execute all sampled circuits
    if not isinstance(executor, Executor):
        executor = Executor(executor)

    results = executor.evaluate(sampled_circuits, observable, force_run_all)

    # Evaluate unbiased estimators [Temme2017] [Endo2018] [Takagi2020]
    unbiased_estimators = [
        norm * s * val  # type: ignore[operator]
        for s, val in zip(signs, results)
    ]

    pec_value = np.average(unbiased_estimators)

    if not full_output:
        return pec_value

    # Build dictionary with additional results and data
    pec_data: Dict[str, Any] = {
        "num_samples": num_samples,
        "precision": precision,
        "pec_value": pec_value,
        "pec_error": np.std(unbiased_estimators) / np.sqrt(num_samples),
        "unbiased_estimators": unbiased_estimators,
        "measured_expectation_values": results,
        "sampled_circuits": sampled_circuits,
    }

    return pec_value, pec_data
Example #8
0
def execute_with_pec(
    circuit: QPROGRAM,
    executor: Callable,
    representations: List[OperationRepresentation],
    precision: float = 0.03,
    num_samples: Optional[int] = None,
    force_run_all: bool = True,
    random_state: Optional[Union[int, np.random.RandomState]] = None,
    full_output: bool = False,
) -> Union[float, Tuple[float, Dict[str, Any]]]:
    r"""Evaluates the expectation value associated to the input circuit
    using probabilistic error cancellation (PEC) [Temme2017]_ [Endo2018]_.

    This function implements PEC by:

    1. Sampling different implementable circuits from the quasi-probability
       representation of the input circuit;
    2. Evaluating the noisy expectation values associated to the sampled
       circuits (through the "executor" function provided by the user);
    3. Estimating the ideal expectation value from a suitable linear
       combination of the noisy ones.

    Args:
        circuit: The input circuit to execute with error-mitigation.
        executor: A function which executes a circuit (sequence of circuits)
            and returns an expectation value (sequence of expectation values).
        representations: Representations (basis expansions) of each operation
            in the input circuit.
        precision: The desired estimation precision (assuming the observable
            is bounded by 1). The number of samples is deduced according
            to the formula (one_norm / precision) ** 2, where 'one_norm'
            is related to the negativity of the quasi-probability
            representation [Temme2017]_. If 'num_samples' is explicitly set
            by the user, 'precision' is ignored and has no effect.
        num_samples: The number of noisy circuits to be sampled for PEC.
            If not given, this is deduced from the argument 'precision'.
        force_run_all: If True, all sampled circuits are executed regardless of
            uniqueness, else a minimal unique set is executed.
        random_state: Seed for sampling circuits.
        full_output: If False only the average PEC value is returned.
            If True a dictionary containing all PEC data is returned too.

    Returns:
        pec_value: The PEC estimate of the ideal expectation value associated
            to the input circuit.
        pec_data: A dictionary which contains all the raw data involved in the
            PEC process (including the PEC estimation error). The error is
            estimated as pec_std / sqrt(num_samples), where 'pec_std' is the
            standard deviation of the PEC samples, i.e., the square root of the
            mean squared deviation of the sampled values from 'pec_value'.
            This is returned only if ``full_output`` is ``True``.

    .. [Temme2017] : Kristan Temme, Sergey Bravyi, Jay M. Gambetta,
        "Error mitigation for short-depth quantum circuits,"
        *Phys. Rev. Lett.* **119**, 180509 (2017),
        (https://arxiv.org/abs/1612.02058).

    .. [Endo2018] : Suguru Endo, Simon C. Benjamin, Ying Li,
        "Practical Quantum Error Mitigation for Near-Future Applications"
        *Phys. Rev. **X 8**, 031027 (2018),
        (https://arxiv.org/abs/1712.09271).

    .. [Takagi2020] : Ryuji Takagi,
        "Optimal resource cost for error mitigation,"
        (https://arxiv.org/abs/2006.12509).
    """
    if isinstance(random_state, int):
        random_state = np.random.RandomState(random_state)

    if not (0 < precision <= 1):
        raise ValueError(
            "The value of 'precision' should be within the interval (0, 1],"
            f" but precision is {precision}.")

    # Get the 1-norm of the circuit quasi-probability representation
    _, _, norm = sample_circuit(circuit, representations)

    # Deduce the number of samples (if not given by the user)
    if not isinstance(num_samples, int):
        num_samples = int((norm / precision)**2)

    # Issue warning for very large sample size
    if num_samples > 10**5:
        warnings.warn(_LARGE_SAMPLE_WARN, LargeSampleWarning)

    sampled_circuits = []
    signs = []

    for _ in range(num_samples):
        sampled_circuit, sign, _ = sample_circuit(circuit, representations,
                                                  random_state)
        sampled_circuits.append(sampled_circuit)
        signs.append(sign)

    # Execute all sampled circuits
    collected_executor = generate_collected_executor(
        executor, force_run_all=force_run_all)
    exp_values = collected_executor(sampled_circuits)

    # Evaluate unbiased estimators [Temme2017] [Endo2018] [Takagi2020]
    unbiased_estimators = [norm * s * val for s, val in zip(signs, exp_values)]

    pec_value = np.average(unbiased_estimators)

    if not full_output:
        return pec_value

    # Build dictionary with additional results and data
    pec_data: Dict[str, Any] = {}

    pec_data = {
        "num_samples": num_samples,
        "precision": precision,
        "pec_value": pec_value,
        "pec_error": np.std(unbiased_estimators) / np.sqrt(num_samples),
        "unbiased_estimators": unbiased_estimators,
        "measured_expectation_values": exp_values,
        "sampled_circuits": sampled_circuits,
    }

    return pec_value, pec_data