From 620e83a53edbb496c3f4a973fbd28203a61a0a21 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Thu, 25 Sep 2025 12:14:55 -0700 Subject: [PATCH 1/5] Micro-optimizations to WaitGate - Noticed some inefficiencies in the creation of WaitGate where it is doing unnecessary object boxing. - This removes (slow) comparisons between Duration objects. This improves creation of WaitGate objects (e.g. from cirq.wait(nanos=10)) from about 27 micros to about 16 micros. While not a huge effect, this could have measurable savings for certain T1 and similar experiments that involve creation of lots of wait gates. --- cirq-core/cirq/ops/wait_gate.py | 7 +++++-- cirq-core/cirq/value/duration.py | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/ops/wait_gate.py b/cirq-core/cirq/ops/wait_gate.py index e94419ae475..e928605a991 100644 --- a/cirq-core/cirq/ops/wait_gate.py +++ b/cirq-core/cirq/ops/wait_gate.py @@ -51,8 +51,11 @@ def __init__( ValueError: If the `qid_shape` provided is empty or `num_qubits` contradicts `qid_shape`. """ - self._duration = value.Duration(duration) - if not protocols.is_parameterized(self.duration) and self.duration < 0: + self._duration = ( + duration if isinstance(duration, value.Duration) else value.Duration(duration) + ) + if not self.duration._is_parameterized_() and self.duration.total_picos() < 0: + # if not protocols.is_parameterized(self.duration) and self.duration.total_picos() < 0: raise ValueError('duration < 0') if qid_shape is None: if num_qubits is None: diff --git a/cirq-core/cirq/value/duration.py b/cirq-core/cirq/value/duration.py index ef5a966604b..73398a71411 100644 --- a/cirq-core/cirq/value/duration.py +++ b/cirq-core/cirq/value/duration.py @@ -84,19 +84,19 @@ def __init__( """ self._time_vals: list[_NUMERIC_INPUT_TYPE] = [0, 0, 0, 0] self._multipliers = [1, 1000, 1000_000, 1000_000_000] - if value is not None and value != 0: + if value is not None: if isinstance(value, datetime.timedelta): # timedelta has microsecond resolution. self._time_vals[2] = int(value / datetime.timedelta(microseconds=1)) elif isinstance(value, Duration): self._time_vals = value._time_vals - else: + elif value != 0: raise TypeError(f'Not a `cirq.DURATION_LIKE`: {repr(value)}.') input_vals = [picos, nanos, micros, millis] self._time_vals = _add_time_vals(self._time_vals, input_vals) def _is_parameterized_(self) -> bool: - return protocols.is_parameterized(self._time_vals) + return any(isinstance(val, sympy.Expr) for val in self._time_vals) def _parameter_names_(self) -> AbstractSet[str]: return protocols.parameter_names(self._time_vals) From e5327b843c7b70ebd1b22fc1fb20f7da2fc632b9 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Thu, 25 Sep 2025 12:46:45 -0700 Subject: [PATCH 2/5] How embarassing! My commented code is showing! --- cirq-core/cirq/ops/wait_gate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cirq-core/cirq/ops/wait_gate.py b/cirq-core/cirq/ops/wait_gate.py index e928605a991..9b8ae2b2284 100644 --- a/cirq-core/cirq/ops/wait_gate.py +++ b/cirq-core/cirq/ops/wait_gate.py @@ -55,7 +55,6 @@ def __init__( duration if isinstance(duration, value.Duration) else value.Duration(duration) ) if not self.duration._is_parameterized_() and self.duration.total_picos() < 0: - # if not protocols.is_parameterized(self.duration) and self.duration.total_picos() < 0: raise ValueError('duration < 0') if qid_shape is None: if num_qubits is None: From fffc36a04ee06290df70effdf8c513f4af85fe8c Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Thu, 25 Sep 2025 15:35:05 -0700 Subject: [PATCH 3/5] sympy is basic --- cirq-core/cirq/value/duration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/value/duration.py b/cirq-core/cirq/value/duration.py index 73398a71411..1c328d39c1d 100644 --- a/cirq-core/cirq/value/duration.py +++ b/cirq-core/cirq/value/duration.py @@ -96,7 +96,7 @@ def __init__( self._time_vals = _add_time_vals(self._time_vals, input_vals) def _is_parameterized_(self) -> bool: - return any(isinstance(val, sympy.Expr) for val in self._time_vals) + return any(isinstance(val, sympy.Basic) for val in self._time_vals) def _parameter_names_(self) -> AbstractSet[str]: return protocols.parameter_names(self._time_vals) From e76824646aecca3031ddff89df324bba9212dd2b Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Fri, 26 Sep 2025 06:08:16 -0700 Subject: [PATCH 4/5] not more is parameterized. --- cirq-core/cirq/ops/wait_gate.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/wait_gate.py b/cirq-core/cirq/ops/wait_gate.py index 9b8ae2b2284..e5c25c4e468 100644 --- a/cirq-core/cirq/ops/wait_gate.py +++ b/cirq-core/cirq/ops/wait_gate.py @@ -16,6 +16,8 @@ from typing import AbstractSet, Any, TYPE_CHECKING +import sympy + from cirq import protocols, value from cirq.ops import raw_types @@ -54,7 +56,8 @@ def __init__( self._duration = ( duration if isinstance(duration, value.Duration) else value.Duration(duration) ) - if not self.duration._is_parameterized_() and self.duration.total_picos() < 0: + total_picos = self.duration.total_picos() + if not isinstance(total_picos, sympy.Basic) and self.duration.total_picos() < 0: raise ValueError('duration < 0') if qid_shape is None: if num_qubits is None: From ee34a74800abffc6eba86f57c4e2d16261615f73 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Fri, 26 Sep 2025 06:17:00 -0700 Subject: [PATCH 5/5] Dont call twice. --- cirq-core/cirq/ops/wait_gate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/wait_gate.py b/cirq-core/cirq/ops/wait_gate.py index e5c25c4e468..fd5d860102d 100644 --- a/cirq-core/cirq/ops/wait_gate.py +++ b/cirq-core/cirq/ops/wait_gate.py @@ -57,7 +57,7 @@ def __init__( duration if isinstance(duration, value.Duration) else value.Duration(duration) ) total_picos = self.duration.total_picos() - if not isinstance(total_picos, sympy.Basic) and self.duration.total_picos() < 0: + if not isinstance(total_picos, sympy.Basic) and total_picos < 0: raise ValueError('duration < 0') if qid_shape is None: if num_qubits is None: