Source code for mitiq.zne.scaling.layer_scaling

# Copyright (C) Unitary Fund
#
# This source code is licensed under the GPL license (v3) found in the
# LICENSE file in the root directory of this source tree.

"""Functions for layer-wise unitary folding on supported circuits."""

from copy import deepcopy
from typing import Callable, List

import cirq
import numpy as np
from cirq import Moment, inverse

from mitiq import QPROGRAM
from mitiq.interface import accept_qprogram_and_validate
from mitiq.utils import _append_measurements, _pop_measurements
from mitiq.zne.scaling.folding import _check_foldable


[docs] @accept_qprogram_and_validate def layer_folding( circuit: cirq.Circuit, layers_to_fold: List[int] ) -> cirq.Circuit: """Applies a variable amount of folding to select layers of a circuit. Note that this method only works for the univariate extrapolation methods. It allows a user to choose which layers in the input circuit will be scaled. .. seealso:: If you would prefer to use a multivariate extrapolation method for unitary folding, use :func:`mitiq.lre.multivariate_scaling.layerwise_folding` instead. The layerwise folding required for multivariate extrapolation is different as the layers in the input circuit have to be scaled in a specific pattern. The required specific pattern for multivariate extrapolation does not allow a user to provide a choice of which layers to fold. Args: circuit: The input circuit. layers_to_fold: A list with the index referring to the layer number, and the element filled by an integer representing the number of times the layer is folded. Returns: The folded circuit. """ folded = deepcopy(circuit) measurements = _pop_measurements(folded) layers = [] for i, layer in enumerate(folded): layers.append(layer) # Apply the requisite number of folds to each layer. num_fold = layers_to_fold[i] for _ in range(num_fold): # We only fold the layer if it does not contain a measurement. if not cirq.is_measurement(layer): layers.append(Moment(inverse(layer))) layers.append(Moment(layer)) # We combine each layer into a single circuit. combined_circuit = cirq.Circuit() for layer in layers: combined_circuit.append(layer) _append_measurements(combined_circuit, measurements) return combined_circuit
[docs] def get_layer_folding( layer_index: int, ) -> Callable[[QPROGRAM, float], QPROGRAM]: """Return function to perform folding. The function return can be used as an argument to define the noise scaling within the `execute_with_zne` function. Args: layer_index: The layer of the circuit to apply folding to. Returns: The function for folding the ith layer. """ @accept_qprogram_and_validate def fold_ith_layer( circuit: cirq.Circuit, scale_factor: int ) -> cirq.Circuit: """Returns a circuit folded according to integer scale factors. Args: circuit: Circuit to fold. scale_factor: Factor to scale the circuit by. Returns: The folded quantum circuit. """ _check_foldable(circuit) layers = [0] * len(circuit) num_folds = (scale_factor - 1) // 2 if np.isclose(num_folds, int(num_folds)): num_folds = int(num_folds) layers[layer_index] = num_folds folded = layer_folding(circuit, layers_to_fold=layers) return folded return fold_ith_layer