Source code for mitiq.ddd.ddd

# Copyright (C) 2022 Unitary Fund
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""High-level digital dynamical decoupling (DDD) tools."""

from typing import (
    Optional,
    Callable,
    Union,
    Tuple,
    Dict,
    Any,
    List,
)

from functools import wraps, partial
import numpy as np

from mitiq import Executor, Observable, QPROGRAM, QuantumResult

from mitiq.ddd.insertion import insert_ddd_sequences


[docs]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
[docs]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
[docs]def ddd_decorator( observable: Optional[Observable] = None, *, rule: Callable[[int], QPROGRAM], rule_args: Dict[str, Any] = {}, num_trials: int = 1, full_output: bool = False, ) -> Callable[ [Callable[[QPROGRAM], QuantumResult]], Callable[[QPROGRAM], Union[float, Tuple[float, Dict[str, Any]]]], ]: """Decorator which adds an error-mitigation layer based on digital dynamical decoupling (DDD) to an executor function, i.e., a function which executes a quantum circuit with an arbitrary backend and returns a ``QuantumResult`` (e.g. an expectation value). Args: 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-mitigating decorator to be applied to an executor function. """ def decorator( executor: Callable[[QPROGRAM], QuantumResult] ) -> Callable[[QPROGRAM], Union[float, Tuple[float, Dict[str, Any]]]]: return mitigate_executor( executor, observable, rule=rule, rule_args=rule_args, num_trials=num_trials, full_output=full_output, ) return decorator