Пример #1
0
def test_raw():
    circuit = cirq.Circuit(cirq.H.on(cirq.LineQubit(0)))
    observable = Observable(PauliString("X"))
    executor = Executor(mitiq_cirq.compute_density_matrix)
    raw_value = raw.execute(circuit, executor, observable)

    assert isinstance(raw_value, complex)
    assert executor.executed_circuits == [circuit]

    compute_density_matrix_noiseless = functools.partial(
        mitiq_cirq.compute_density_matrix, noise_level=(0.0, ))
    executor_noiseless = Executor(compute_density_matrix_noiseless)
    true_value = raw.execute(circuit, executor_noiseless, observable)
    assert np.isclose(true_value, 1.0)
    assert executor_noiseless.executed_circuits == [circuit]
Пример #2
0
def execute(
    circuit: QPROGRAM,
    executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
    observable: Optional[Observable] = None,
) -> complex:
    """Evaluates the expectation value associated to the input circuit
    without using error mitigation.

    The only purpose of this function is to provide the same interface for
    non-error-mitigated values as the rest of the techniques in Mitiq. This
    is useful when comparing error-mitigated results to non-error-mitigated
    results.

    Args:
        circuit: The circuit to run.
        executor: Executes a circuit and returns a `QuantumResult`.
        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.
    """
    if not isinstance(executor, Executor):
        executor = Executor(executor)

    return executor.evaluate(circuit, observable)[0]
Пример #3
0
def test_execute_with_ddd_with_num_trials(executor):
    """Tests the option num_trials of execute_with_ddd."""
    executor = Executor(executor)
    mitigated_1 = execute_with_ddd(
        circuit_cirq_a,
        executor,
        rule=xx,
        num_trials=1,
    )
    assert executor.calls_to_executor == 1
    assert len(executor.executed_circuits) == 1

    mitigated_2 = execute_with_ddd(
        circuit_cirq_a,
        executor,
        rule=xx,
        num_trials=2,
    )
    # Note executor contains the history of both experiments
    if executor.can_batch:
        assert executor.calls_to_executor == 2
    else:
        assert executor.calls_to_executor == 3
    assert len(executor.executed_circuits) == 3

    # For deterministic DDD sequences num_trials is irrelevant
    assert np.isclose(mitigated_1, mitigated_2)
Пример #4
0
def test_execute_with_ddd_with_full_output():
    """Tests the option full_output of execute_with_ddd."""
    executor = Executor(noiseless_serial_executor)

    ddd_value, ddd_data = execute_with_ddd(
        circuit_cirq_a,
        executor,
        rule=xx,
        num_trials=2,
        full_output=True,
    )
    assert len(executor.executed_circuits) == 2
    assert len(ddd_data["circuits_with_ddd"]) == 2
    assert len(ddd_data["ddd_trials"]) == 2
    assert ddd_data["ddd_value"] == ddd_value
    # For a deterministic rule
    assert ddd_data["ddd_trials"][0] == ddd_data["ddd_trials"][1]
Пример #5
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
Пример #6
0
def execute_with_cdr(
    circuit: QPROGRAM,
    executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
    observable: Optional[Observable] = None,
    *,
    simulator: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
    num_training_circuits: int = 10,
    fraction_non_clifford: float = 0.1,
    fit_function: Callable[..., float] = linear_fit_function,
    num_fit_parameters: Optional[int] = None,
    scale_factors: Sequence[float] = (1, ),
    scale_noise: Callable[[QPROGRAM, float], QPROGRAM] = fold_gates_at_random,
    **kwargs: Any,
) -> float:
    """Function for the calculation of an observable from some circuit of
    interest to be mitigated with CDR (or vnCDR) based on
    Ref. :cite:`Czarnik_2021_Quantum` and Ref. :cite:`Lowe_2021_PRR`.

    The circuit of interest must be compiled in the native basis of the IBM
    quantum computers, that is {Rz, sqrt(X), CNOT}, or such that all the
    non-Clifford gates are contained in the Rz rotations.

    The observable/s to be calculated should be input as an array or a list of
    arrays representing the diagonal of the observables to be measured. Note
    these observables MUST be diagonal in z-basis measurements corresponding to
    the circuit of interest.

    Returns mitigated observables list of raw observables (at noise scale
    factors).

    This function returns the mitigated observable/s.

    Args:
        circuit: Quantum program to execute with error mitigation.
        executor: Executes a circuit and returns a `QuantumResult`.
        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.
        simulator: Executes a circuit without noise and returns a
            `QuantumResult`. For CDR to be efficient, the simulator must
            be able to efficiently simulate near-Clifford circuits.
        num_training_circuits: Number of training circuits to be used in the
            mitigation.
        fraction_non_clifford: The fraction of non-Clifford gates to be
            substituted in the training circuits.
        fit_function: The function to map noisy to exact data. Takes array of
            noisy and data and parameters returning a float. See
            ``cdr.linear_fit_function`` for an example.
        num_fit_parameters: The number of parameters the fit_function takes.
        scale_noise: scale_noise: Function for scaling the noise of a quantum
            circuit.
        scale_factors: Factors by which to scale the noise.
            - When 1.0 is the only scale factor, the method is known as CDR.
            - Note: When scale factors larger than 1.0 are provided, the method
            is known as "variable-noise CDR."
        kwargs: Available keyword arguments are:
            - method_select (string): Specifies the method used to select the
            non-Clifford gates to replace when constructing the
            near-Clifford training circuits. Can be 'uniform' or
            'gaussian'.
            - method_replace (string): Specifies the method used to replace
            the selected non-Clifford gates with a Clifford when
            constructing the near-Clifford training circuits. Can be
            'uniform', 'gaussian', or 'closest'.
            - sigma_select (float): Width of the Gaussian distribution used for
            ``method_select='gaussian'``.
            - sigma_replace (float): Width of the Gaussian distribution used
            for ``method_replace='gaussian'``.
            - random_state (int): Seed for sampling.
    """
    # Handle keyword arguments for generating training circuits.

    method_select = kwargs.get("method_select", "uniform")
    method_replace = kwargs.get("method_replace", "closest")
    random_state = kwargs.get("random_state", None)
    kwargs_for_training_set_generation = {
        "sigma_select": kwargs.get("sigma_select"),
        "sigma_replace": kwargs.get("sigma_replace"),
    }

    if num_fit_parameters is None:
        if fit_function is linear_fit_function:
            num_fit_parameters = 1 + len(scale_factors)
        elif fit_function is linear_fit_function_no_intercept:
            num_fit_parameters = len(scale_factors)
        else:
            raise ValueError(
                "Must provide `num_fit_parameters` for custom fit function.")

    # cast executor and simulator inputs to Executor type
    if not isinstance(executor, Executor):
        executor = Executor(executor)

    if not isinstance(simulator, Executor):
        simulator = Executor(simulator)

    # Check if circuit is already Clifford
    if is_clifford(circuit):
        return simulator.evaluate(circuit, observable)[0].real

    # Generate training circuits.
    training_circuits = generate_training_circuits(
        circuit,
        num_training_circuits,
        fraction_non_clifford,
        method_select,
        method_replace,
        random_state,
        kwargs=kwargs_for_training_set_generation,
    )

    # [Optionally] Scale noise in circuits.
    all_circuits = [
        [scale_noise(c, s) for s in scale_factors]
        for c in [circuit] + training_circuits  # type: ignore
    ]

    to_run = [circuit for circuits in all_circuits for circuit in circuits]
    all_circuits_shape = (len(all_circuits), len(all_circuits[0]))

    results = executor.evaluate(to_run, observable)
    noisy_results = np.array(results).reshape(all_circuits_shape)

    results = simulator.evaluate(training_circuits, observable)
    ideal_results = np.array(results)

    # Do the regression.
    fitted_params, _ = curve_fit(
        lambda x, *params: fit_function(x, params),
        noisy_results[1:, :].T,
        ideal_results,
        p0=np.zeros(num_fit_parameters),
    )
    return fit_function(noisy_results[0, :], fitted_params)
Пример #7
0
def execute_with_ddd(
    circuit: QPROGRAM,
    executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
    observable: Optional[Observable] = None,
    *,
    rule: Callable[[int], QPROGRAM],
    rule_args: Dict[str, Any] = {},
    num_trials: int = 1,
    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 digital dynamical decoupling (DDD).

    Args:
        circuit: The input circuit to execute with DDD.
        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.
        rule: A function that takes as main argument a slack length (i.e. the
            number of idle moments) of a slack window (i.e. a single-qubit idle
            window in a circuit) and returns the DDD sequence of gates to be
            applied in that window. Mitiq provides standard built-in rules
            that can be directly imported from ``mitiq.ddd.rules``.
        rule_args: An optional dictionary of keyword arguments for ``rule``.
        num_trials: The number of independent experiments to average over.
            A number larger than 1 can be useful to average over multiple
            applications of a rule returning non-deterministic DDD sequences.
        full_output: If ``False`` only the mitigated expectation value is
            returned. If ``True`` a dictionary containing all DDD data is
            returned too.

    Returns:
        The tuple ``(ddd_value, ddd_data)`` where ``ddd_value`` is the
        expectation value estimated with DDD and ``ddd_data`` is a dictionary
        containing all the raw data involved in the DDD process (e.g. the
        circuit filled with DDD sequences). If ``full_output`` is false,
        only ``ddd_value`` is returned.
    """
    # Initialize executor
    if not isinstance(executor, Executor):
        executor = Executor(executor)

    rule_partial: Callable[[int], QPROGRAM]
    rule_partial = partial(rule, **rule_args)

    # Insert DDD sequences in (a copy of) the input circuit
    circuits_with_ddd = [
        insert_ddd_sequences(circuit, rule_partial) for _ in range(num_trials)
    ]
    results = executor.evaluate(
        circuits_with_ddd,
        observable,
        force_run_all=True,
    )

    assert len(results) == num_trials

    ddd_value = np.real_if_close(np.sum(results)) / num_trials
    if not full_output:
        return ddd_value

    ddd_data = {
        "ddd_value": ddd_value,
        "ddd_trials": results,
        "circuits_with_ddd": circuits_with_ddd,
    }
    return ddd_value, ddd_data
Пример #8
0
def mitigate_executor(
    executor: Callable[[QPROGRAM], QuantumResult],
    observable: Optional[Observable] = None,
    *,
    rule: Callable[[int], QPROGRAM],
    rule_args: Dict[str, Any] = {},
    num_trials: int = 1,
    full_output: bool = False,
) -> Callable[[QPROGRAM], Union[float, Tuple[float, Dict[str, Any]]]]:
    """Returns a modified version of the input 'executor' which is
    error-mitigated with digital dynamical decoupling (DDD).

    Args:
        executor: A function 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.
        rule: A function that takes as main argument a slack length (i.e. the
            number of idle moments) of a slack window (i.e. a single-qubit idle
            window in a circuit) and returns the DDD sequence of gates to be
            applied in that window. Mitiq provides standard built-in rules
            that can be directly imported from `mitiq.ddd.rules`.
        rule_args: An optional dictionary of keyword arguments for `rule`.
        num_trials: The number of independent experiments to average over.
            A number larger than 1 can be useful to average over multiple
            applications of a rule returning non-deterministic DDD sequences.
        full_output: If False only the mitigated expectation value is returned.
            If True a dictionary containing all DDD data is returned too.

    Returns:
        The error-mitigated version of the input executor.
    """
    executor_obj = Executor(executor)
    if not executor_obj.can_batch:

        @wraps(executor)
        def new_executor(
            circuit: QPROGRAM, ) -> Union[float, Tuple[float, Dict[str, Any]]]:
            return execute_with_ddd(
                circuit,
                executor,
                observable,
                rule=rule,
                rule_args=rule_args,
                num_trials=num_trials,
                full_output=full_output,
            )

    else:

        @wraps(executor)
        def new_executor(
            circuits: List[QPROGRAM],
        ) -> List[Union[float, Tuple[float, Dict[str, Any]]]]:
            return [
                execute_with_ddd(
                    circuit,
                    executor,
                    observable,
                    rule=rule,
                    rule_args=rule_args,
                    num_trials=num_trials,
                    full_output=full_output,
                ) for circuit in circuits
            ]

    return new_executor