Skip to content

Commit

Permalink
Merge branch 'main' into ci-update-actions-to-node20
Browse files Browse the repository at this point in the history
  • Loading branch information
pavoljuhas committed Feb 12, 2024
2 parents 527d395 + ef0137c commit 58b9e3b
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 118 deletions.
64 changes: 33 additions & 31 deletions cirq-core/cirq/devices/noise_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
# limitations under the License.

from typing import TYPE_CHECKING, Any, Dict, Tuple, Type, Union
import numpy as np

from cirq import ops, protocols, value
from cirq._compat import proper_repr
from cirq import ops, protocols, value, qis
from cirq._compat import proper_repr, deprecated

if TYPE_CHECKING:
import cirq
Expand Down Expand Up @@ -97,8 +96,10 @@ def _from_json_dict_(cls, gate_type, qubits, **kwargs) -> 'OpIdentifier':
return cls(gate_type, *qubits)


# TODO: expose all from top-level cirq?
def decay_constant_to_xeb_fidelity(decay_constant: float, num_qubits: int = 2) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.decay_constant_to_xeb_fidelity')
def decay_constant_to_xeb_fidelity(
decay_constant: float, num_qubits: int = 2
) -> float: # pragma: no cover
"""Calculates the XEB fidelity from the depolarization decay constant.
Args:
Expand All @@ -108,11 +109,13 @@ def decay_constant_to_xeb_fidelity(decay_constant: float, num_qubits: int = 2) -
Returns:
Calculated XEB fidelity.
"""
N = 2**num_qubits
return 1 - ((1 - decay_constant) * (1 - 1 / N))
return qis.decay_constant_to_xeb_fidelity(decay_constant, num_qubits)


def decay_constant_to_pauli_error(decay_constant: float, num_qubits: int = 1) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.decay_constant_to_pauli_error')
def decay_constant_to_pauli_error(
decay_constant: float, num_qubits: int = 1
) -> float: # pragma: no cover
"""Calculates pauli error from the depolarization decay constant.
Args:
Expand All @@ -122,11 +125,13 @@ def decay_constant_to_pauli_error(decay_constant: float, num_qubits: int = 1) ->
Returns:
Calculated Pauli error.
"""
N = 2**num_qubits
return (1 - decay_constant) * (1 - 1 / N / N)
return qis.decay_constant_to_pauli_error(decay_constant, num_qubits)


def pauli_error_to_decay_constant(pauli_error: float, num_qubits: int = 1) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.pauli_error_to_decay_constant')
def pauli_error_to_decay_constant(
pauli_error: float, num_qubits: int = 1
) -> float: # pragma: no cover
"""Calculates depolarization decay constant from pauli error.
Args:
Expand All @@ -136,11 +141,13 @@ def pauli_error_to_decay_constant(pauli_error: float, num_qubits: int = 1) -> fl
Returns:
Calculated depolarization decay constant.
"""
N = 2**num_qubits
return 1 - (pauli_error / (1 - 1 / N / N))
return qis.pauli_error_to_decay_constant(pauli_error, num_qubits)


def xeb_fidelity_to_decay_constant(xeb_fidelity: float, num_qubits: int = 2) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.xeb_fidelity_to_decay_constant')
def xeb_fidelity_to_decay_constant(
xeb_fidelity: float, num_qubits: int = 2
) -> float: # pragma: no cover
"""Calculates the depolarization decay constant from XEB fidelity.
Args:
Expand All @@ -150,11 +157,11 @@ def xeb_fidelity_to_decay_constant(xeb_fidelity: float, num_qubits: int = 2) ->
Returns:
Calculated depolarization decay constant.
"""
N = 2**num_qubits
return 1 - (1 - xeb_fidelity) / (1 - 1 / N)
return qis.xeb_fidelity_to_decay_constant(xeb_fidelity, num_qubits)


def pauli_error_from_t1(t_ns: float, t1_ns: float) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.pauli_error_from_t1')
def pauli_error_from_t1(t_ns: float, t1_ns: float) -> float: # pragma: no cover
"""Calculates the pauli error from T1 decay constant.
This computes error for a specific duration, `t`.
Expand All @@ -166,11 +173,11 @@ def pauli_error_from_t1(t_ns: float, t1_ns: float) -> float:
Returns:
Calculated Pauli error resulting from T1 decay.
"""
t2 = 2 * t1_ns
return (1 - np.exp(-t_ns / t2)) / 2 + (1 - np.exp(-t_ns / t1_ns)) / 4
return qis.pauli_error_from_t1(t_ns, t1_ns)


def average_error(decay_constant: float, num_qubits: int = 1) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.average_error')
def average_error(decay_constant: float, num_qubits: int = 1) -> float: # pragma: no cover
"""Calculates the average error from the depolarization decay constant.
Args:
Expand All @@ -180,11 +187,13 @@ def average_error(decay_constant: float, num_qubits: int = 1) -> float:
Returns:
Calculated average error.
"""
N = 2**num_qubits
return (1 - decay_constant) * (1 - 1 / N)
return qis.average_error(decay_constant, num_qubits)


def decoherence_pauli_error(t1_ns: float, tphi_ns: float, gate_time_ns: float) -> float:
@deprecated(deadline='v2.0', fix='use cirq.qis.decoherence_pauli_error')
def decoherence_pauli_error(
t1_ns: float, tphi_ns: float, gate_time_ns: float
) -> float: # pragma: no cover
"""The component of Pauli error caused by decoherence on a single qubit.
Args:
Expand All @@ -195,11 +204,4 @@ def decoherence_pauli_error(t1_ns: float, tphi_ns: float, gate_time_ns: float) -
Returns:
Calculated Pauli error resulting from decoherence.
"""
gamma_2 = (1 / (2 * t1_ns)) + 1 / tphi_ns

exp1 = np.exp(-gate_time_ns / t1_ns)
exp2 = np.exp(-gate_time_ns * gamma_2)
px = 0.25 * (1 - exp1)
py = px
pz = 0.5 * (1 - exp2) - px
return px + py + pz
return qis.decoherence_pauli_error(t1_ns, tphi_ns, gate_time_ns)
85 changes: 1 addition & 84 deletions cirq-core/cirq/devices/noise_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import pytest

import cirq
from cirq.devices.noise_utils import (
OpIdentifier,
decay_constant_to_xeb_fidelity,
decay_constant_to_pauli_error,
pauli_error_to_decay_constant,
xeb_fidelity_to_decay_constant,
pauli_error_from_t1,
average_error,
decoherence_pauli_error,
)
from cirq.devices.noise_utils import OpIdentifier


def test_op_identifier():
Expand Down Expand Up @@ -67,74 +55,3 @@ def test_op_id_instance():
gate = cirq.SingleQubitCliffordGate.from_xz_map((cirq.X, False), (cirq.Z, False))
op_id = OpIdentifier(gate, q0)
cirq.testing.assert_equivalent_repr(op_id)


@pytest.mark.parametrize(
'decay_constant,num_qubits,expected_output',
[(0.01, 1, 1 - (0.99 * 1 / 2)), (0.05, 2, 1 - (0.95 * 3 / 4))],
)
def test_decay_constant_to_xeb_fidelity(decay_constant, num_qubits, expected_output):
val = decay_constant_to_xeb_fidelity(decay_constant, num_qubits)
assert val == expected_output


@pytest.mark.parametrize(
'decay_constant,num_qubits,expected_output',
[(0.01, 1, 0.99 * 3 / 4), (0.05, 2, 0.95 * 15 / 16)],
)
def test_decay_constant_to_pauli_error(decay_constant, num_qubits, expected_output):
val = decay_constant_to_pauli_error(decay_constant, num_qubits)
assert val == expected_output


@pytest.mark.parametrize(
'pauli_error,num_qubits,expected_output',
[(0.01, 1, 1 - (0.01 / (3 / 4))), (0.05, 2, 1 - (0.05 / (15 / 16)))],
)
def test_pauli_error_to_decay_constant(pauli_error, num_qubits, expected_output):
val = pauli_error_to_decay_constant(pauli_error, num_qubits)
assert val == expected_output


@pytest.mark.parametrize(
'xeb_fidelity,num_qubits,expected_output',
[(0.01, 1, 1 - 0.99 / (1 / 2)), (0.05, 2, 1 - 0.95 / (3 / 4))],
)
def test_xeb_fidelity_to_decay_constant(xeb_fidelity, num_qubits, expected_output):
val = xeb_fidelity_to_decay_constant(xeb_fidelity, num_qubits)
assert val == expected_output


@pytest.mark.parametrize(
't,t1_ns,expected_output',
[
(20, 1e5, (1 - np.exp(-20 / 2e5)) / 2 + (1 - np.exp(-20 / 1e5)) / 4),
(4000, 1e4, (1 - np.exp(-4000 / 2e4)) / 2 + (1 - np.exp(-4000 / 1e4)) / 4),
],
)
def test_pauli_error_from_t1(t, t1_ns, expected_output):
val = pauli_error_from_t1(t, t1_ns)
assert val == expected_output


@pytest.mark.parametrize(
'decay_constant,num_qubits,expected_output', [(0.01, 1, 0.99 * 1 / 2), (0.05, 2, 0.95 * 3 / 4)]
)
def test_average_error(decay_constant, num_qubits, expected_output):
val = average_error(decay_constant, num_qubits)
assert val == expected_output


@pytest.mark.parametrize(
'T1_ns,Tphi_ns,gate_time_ns', [(1e4, 2e4, 25), (1e5, 2e3, 25), (1e4, 2e4, 4000)]
)
def test_decoherence_pauli_error(T1_ns, Tphi_ns, gate_time_ns):
val = decoherence_pauli_error(T1_ns, Tphi_ns, gate_time_ns)
# Expected value is of the form:
#
# (1/4) * [1 - e^(-t/T1)] + (1/2) * [1 - e^(-t/(2*T1) - t/Tphi]
#
expected_output = 0.25 * (1 - np.exp(-gate_time_ns / T1_ns)) + 0.5 * (
1 - np.exp(-gate_time_ns * ((1 / (2 * T1_ns)) + 1 / Tphi_ns))
)
assert val == expected_output
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from functools import cached_property
from typing import Dict, TYPE_CHECKING, List, Set, Type

from cirq import ops, devices
from cirq import ops, devices, qis
from cirq.devices import noise_utils

if TYPE_CHECKING:
Expand Down Expand Up @@ -129,7 +129,7 @@ def expected_gates(cls) -> Set[Type[ops.Gate]]:
def _get_pauli_error(self, p_error: float, op_id: noise_utils.OpIdentifier):
time_ns = float(self.gate_times_ns[op_id.gate_type])
for q in op_id.qubits:
p_error -= noise_utils.decoherence_pauli_error(self.t1_ns[q], self.tphi_ns[q], time_ns)
p_error -= qis.decoherence_pauli_error(self.t1_ns[q], self.tphi_ns[q], time_ns)
return p_error

@cached_property
Expand Down
10 changes: 10 additions & 0 deletions cirq-core/cirq/qis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,13 @@
validate_qid_shape,
validate_normalized_state_vector,
)

from cirq.qis.noise_utils import (
decay_constant_to_xeb_fidelity,
decay_constant_to_pauli_error,
pauli_error_to_decay_constant,
xeb_fidelity_to_decay_constant,
pauli_error_from_t1,
average_error,
decoherence_pauli_error,
)
123 changes: 123 additions & 0 deletions cirq-core/cirq/qis/noise_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright 2021 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np


# TODO: expose all from top-level cirq?
def decay_constant_to_xeb_fidelity(decay_constant: float, num_qubits: int = 2) -> float:
"""Calculates the XEB fidelity from the depolarization decay constant.
Args:
decay_constant: Depolarization decay constant.
num_qubits: Number of qubits.
Returns:
Calculated XEB fidelity.
"""
N = 2**num_qubits
return 1 - ((1 - decay_constant) * (1 - 1 / N))


def decay_constant_to_pauli_error(decay_constant: float, num_qubits: int = 1) -> float:
"""Calculates pauli error from the depolarization decay constant.
Args:
decay_constant: Depolarization decay constant.
num_qubits: Number of qubits.
Returns:
Calculated Pauli error.
"""
N = 2**num_qubits
return (1 - decay_constant) * (1 - 1 / N / N)


def pauli_error_to_decay_constant(pauli_error: float, num_qubits: int = 1) -> float:
"""Calculates depolarization decay constant from pauli error.
Args:
pauli_error: The pauli error.
num_qubits: Number of qubits.
Returns:
Calculated depolarization decay constant.
"""
N = 2**num_qubits
return 1 - (pauli_error / (1 - 1 / N / N))


def xeb_fidelity_to_decay_constant(xeb_fidelity: float, num_qubits: int = 2) -> float:
"""Calculates the depolarization decay constant from XEB fidelity.
Args:
xeb_fidelity: The XEB fidelity.
num_qubits: Number of qubits.
Returns:
Calculated depolarization decay constant.
"""
N = 2**num_qubits
return 1 - (1 - xeb_fidelity) / (1 - 1 / N)


def pauli_error_from_t1(t_ns: float, t1_ns: float) -> float:
"""Calculates the pauli error from T1 decay constant.
This computes error for a specific duration, `t`.
Args:
t_ns: The duration of the gate in ns.
t1_ns: The T1 decay constant in ns.
Returns:
Calculated Pauli error resulting from T1 decay.
"""
t2 = 2 * t1_ns
return (1 - np.exp(-t_ns / t2)) / 2 + (1 - np.exp(-t_ns / t1_ns)) / 4


def average_error(decay_constant: float, num_qubits: int = 1) -> float:
"""Calculates the average error from the depolarization decay constant.
Args:
decay_constant: Depolarization decay constant.
num_qubits: Number of qubits.
Returns:
Calculated average error.
"""
N = 2**num_qubits
return (1 - decay_constant) * (1 - 1 / N)


def decoherence_pauli_error(t1_ns: float, tphi_ns: float, gate_time_ns: float) -> float:
"""The component of Pauli error caused by decoherence on a single qubit.
Args:
t1_ns: T1 time in nanoseconds.
tphi_ns: Tphi time in nanoseconds.
gate_time_ns: Duration in nanoseconds of the gate affected by this error.
Returns:
Calculated Pauli error resulting from decoherence.
"""
gamma_2 = (1 / (2 * t1_ns)) + 1 / tphi_ns

exp1 = np.exp(-gate_time_ns / t1_ns)
exp2 = np.exp(-gate_time_ns * gamma_2)
px = 0.25 * (1 - exp1)
py = px
pz = 0.5 * (1 - exp2) - px
return px + py + pz
Loading

0 comments on commit 58b9e3b

Please sign in to comment.