Skip to content

Commit

Permalink
refactor(noise_amplifier): stop using Circuit and DAG amplifier classes
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrorrivero committed Dec 21, 2023
1 parent c9dddef commit 0d7ab69
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 66 deletions.
28 changes: 24 additions & 4 deletions docs/reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,20 @@ EstimatorResult: {
```

### Custom ZNE strategies
This prototype has been devised specifically to allow users to create their custom noise amplification and extrapolation techniques. This can be easily done through custom implementations of the `CircuitNoiseAmplifier` and `Extrapolator` abstract classes respectively:
This prototype has been devised specifically to allow users to create their custom noise amplification and extrapolation techniques. This can be easily done through custom implementations of the `NoiseAmplifier` and `Extrapolator` abstract classes respectively:
```python
from zne.extrapolation import Extrapolator, ReckoningResult
from zne.noise_amplification import CircuitNoiseAmplifier
from zne.noise_amplification import NoiseAmplifier


############################ NOISE AMPLIFIER ############################
class CustomAmplifier(CircuitNoiseAmplifier):
class CustomAmplifier(NoiseAmplifier):
def amplify_circuit_noise(self, circuit, noise_factor):
return circuit.copy() # Dummy, nonperforming

def amplify_dag_noise(self, dag, noise_factor):
return super().amplify_dag_noise(dag, noise_factor)


############################ EXTRAPOLATOR ############################
class CustomExtrapolator(Extrapolator):
Expand All @@ -143,7 +146,7 @@ class CustomExtrapolator(Extrapolator):
return ReckoningResult(value, std_error, metadata) # Dummy, nonperforming
```
where we only need to implement a number of (performing) methods with the appropriate [function signature](https://stackoverflow.com/a/72789014/12942875):
- __[CircuitNoiseAmplifier]__ Amplify circuit noise:
- __[NoiseAmplifier]__ Amplify circuit noise:
```python
@abstractmethod
def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) -> QuantumCircuit:
Expand All @@ -157,6 +160,19 @@ where we only need to implement a number of (performing) methods with the approp
The noise amplified circuit
"""
```
- __[NoiseAmplifier]__ Amplify circuit noise:
```python
def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit:
"""Noise amplification strategy over :class:`~qiskit.dagcircuit.DAGCircuit`.
Args:
dag: The original dag circuit.
noise_factor: The noise amplification factor by which to amplify the circuit noise.
Returns:
The noise amplified dag circuit
"""
```
- __[Extrapolator]__ Minimum data points necessary for extrapolation:
```python
@property
Expand Down Expand Up @@ -189,6 +205,10 @@ where we only need to implement a number of (performing) methods with the approp
"""
```

If only one of `NoiseAmplifier.amplify_circuit_noise` or `NoiseAmplifier.amplify_dag_noise`
is implemented, delegating the implementation of the other to the parent class
(i.e. `super()`) will default to executing the first with the appropriate type conversion.

Finally, we simply pass instances of these to the constructor through the `ZNEStrategy` object:
```python
zne_strategy = ZNEStrategy(
Expand Down
7 changes: 5 additions & 2 deletions docs/tutorials/1-zne.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,16 @@
"outputs": [],
"source": [
"from zne.extrapolation import Extrapolator, ReckoningResult\n",
"from zne.noise_amplification import CircuitNoiseAmplifier\n",
"from zne.noise_amplification import NoiseAmplifier\n",
"\n",
"\n",
"############################ NOISE AMPLIFIER ############################\n",
"class CustomAmplifier(CircuitNoiseAmplifier):\n",
"class CustomAmplifier(NoiseAmplifier):\n",
" def amplify_circuit_noise(self, circuit, noise_factor):\n",
" return circuit.copy() # Dummy, nonperforming\n",
" \n",
" def amplify_dag_noise(self, dag, noise_factor):\n",
" return super().amplify_dag_noise(dag, noise_factor)\n",
"\n",
"\n",
"############################ EXTRAPOLATOR ############################\n",
Expand Down
7 changes: 5 additions & 2 deletions docs/tutorials/2-noise_amplification.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,16 @@
"outputs": [],
"source": [
"from zne.extrapolation import Extrapolator, ReckoningResult\n",
"from zne.noise_amplification import CircuitNoiseAmplifier\n",
"from zne.noise_amplification import NoiseAmplifier\n",
"\n",
"\n",
"############################ NOISE AMPLIFIER ############################\n",
"class CustomAmplifier(CircuitNoiseAmplifier):\n",
"class CustomAmplifier(NoiseAmplifier):\n",
" def amplify_circuit_noise(self, circuit, noise_factor):\n",
" return circuit.copy() # Dummy, nonperforming\n",
" \n",
" def amplify_dag_noise(self, dag, noise_factor):\n",
" return super().amplify_dag_noise(dag, noise_factor)\n",
"\n",
"\n",
"############################ EXTRAPOLATOR ############################\n",
Expand Down
7 changes: 5 additions & 2 deletions docs/tutorials/teaser.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,16 @@
"outputs": [],
"source": [
"from zne.extrapolation import Extrapolator, ReckoningResult\n",
"from zne.noise_amplification import CircuitNoiseAmplifier\n",
"from zne.noise_amplification import NoiseAmplifier\n",
"\n",
"\n",
"############################ NOISE AMPLIFIER ############################\n",
"class CustomAmplifier(CircuitNoiseAmplifier):\n",
"class CustomAmplifier(NoiseAmplifier):\n",
" def amplify_circuit_noise(self, circuit, noise_factor):\n",
" return circuit.copy() # Dummy, nonperforming\n",
" \n",
" def amplify_dag_noise(self, dag, noise_factor):\n",
" return super().amplify_dag_noise(dag, noise_factor)\n",
"\n",
"\n",
"############################ EXTRAPOLATOR ############################\n",
Expand Down
41 changes: 0 additions & 41 deletions test/noise_amplification/test_noise_amplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from qiskit.transpiler import TransformationPass

from zne import NOISE_AMPLIFIER_LIBRARY
from zne.noise_amplification import CircuitNoiseAmplifier, DAGNoiseAmplifier


@mark.parametrize(
Expand Down Expand Up @@ -75,43 +74,3 @@ def test_build_pass_manager(self, NoiseAmplifier, noise_factor):
_ = NoiseAmplifier().build_pass_manager(noise_factor)
pass_builder.assert_called_once_with(noise_factor)
pass_manager_class.assert_called_once_with(TRANSPILER_PASS)

@mark.parametrize("noise_factor", [1, 1.2, 2.4, -3.14])
def test_amplify_dag_noise(self, NoiseAmplifier, noise_factor):
if issubclass(NoiseAmplifier, CircuitNoiseAmplifier):
DAG = f"DAG:{noise_factor}"
CIRCUIT = f"CIRCUIT:{noise_factor}"
AMP_CIRCUIT = f"AMP_CIRCUIT:{noise_factor}"
AMP_DAG = f"AMP_DAG:{noise_factor}"
noise_amplifier = NoiseAmplifier()
noise_amplifier.amplify_circuit_noise = Mock(return_value=AMP_CIRCUIT)
target = "zne.noise_amplification.noise_amplifier"
with patch(target + ".dag_to_circuit", return_value=CIRCUIT) as dag_to_circuit, patch(
target + ".circuit_to_dag",
return_value=AMP_DAG,
) as circuit_to_dag:
assert AMP_DAG == noise_amplifier.amplify_dag_noise(DAG, noise_factor)
dag_to_circuit.assert_called_once_with(DAG)
noise_amplifier.amplify_circuit_noise.assert_called_once_with(CIRCUIT, noise_factor)
circuit_to_dag.assert_called_once_with(AMP_CIRCUIT)

@mark.parametrize("noise_factor", [1, 1.2, 2.4, -3.14])
def test_amplify_circuit_noise(self, NoiseAmplifier, noise_factor):
if issubclass(NoiseAmplifier, DAGNoiseAmplifier):
CIRCUIT = f"CIRCUIT:{noise_factor}"
DAG = f"DAG:{noise_factor}"
AMP_DAG = f"AMP_DAG:{noise_factor}"
AMP_CIRCUIT = f"AMP_CIRCUIT:{noise_factor}"
noise_amplifier = NoiseAmplifier()
noise_amplifier.amplify_dag_noise = Mock(return_value=AMP_DAG)
target = "zne.noise_amplification.noise_amplifier"
with patch(
target + ".dag_to_circuit", return_value=AMP_CIRCUIT
) as dag_to_circuit, patch(
target + ".circuit_to_dag",
return_value=DAG,
) as circuit_to_dag:
assert AMP_CIRCUIT == noise_amplifier.amplify_circuit_noise(CIRCUIT, noise_factor)
circuit_to_dag.assert_called_once_with(CIRCUIT)
noise_amplifier.amplify_dag_noise.assert_called_once_with(DAG, noise_factor)
dag_to_circuit.assert_called_once_with(AMP_DAG)
20 changes: 18 additions & 2 deletions zne/noise_amplification/folding_amplifier/folding_amplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@

from numpy.random import Generator, default_rng
from qiskit import QuantumCircuit
from qiskit.dagcircuit import DAGCircuit

from ...utils.typing import isreal
from ..noise_amplifier import CircuitNoiseAmplifier
from ..noise_amplifier import NoiseAmplifier


class FoldingAmplifier(CircuitNoiseAmplifier):
class FoldingAmplifier(NoiseAmplifier):
"""Interface for folding amplifier strategies."""

def __init__( # pylint: disable=super-init-not-called, duplicate-code, too-many-arguments
Expand Down Expand Up @@ -52,6 +53,9 @@ def __init__( # pylint: disable=super-init-not-called, duplicate-code, too-many
self._prepare_rng(random_seed)
self._set_noise_factor_relative_tolerance(noise_factor_relative_tolerance)

################################################################################
## PROPERTIES
################################################################################
@property
def options(self) -> dict:
"""Strategy options."""
Expand Down Expand Up @@ -108,6 +112,9 @@ def _set_noise_factor_relative_tolerance(self, tolerance: float) -> None:
raise TypeError("Noise factor relative tolerance must be real valued.")
self._noise_factor_relative_tolerance: float = tolerance

################################################################################
## FOLDING METHODS
################################################################################
@staticmethod
def folding_to_noise_factor(folding: float) -> float:
"""Converts number of foldings to noise factor.
Expand Down Expand Up @@ -171,3 +178,12 @@ def _compute_folding_nums(self, noise_factor: float, num_instructions: int) -> t
)
num_full_foldings, num_sub_foldings = divmod(num_foldings, num_instructions)
return num_full_foldings, num_sub_foldings

################################################################################
## IMPLEMENTATION
################################################################################
# pylint: disable=useless-parent-delegation
def amplify_dag_noise(
self, dag: DAGCircuit, noise_factor: float
) -> DAGCircuit: # pragma: no cover
return super().amplify_dag_noise(dag, noise_factor)
34 changes: 21 additions & 13 deletions zne/noise_amplification/noise_amplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ class NoiseAmplifier(ImmutableStrategy, ABC):
"""Interface for noise amplification strategies."""

@abstractmethod
def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) -> QuantumCircuit:
def amplify_circuit_noise(
self, circuit: QuantumCircuit, noise_factor: float
) -> QuantumCircuit: # pragma: no cover
"""Noise amplification strategy over :class:`~qiskit.circuit.QuantumCircuit`.
Args:
Expand All @@ -36,9 +38,14 @@ def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) ->
Returns:
The noise amplified quantum circuit
"""
dag: DAGCircuit = circuit_to_dag(circuit)
dag = self.amplify_dag_noise(dag, noise_factor)
return dag_to_circuit(dag)

@abstractmethod
def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit:
def amplify_dag_noise(
self, dag: DAGCircuit, noise_factor: float
) -> DAGCircuit: # pragma: no cover
"""Noise amplification strategy over :class:`~qiskit.dagcircuit.DAGCircuit`.
Args:
Expand All @@ -48,6 +55,9 @@ def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit:
Returns:
The noise amplified dag circuit
"""
circuit: QuantumCircuit = dag_to_circuit(dag)
circuit = self.amplify_circuit_noise(circuit, noise_factor)
return circuit_to_dag(circuit)

def build_transpiler_pass(self, noise_factor: float) -> TransformationPass:
"""Builds transpiler pass to perform noise amplification as specified in the strategy.
Expand Down Expand Up @@ -90,21 +100,19 @@ def build_pass_manager(self, noise_factor: float) -> PassManager:
return PassManager(transpiler_pass)


class CircuitNoiseAmplifier(NoiseAmplifier):
# TODO: deprecate
class CircuitNoiseAmplifier(NoiseAmplifier): # pragma: no cover
"""Interface for noise amplification strategies over :class:`~qiskit.dagcircuit.DAGCircuit`."""

# pylint: disable=useless-parent-delegation
def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit:
circuit: QuantumCircuit = dag_to_circuit(dag)
circuit = self.amplify_circuit_noise(circuit, noise_factor)
return circuit_to_dag(circuit)
return super().amplify_dag_noise(dag, noise_factor)


class DAGNoiseAmplifier(NoiseAmplifier):
# TODO: deprecate
class DAGNoiseAmplifier(NoiseAmplifier): # pragma: no cover
"""Interface for noise amplification strategies over :class:`~qiskit.dagcircuit.DAGCircuit`."""

def amplify_circuit_noise(
self, circuit: QuantumCircuit, noise_factor: float
) -> QuantumCircuit: # pragma: no cover
dag: DAGCircuit = circuit_to_dag(circuit)
dag = self.amplify_dag_noise(dag, noise_factor)
return dag_to_circuit(dag)
# pylint: disable=useless-parent-delegation
def amplify_circuit_noise(self, circuit: QuantumCircuit, noise_factor: float) -> QuantumCircuit:
return super().amplify_circuit_noise(circuit, noise_factor)

0 comments on commit 0d7ab69

Please sign in to comment.