Skip to content

Commit 0edbee6

Browse files
authored
Merge branch 'main' into fix/dd-classical-control
2 parents 1023b7b + aa9a4da commit 0edbee6

File tree

24 files changed

+285
-239
lines changed

24 files changed

+285
-239
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ jobs:
287287
- name: Coverage check
288288
run: check/pytest-and-incremental-coverage -n logical
289289
- name: Upload coverage reports to Codecov
290-
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
290+
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
291291
env:
292292
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
293293
windows:
@@ -419,7 +419,7 @@ jobs:
419419
- name: Check out source repository
420420
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
421421
- name: Set up Node environment
422-
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
422+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
423423
with:
424424
node-version: '22.16.0'
425425
- name: Install node dependencies
@@ -434,7 +434,7 @@ jobs:
434434
- name: Check out source repository
435435
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
436436
- name: Set up Node environment
437-
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
437+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
438438
with:
439439
node-version: '22.16.0'
440440
- name: Install node dependencies
@@ -449,7 +449,7 @@ jobs:
449449
- name: Check out source repository
450450
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
451451
- name: Set up Node environment
452-
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
452+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
453453
with:
454454
node-version: '22.16.0'
455455
- name: Install node dependencies
@@ -466,7 +466,7 @@ jobs:
466466
- name: Check out source repository
467467
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
468468
- name: Set up Node environment
469-
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
469+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
470470
with:
471471
node-version: '22.16.0'
472472
- name: Install node dependencies

.github/workflows/scorecard-scanner.yaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,23 @@ jobs:
6161
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
6262
with:
6363
# Save the results
64-
results_file: scorecard-results.sarif
64+
results_file: raw-scorecard-results.sarif
6565
results_format: sarif
6666
# See https://github.com/ossf/scorecard-action#publishing-results.
6767
publish_results: true
6868

69+
- name: Filter results
70+
uses: advanced-security/filter-sarif@2da736ff05ef065cb2894ac6892e47b5eac2c3c0 # v1.1
71+
with:
72+
# The cifuzz action version cannot be pinned to a hash. Filter out those results to avoid
73+
# repeated warnings that we can't do anything about.
74+
patterns: |
75+
-.github/workflows/cifuzz.yml:actions/unpinned-tag
76+
input: raw-scorecard-results.sarif
77+
output: scorecard-results.sarif
78+
6979
- name: Upload results to code-scanning dashboard
7080
# yamllint disable rule:line-length
71-
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
81+
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
7282
with:
7383
sarif_file: scorecard-results.sarif

cirq-core/cirq/contrib/acquaintance/gates.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,14 @@ def _decompose_(self, qubits: Sequence[cirq.Qid]) -> Iterator[cirq.OP_TREE]:
285285
parts.append(list(qubits[n_qubits : n_qubits + part_len]))
286286
n_qubits += part_len
287287
n_parts = len(parts)
288-
op_sort_key = (
289-
None
290-
if self.acquaintance_size is None
291-
else (lambda op: min(qubit_to_position[q] for q in op.qubits) % self.acquaintance_size)
292-
)
288+
289+
# acquaintance_size for the modulo operation in op_sort_key.
290+
# Produce constant key (no sorting) when None or 0.
291+
mod_acquaintance_size: int = self.acquaintance_size or 1
292+
293+
def op_sort_key(op: cirq.Operation) -> int:
294+
return min(qubit_to_position[q] for q in op.qubits) % mod_acquaintance_size
295+
293296
layers = new_layers()
294297
for layer_num in range(n_parts):
295298
layers = new_layers(prior_interstitial=layers.posterior_interstitial)
@@ -306,11 +309,11 @@ def _decompose_(self, qubits: Sequence[cirq.Qid]) -> Iterator[cirq.OP_TREE]:
306309
parts_qubits = list(left_part + right_part)
307310
parts[i] = parts_qubits[: len(right_part)]
308311
parts[i + 1] = parts_qubits[len(right_part) :]
309-
if op_sort_key is not None:
312+
if self.acquaintance_size is not None:
310313
layers.prior_interstitial.sort(key=op_sort_key)
311314
for l in ('prior_interstitial', 'pre', 'intra', 'post'):
312315
yield getattr(layers, l)
313-
if op_sort_key is not None:
316+
if self.acquaintance_size is not None:
314317
layers.posterior_interstitial.sort(key=op_sort_key)
315318
yield layers.posterior_interstitial
316319

cirq-core/cirq/contrib/quantikz/circuit_to_latex_quantikz.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,8 @@ def _get_gate_name(self, gate: ops.Gate) -> str:
412412
return self._format_exponent_for_display(mapped_base)
413413

414414
if (
415-
hasattr(gate, "exponent")
416-
and not (isinstance(gate.exponent, float) and math.isclose(gate.exponent, 1.0))
415+
(gate_exponent := getattr(gate, "exponent", None)) is not None
416+
and not (isinstance(gate_exponent, float) and math.isclose(gate_exponent, 1.0))
417417
and isinstance(gate, tuple(_EXPONENT_GATE_MAP.keys()))
418418
):
419419
has_exp_in_cand = ("^" in name_cand) or ("**" in name_cand)
@@ -425,7 +425,7 @@ def _get_gate_name(self, gate: ops.Gate) -> str:
425425
if needs_recon:
426426
name_cand = (
427427
f"{recon_base}^"
428-
f"{{{self._format_exponent_for_display(gate.exponent)}}}"
428+
f"{{{self._format_exponent_for_display(gate_exponent)}}}"
429429
)
430430

431431
fmt_name = self._escape_string(name_cand)

cirq-core/cirq/contrib/quimb/state_vector.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import warnings
65
from collections.abc import Sequence
76
from typing import cast, TYPE_CHECKING
87

@@ -15,21 +14,6 @@
1514
import numpy as np
1615

1716

18-
def _get_quimb_version():
19-
"""Returns the quimb version and parsed (major,minor) numbers if possible.
20-
Returns:
21-
a tuple of ((major, minor), version string)
22-
"""
23-
version = quimb.__version__
24-
try:
25-
return tuple(int(x) for x in version.split('.')), version
26-
except: # pragma: no cover
27-
return (0, 0), version
28-
29-
30-
QUIMB_VERSION = _get_quimb_version()
31-
32-
3317
def circuit_to_tensors(
3418
circuit: cirq.Circuit, qubits: Sequence[cirq.Qid] | None = None, initial_state: int | None = 0
3519
) -> tuple[list[qtn.Tensor], dict[cirq.Qid, int], None]:
@@ -163,15 +147,7 @@ def tensor_expectation_value(
163147
for q in qubits
164148
]
165149
tn = qtn.TensorNetwork(tensors + end_bras)
166-
if QUIMB_VERSION[0] < (1, 3):
167-
warnings.warn( # pragma: no cover
168-
f'quimb version {QUIMB_VERSION[1]} detected. Please use '
169-
f'quimb>=1.3 for optimal performance in '
170-
'`tensor_expectation_value`. '
171-
'See https://github.com/quantumlib/Cirq/issues/3263'
172-
)
173-
else:
174-
tn.rank_simplify(inplace=True)
150+
tn.rank_simplify(inplace=True)
175151
path_info = tn.contract(get='path-info')
176152
ram_gb = path_info.largest_intermediate * 128 / 8 / 1024 / 1024 / 1024
177153
if ram_gb > max_ram_gb:

cirq-core/cirq/contrib/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ ply>=3.6
44
pylatex~=1.4
55

66
# quimb
7-
quimb>=1.8
7+
quimb~=1.8
88
opt_einsum

cirq-core/cirq/qis/measures.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,10 @@ def _fidelity_state_vectors_or_density_matrices(state1: np.ndarray, state2: np.n
242242
# state1 is a density matrix and state2 is a state vector
243243
return np.real(np.conjugate(state2) @ state1 @ state2)
244244
elif state1.ndim == 2 and state2.ndim == 2:
245-
# Both density matrices
246-
state1_sqrt = _sqrt_positive_semidefinite_matrix(state1)
247-
eigs = linalg.eigvalsh(state1_sqrt @ state2 @ state1_sqrt)
248-
trace = np.sum(np.sqrt(np.abs(eigs)))
249-
return trace**2
245+
# Both density matrices: use SVD-based fidelity for numerical stability
246+
rho1_sqrt = linalg.sqrtm(state1)
247+
rho2_sqrt = linalg.sqrtm(state2)
248+
return (np.sum(linalg.svdvals(rho1_sqrt @ rho2_sqrt))) ** 2
250249
# matrix is reshaped before this point
251250
raise ValueError( # pragma: no cover
252251
'The given arrays must be one- or two-dimensional. '

cirq-core/cirq/qis/measures_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import pytest
2121

2222
import cirq
23+
import cirq.qis.measures as measures
24+
from cirq import partial_trace
2325

2426
N = 15
2527
VEC1 = cirq.testing.random_superposition(N)
@@ -187,6 +189,30 @@ def test_fidelity_bad_shape() -> None:
187189
_ = cirq.fidelity(np.array([[[1.0]]]), np.array([[[1.0]]]), qid_shape=(1,))
188190

189191

192+
def test_fidelity_numerical_stability_high_dim():
193+
init_qubits = 10
194+
final_qubits = init_qubits - 1
195+
rng = np.random.RandomState(42)
196+
psi = rng.randn(2**init_qubits) + 1j * rng.randn(2**init_qubits)
197+
psi /= np.linalg.norm(psi)
198+
rho = np.outer(psi, np.conjugate(psi))
199+
rho_reshaped = rho.reshape((2,) * (init_qubits * 2))
200+
keep_idxs = list(range(final_qubits))
201+
rho_reduced = partial_trace(rho_reshaped, keep_idxs).reshape((2**final_qubits,) * 2)
202+
203+
# Direct fidelity computation (old)
204+
rho1_sqrt = measures._sqrt_positive_semidefinite_matrix(rho_reduced)
205+
eigs = measures.linalg.eigvalsh(rho1_sqrt @ rho_reduced @ rho1_sqrt)
206+
cirq_fidelity = (np.sum(np.sqrt(np.abs(eigs)))) ** 2
207+
# SVD-based fidelity (patched)
208+
get_fidelity = cirq.fidelity(
209+
rho_reduced, rho_reduced, validate=False, qid_shape=(2,) * final_qubits
210+
)
211+
# Old version should exceed 1, new should be ~1
212+
assert cirq_fidelity > 1 + 1e-6
213+
assert get_fidelity == pytest.approx(1, abs=1e-6)
214+
215+
190216
def test_von_neumann_entropy() -> None:
191217
# 1x1 matrix
192218
assert cirq.von_neumann_entropy(np.array([[1]])) == 0

cirq-core/cirq/qis/states.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ def validate_qid_shape(
906906
907907
Raises:
908908
ValueError: if the size of `state_vector` does not match that given in
909-
`qid_shape`, or if `qid_shape` is not given, if `state_vector` does
909+
`qid_shape`, or if `qid_shape` is not given and `state_vector` does
910910
not have a dimension that is a power of two.
911911
"""
912912
size = state_vector.size

cirq-core/cirq/study/result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def dataframe_from_measurements(measurements: Mapping[str, np.ndarray]) -> pd.Da
144144
converted_dict[key] = np.sum(basis * bitstrings, axis=1)
145145

146146
# Use objects to accommodate more than 64 qubits if needed.
147-
dtype = object if any(bs.shape[1] > 63 for _, bs in measurements.items()) else np.int64
147+
dtype = object if any(bs.shape[1] > 63 for bs in measurements.values()) else np.int64
148148
return pd.DataFrame(converted_dict, dtype=dtype)
149149

150150
@property

0 commit comments

Comments
 (0)