-
-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Noise Scaling for LRE #2347
Noise Scaling for LRE #2347
Changes from 19 commits
1b61317
63297af
11c5261
7b3e2fe
105a787
f6885cc
34da8ef
47c9e00
7f0fbf9
ed02c60
c7edf46
a631380
45feb84
94b46d2
609f86d
14c0f7e
fa3c320
22fd4e6
71a6430
599b3a5
11920be
bfe07bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# 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. | ||
|
||
"""Methods for scaling noise in circuits by layers and using multivariate extrapolation.""" | ||
|
||
from mitiq.lre.multivariate_scaling.layerwise_folding import multivariate_layer_scaling |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,209 @@ | ||||||||||||||||||
# 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 layerwise folding of input circuits to allow for multivariate | ||||||||||||||||||
extrapolation as defined in :cite:`Russo_2024_LRE`. | ||||||||||||||||||
""" | ||||||||||||||||||
|
||||||||||||||||||
import itertools | ||||||||||||||||||
from copy import deepcopy | ||||||||||||||||||
from typing import Any, Callable, List, Optional, Tuple | ||||||||||||||||||
|
||||||||||||||||||
import numpy as np | ||||||||||||||||||
from cirq import Circuit | ||||||||||||||||||
|
||||||||||||||||||
from mitiq import QPROGRAM | ||||||||||||||||||
from mitiq.utils import _append_measurements, _pop_measurements | ||||||||||||||||||
from mitiq.zne.scaling import fold_gates_at_random | ||||||||||||||||||
from mitiq.zne.scaling.folding import _check_foldable | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
def _get_num_layers_without_measurements(input_circuit: Circuit) -> int: | ||||||||||||||||||
"""Checks if the circuit has non-terminal measurements and returns the | ||||||||||||||||||
number of layers in the input circuit without the terminal measurements. | ||||||||||||||||||
|
||||||||||||||||||
Args: | ||||||||||||||||||
input_circuit: Circuit of interest | ||||||||||||||||||
|
||||||||||||||||||
Returns: | ||||||||||||||||||
num_layers: the number of layers in the input circuit without the | ||||||||||||||||||
terminal measurements | ||||||||||||||||||
|
||||||||||||||||||
""" | ||||||||||||||||||
|
||||||||||||||||||
_check_foldable(input_circuit) | ||||||||||||||||||
circuit = deepcopy(input_circuit) | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
_pop_measurements(circuit) | ||||||||||||||||||
return len(circuit) | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
def _get_chunks( | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
input_circuit: Circuit, num_chunks: Optional[int] = None | ||||||||||||||||||
) -> List[Circuit]: | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Side note: Are we still supporting versions of Python that wouldn't allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good question. Which versions of python did you have in mind? I know there's an open issue to drop 3.9 once we add support for 3.12. The validate workflow only runs for python 3.11 which is used for type checking by mypy. mitiq/.github/workflows/build.yml Lines 25 to 32 in 837ea83
From what I could find, 3.9 uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL that type aliases |
||||||||||||||||||
"""Splits a circuit into approximately equal chunks. | ||||||||||||||||||
|
||||||||||||||||||
Adapted from: | ||||||||||||||||||
https://stackoverflow.com/questions/2130016/splitting-a-list-into-n-parts-of-approximately-equal-length | ||||||||||||||||||
|
||||||||||||||||||
Args: | ||||||||||||||||||
input_circuit: Circuit of interest | ||||||||||||||||||
num_chunks: Number of desired approximately equal chunks | ||||||||||||||||||
* when num_chunks == num_layers, the original circuit is | ||||||||||||||||||
returned | ||||||||||||||||||
* when num_chunks == 1, the entire circuit is chunked into 1 | ||||||||||||||||||
layer | ||||||||||||||||||
Returns: | ||||||||||||||||||
cosenal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
split_circuit: Circuit of interest split into approximately equal | ||||||||||||||||||
chunks | ||||||||||||||||||
|
||||||||||||||||||
Raises: | ||||||||||||||||||
ValueError: | ||||||||||||||||||
When the number of chunks for the input circuit is larger than | ||||||||||||||||||
the number of layers in the input circuit. | ||||||||||||||||||
|
||||||||||||||||||
ValueError: | ||||||||||||||||||
When the number of chunks is less than 1. | ||||||||||||||||||
|
||||||||||||||||||
""" | ||||||||||||||||||
num_layers = _get_num_layers_without_measurements(input_circuit) | ||||||||||||||||||
if num_chunks is None: | ||||||||||||||||||
num_chunks = num_layers | ||||||||||||||||||
cosenal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
||||||||||||||||||
if num_chunks < 1: | ||||||||||||||||||
raise ValueError( | ||||||||||||||||||
"Number of chunks should be greater than or equal to 1." | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
if num_chunks > num_layers: | ||||||||||||||||||
raise ValueError( | ||||||||||||||||||
f"Number of chunks {num_chunks} cannot be greater than the number" | ||||||||||||||||||
f" of layers {num_layers}." | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
k, m = divmod(num_layers, num_chunks) | ||||||||||||||||||
return [ | ||||||||||||||||||
input_circuit[i * k + min(i, m) : (i + 1) * k + min(i + 1, m)] | ||||||||||||||||||
for i in range(num_chunks) | ||||||||||||||||||
] | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
def _get_scale_factor_vectors( | ||||||||||||||||||
input_circuit: Circuit, | ||||||||||||||||||
degree: int, | ||||||||||||||||||
fold_multiplier: int, | ||||||||||||||||||
num_chunks: Optional[int] = None, | ||||||||||||||||||
) -> List[Tuple[Any, ...]]: | ||||||||||||||||||
"""Returns the patterned scale factor vectors required for multivariate | ||||||||||||||||||
extrapolation. | ||||||||||||||||||
|
||||||||||||||||||
Args: | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
input_circuit: Circuit to be scaled | ||||||||||||||||||
degree: Degree of the multivariate polynomial | ||||||||||||||||||
num_chunks: Number of desired approximately equal chunks | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
fold_multiplier: Scaling gap required by unitary folding | ||||||||||||||||||
|
||||||||||||||||||
Returns: | ||||||||||||||||||
scale_factor_vectors: Multiple variations of scale factors for each | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
layer in the input circuit | ||||||||||||||||||
""" | ||||||||||||||||||
|
||||||||||||||||||
circuit_chunks = _get_chunks(input_circuit, num_chunks) | ||||||||||||||||||
num_layers = len(circuit_chunks) | ||||||||||||||||||
|
||||||||||||||||||
# Find the exponents of all the monomial terms required for the folding | ||||||||||||||||||
# pattern. | ||||||||||||||||||
pattern_full = [] | ||||||||||||||||||
for i in range(degree + 1): | ||||||||||||||||||
for j in itertools.combinations_with_replacement(range(num_layers), i): | ||||||||||||||||||
pattern = np.zeros(num_layers, dtype=int) | ||||||||||||||||||
# Get the monomial terms in graded lexicographic order. | ||||||||||||||||||
for index in j: | ||||||||||||||||||
pattern[index] += 1 | ||||||||||||||||||
# Use the fold multiplier on the folding pattern to determine which | ||||||||||||||||||
# layers will be scaled. | ||||||||||||||||||
pattern_full.append(tuple(fold_multiplier * pattern)) | ||||||||||||||||||
|
||||||||||||||||||
# Get the scale factor vectors. | ||||||||||||||||||
# The layers are scaled as 2n+1 due to unitary folding. | ||||||||||||||||||
return [ | ||||||||||||||||||
tuple(2 * num_folds + 1 for num_folds in pattern) | ||||||||||||||||||
for pattern in pattern_full | ||||||||||||||||||
] | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
def multivariate_layer_scaling( | ||||||||||||||||||
input_circuit: Circuit, | ||||||||||||||||||
degree: int, | ||||||||||||||||||
fold_multiplier: int, | ||||||||||||||||||
num_chunks: Optional[int] = None, | ||||||||||||||||||
folding_method: Callable[ | ||||||||||||||||||
[QPROGRAM, float], QPROGRAM | ||||||||||||||||||
] = fold_gates_at_random, | ||||||||||||||||||
) -> List[Circuit]: | ||||||||||||||||||
r""" | ||||||||||||||||||
Defines the noise scaling function required for Layerwise Richardson | ||||||||||||||||||
Extrapolation as defined in :cite:`Russo_2024_LRE`. | ||||||||||||||||||
|
||||||||||||||||||
Note that this method only works for the multivariate extrapolation | ||||||||||||||||||
methods. It does not allows a user to choose which layers in the input | ||||||||||||||||||
circuit will be scaled. | ||||||||||||||||||
|
||||||||||||||||||
.. seealso:: | ||||||||||||||||||
|
||||||||||||||||||
If you would prefer to choose the layers for unitary | ||||||||||||||||||
folding, use :func:`mitiq.zne.scaling.layer_scaling.get_layer_folding` | ||||||||||||||||||
instead. | ||||||||||||||||||
|
||||||||||||||||||
Args: | ||||||||||||||||||
input_circuit: Circuit to be scaled | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
degree: Degree of the multivariate polynomial | ||||||||||||||||||
fold_multiplier: Scaling gap required by unitary folding | ||||||||||||||||||
num_chunks: Number of desired approximately equal chunks. When the | ||||||||||||||||||
number of chunks is the same as the layers in the input circuit, | ||||||||||||||||||
the input circuit is unchanged. | ||||||||||||||||||
folding_method: Unitary folding method. Default is | ||||||||||||||||||
:func:`fold_gates_at_random` | ||||||||||||||||||
|
||||||||||||||||||
Returns: | ||||||||||||||||||
Multiple folded variations of the input circuit | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
||||||||||||||||||
Raises: | ||||||||||||||||||
ValueError: | ||||||||||||||||||
When the degree for the multinomial is not greater than or | ||||||||||||||||||
equal to 1; when the fold multiplier to scale the circuit is | ||||||||||||||||||
greater than/equal to 1; when the number of chunks for a | ||||||||||||||||||
large circuit is 0 or when the number of chunks in a circuit is | ||||||||||||||||||
greater than the number of layers in the input circuit. | ||||||||||||||||||
|
||||||||||||||||||
""" | ||||||||||||||||||
if degree < 1: | ||||||||||||||||||
raise ValueError( | ||||||||||||||||||
"Multinomial degree must be greater than or equal to 1." | ||||||||||||||||||
) | ||||||||||||||||||
if fold_multiplier < 1: | ||||||||||||||||||
raise ValueError("Fold multiplier must be greater than or equal to 1.") | ||||||||||||||||||
circuit_copy = deepcopy(input_circuit) | ||||||||||||||||||
terminal_measurements = _pop_measurements(circuit_copy) | ||||||||||||||||||
|
||||||||||||||||||
scaling_pattern = _get_scale_factor_vectors( | ||||||||||||||||||
circuit_copy, degree, fold_multiplier, num_chunks | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
chunks = _get_chunks(circuit_copy, num_chunks) | ||||||||||||||||||
|
||||||||||||||||||
multiple_folded_circuits = [] | ||||||||||||||||||
for scale_factor_vector in scaling_pattern: | ||||||||||||||||||
folded_circuit = Circuit() | ||||||||||||||||||
for chunk, scale_factor in zip(chunks, scale_factor_vector): | ||||||||||||||||||
if scale_factor == 1: | ||||||||||||||||||
folded_circuit += chunk | ||||||||||||||||||
else: | ||||||||||||||||||
chunks_circ = Circuit(chunk) | ||||||||||||||||||
folded_chunk_circ = folding_method(chunks_circ, scale_factor) | ||||||||||||||||||
folded_circuit += folded_chunk_circ | ||||||||||||||||||
_append_measurements(folded_circuit, terminal_measurements) | ||||||||||||||||||
multiple_folded_circuits.append((folded_circuit)) | ||||||||||||||||||
purva-thakre marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
||||||||||||||||||
return multiple_folded_circuits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can raise an UnfoldableCircuitError, it should be documented in the docstring of this caller function as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had included it previously. #2347 (comment)
We should agree on style guidelines for this project and update the docs style guidelines section on docstrings.
Couple of things that confused me:
UnfoldableCircuitError
once as a defined exception. Since_check_foldable
is a private function, the error raised by the function does not show up publicly.As we rely on google-style docstrings, there is no convention to allow lists in the description of the different sections of a docstring. Portions of our API-doc have run-on sentences because someone used a list in the section description which appear as badly formatted sentences publicly.