Skip to content
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

Update T1 experiment #6487

Merged
merged 13 commits into from
Mar 13, 2024
20 changes: 14 additions & 6 deletions cirq-core/cirq/experiments/t1_decay_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Optional, TYPE_CHECKING
from typing import Any, Optional, Sequence, TYPE_CHECKING, cast

import warnings
import pandas as pd
Expand Down Expand Up @@ -77,7 +77,12 @@ def t1_decay(

var = sympy.Symbol('delay_ns')

sweep = study.Linspace(var, start=min_delay_nanos, stop=max_delay_nanos, length=num_points)
if min_delay_nanos == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the original parameter to make it clear that this happens when no min_delay is supplied

if min_delay is None

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to keep it as is in case the user specifies 0 min delay.

min_delay_nanos = 0.4
sweep_vals_ns = np.unique(
np.round(np.logspace(np.log10(min_delay_nanos), np.log10(max_delay_nanos), num_points))
)
sweep = study.Points(var, cast(Sequence[float], sweep_vals_ns))

circuit = circuits.Circuit(
ops.X(qubit), ops.wait(qubit, nanos=var), ops.measure(qubit, key='output')
Expand Down Expand Up @@ -118,8 +123,8 @@ def data(self) -> pd.DataFrame:
def constant(self) -> float:
"""The t1 decay constant."""

def exp_decay(x, t1):
return np.exp(-x / t1)
def exp_decay(x, t1, a, b):
return a * np.exp(-x / t1) + b

xs = self._data['delay_ns']
ts = self._data['true_count']
Expand All @@ -132,7 +137,8 @@ def exp_decay(x, t1):

# Fit to exponential decay to find the t1 constant
try:
popt, _ = optimize.curve_fit(exp_decay, xs, probs, p0=[t1_guess])
popt, _ = optimize.curve_fit(exp_decay, xs, probs, p0=[t1_guess, 1.0, 0.0])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can assign directly

self.popt, _ = optimize.curve_fit(exp_decay, xs, probs, p0=[t1_guess, 1.0, 0.0])

self.popt = popt
t1 = popt[0]
return t1
except RuntimeError:
Expand Down Expand Up @@ -166,7 +172,9 @@ def plot(
ax.plot(xs, ts / (fs + ts), 'ro-', **plot_kwargs)

if include_fit and not np.isnan(self.constant):
ax.plot(xs, np.exp(-xs / self.constant), label='curve fit')
t1 = self.constant
t1, a, b = self.popt
ax.plot(xs, a * np.exp(-xs / t1) + b, label='curve fit')
plt.legend()

ax.set_xlabel(r"Delay between initialization and measurement (nanoseconds)")
Expand Down
40 changes: 14 additions & 26 deletions cirq-core/cirq/experiments/t1_decay_experiment_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ def noisy_moment(self, moment, system_qubits):
repetitions=10,
max_delay=cirq.Duration(nanos=500),
)
results.plot()
results.plot(include_fit=True)


def test_result_eq():
eq = cirq.testing.EqualsTester()
eq.make_equality_group(
lambda: cirq.experiments.T1DecayResult(
data=pd.DataFrame(
columns=['delay_ns', 'false_count', 'true_count'], index=[0], data=[[100.0, 2, 8]]
columns=['delay_ns', 'false_count', 'true_count'], index=[0], data=[[100, 2, 8]]
)
)
)
Expand Down Expand Up @@ -103,7 +103,7 @@ def noisy_moment(self, moment, system_qubits):
data=pd.DataFrame(
columns=['delay_ns', 'false_count', 'true_count'],
index=range(4),
data=[[100.0, 0, 10], [400.0, 0, 10], [700.0, 10, 0], [1000.0, 10, 0]],
data=[[100.0, 0, 10], [215.0, 0, 10], [464.0, 0, 10], [1000.0, 10, 0]],
)
)

Expand All @@ -117,13 +117,14 @@ def test_all_on_results():
min_delay=cirq.Duration(nanos=100),
max_delay=cirq.Duration(micros=1),
)
assert results == cirq.experiments.T1DecayResult(
desired = cirq.experiments.T1DecayResult(
data=pd.DataFrame(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you change this line to

desired = ...
assert results == desired, f'{results.data=} {desired.data=}'

so that we get a more informative error message.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this, and it just outputs

>       assert results == desired, f'{results.data=} {desired.data=}'
E       AssertionError: results.data=   delay_ns  false_count  true_count
E         0       100           10           0
E         1       215           10           0
E         2       464           10           0
E         3      1000           10           0 desired.data=   delay_ns  false_count  true_count
E         0       100           10           0
E         1       215           10           0
E         2       464           10           0
E         3      1000           10           0

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it may have something to do with data types since I changed line 83 in t1_decay_experiment.py to np.logspace(np.log10(min_delay_nanos), np.log10(max_delay_nanos), num_points, dtype=int).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like it's indeed a types issue specifically for the delay_ns column

>>> results.data.dtypes
delay_ns       float64
false_count      int64
true_count       int64
dtype: object
>>> desired.data.dtypes
delay_ns       int64
false_count    int64
true_count     int64
dtype: object

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's pretty strange. When I run it on linux, delay_ns is int64, and in line 83 of t1_decay_experiment.py, I specifically ask for dtype=int. What do you recommend? Should I manually convert delay_ns to integers in the test? It seems like there is some deeper issue when running on windows.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the result is computed by sampler.sample which is probably changing the dtype to float internally. I suggest that you ensure that the desired result is also float.

Why do you want to force delay_ns to be int?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it back to float

columns=['delay_ns', 'false_count', 'true_count'],
index=range(4),
data=[[100.0, 0, 10], [400.0, 0, 10], [700.0, 0, 10], [1000.0, 0, 10]],
data=[[100.0, 0, 10], [215.0, 0, 10], [464.0, 0, 10], [1000.0, 0, 10]],
)
)
assert results == desired, f'{results.data=} {desired.data=}'


def test_all_off_results():
Expand All @@ -135,13 +136,14 @@ def test_all_off_results():
min_delay=cirq.Duration(nanos=100),
max_delay=cirq.Duration(micros=1),
)
assert results == cirq.experiments.T1DecayResult(
desired = cirq.experiments.T1DecayResult(
data=pd.DataFrame(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

columns=['delay_ns', 'false_count', 'true_count'],
index=range(4),
data=[[100.0, 10, 0], [400.0, 10, 0], [700.0, 10, 0], [1000.0, 10, 0]],
data=[[100.0, 10, 0], [215.0, 10, 0], [464.0, 10, 0], [1000.0, 10, 0]],
)
)
assert results == desired, f'{results.data=} {desired.data=}'


@pytest.mark.usefixtures('closefigures')
Expand All @@ -150,28 +152,14 @@ def test_curve_fit_plot_works():
data=pd.DataFrame(
columns=['delay_ns', 'false_count', 'true_count'],
index=range(4),
data=[[100.0, 6, 4], [400.0, 10, 0], [700.0, 10, 0], [1000.0, 10, 0]],
data=[[100.0, 6, 4], [215.0, 10, 0], [464.0, 10, 0], [1000.0, 10, 0]],
)
)

good_fit.plot(include_fit=True)


@pytest.mark.usefixtures('closefigures')
def test_curve_fit_plot_warning():
bad_fit = cirq.experiments.T1DecayResult(
data=pd.DataFrame(
columns=['delay_ns', 'false_count', 'true_count'],
index=range(4),
data=[[100.0, 10, 0], [400.0, 10, 0], [700.0, 10, 0], [1000.0, 10, 0]],
)
)

with pytest.warns(RuntimeWarning, match='Optimal parameters could not be found for curve fit'):
bad_fit.plot(include_fit=True)


@pytest.mark.parametrize('t1', [200, 500, 700])
@pytest.mark.parametrize('t1', [200.0, 500.0, 700.0])
def test_noise_model_continous(t1):
class GradualDecay(cirq.NoiseModel):
def __init__(self, t1: float):
Expand All @@ -196,10 +184,10 @@ def noisy_moment(self, moment, system_qubits):
results = cirq.experiments.t1_decay(
sampler=cirq.DensityMatrixSimulator(noise=GradualDecay(t1)),
qubit=cirq.GridQubit(0, 0),
num_points=4,
num_points=10,
repetitions=10,
min_delay=cirq.Duration(nanos=100),
max_delay=cirq.Duration(micros=1),
min_delay=cirq.Duration(nanos=1),
max_delay=cirq.Duration(micros=10),
)

assert np.isclose(results.constant, t1, 50)
Expand Down
Loading