From 5eb43479966ecced6c491f78ecc973efb83adb95 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:28:21 +0000 Subject: [PATCH 1/3] Initial plan From 67e74f2430b8667a96b3c3d9a102ee52950af3e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:57:34 +0000 Subject: [PATCH 2/3] Initial plan: fix lint, tests, docstring discrepancy Co-authored-by: blackboxprogramming <118287761+blackboxprogramming@users.noreply.github.com> --- .coverage | Bin 0 -> 53248 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .coverage diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..91a66f6532fd197ed1250bc42f5277e5b4cdc2a2 GIT binary patch literal 53248 zcmeI)O>Y}T7zglOJB@3{VS}p53Q?5dP^DIF2dhZ5^Z)@OQ4xt2N`*M!j_q;0)VrJP zOI{8LTq+g51D~RN0lopJO5EUtda6K(Q;W*;?Ckm_jjOhzN>lw;wzJ-ynSJIrZ+4vc z)6462s6@x}9T|$V#++fA#=AlohLNZHEZx&BM>|vL6Z&b6>`&Uw8@25pX05A6ar$$^ z`f~O)t6JPB{x$Ps@m&7189P_W*XRT`2tWV=5O`?>`j?Bj()_%62uB*LDoFKVcsCGkrfC^FehRZ3$=_Gqu_^P9P z<+fCyqd0B1%Z2iH-xPAC6DQ1DDz20CTXdTls|_6(R^?)c3TkV%AqeDdXsWa${Tt~WuLfh4rt3^ zeaD(JCFd5^xgyuziqxR-D`J?l5?90ZDuOFPJ>b;Lbm{yF)1Y3dc7v@Ry>x2GDD>jz z4Ql*GogRueUp<~HEiIY1w&IS$JdLIu27{gaSf?2rp3rsp{N#Saqh!33@F?o_gx86O z4CfZQai}+sj+vDdL;5_>gqI|wNDsBQaj+DFX!gA3cwdWqOrU6h&|Huv>qKsghf2V8 zXnEVpmm8{Dmz*BNwyMt!hb=+95*N(&3-jeOjVlGaLophrcPqu6Q(dK1_ixADb>+I5{bB4xv%!$LX7MnKH>z( zQ+rBPj+4{yzT^dgP0K1TTfy7uQL)^W0nb9gvS@luWBP)4y#gEF8Yy06T% z(Bv%7SZVpSO)}w99og2yof~DO#GS5sq&U4AeIP>>+Kv*OBn~F-SUWNhEnm^eWG#&Y zBeCxOsB|$O(wTG))wL-vXCK#hxQQEee&lz_$N8mPf62_1&YUs(Q8I${)_sF+*dPD_ z2tWV=5P$##AOHafKmY;|c=-gfX2#6%`~QsfyJ6j<2W$|400bZa0SG_<0uX=z1Rwwb z2pma)VkWz2rGFOk#+;d5m{0x&;H_HyZ0*eg)hc7%HLSbVKS#2H2pR$qfB*y_009U< z00Izz00bZafq_6VyJ)7r2FMh$3;E=?0Q&F$ZyVO_K~n5M00Izz00bZa0SG_<0uX=z z1R!we0_*0K(O+EYdX8H0BiB{_%8ut>U)hSJ8%9o*zQ?ay?aAiE1C79TqMi&rpT1;) zKmRwapA5QTg8&2|009U<00Izz00bZa0SG|g#SvJa>M!R1ulN7>{=XNeVG#)gAOHaf zKmY;|fB*y_009U<;5i6n&8#u2KmUJVSP!1V6;LDuAOHafKmY;|fB*y_009U<00M_D zkj>8K_2>V04C~IJhl2nh009U<00Izz00bZa0SG_<0uXo_fwDQ3&tJ_L#uu+#GEQZU zd#@Xp3O^UV&7Av_zU2ScUoI3MJpAMD-~K)SFrz>Jzi(LgpXLHc3jqi~00Izz00bZa z0SG_<0uX?}5fPZ8UlPQh{~L2hBw~aK0SG_<0uX=z1Rwwb2tWV=5P-ll3GnCtc>n)Q zbjSn&2tWV=5P$##AOHafKmY;|I5GnK|NnUZe`J~qfkFTR5P$##AOHafKmY;|fB*!Z GN#H+RAAHXM literal 0 HcmV?d00001 From 5e7b36c2506797062a82b32a132732505c3a80c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 09:00:14 +0000 Subject: [PATCH 3/3] Fix lint, most_likely() tie-breaking, add 8 tests (36 total, 99% coverage) Co-authored-by: blackboxprogramming <118287761+blackboxprogramming@users.noreply.github.com> --- .coverage | Bin 53248 -> 53248 bytes quantum_simulator.py | 5 +- tests/test_quantum_simulator.py | 87 +++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/.coverage b/.coverage index 91a66f6532fd197ed1250bc42f5277e5b4cdc2a2..875e4eee3a7b561e0eba434152a12dbf27e6a295 100644 GIT binary patch delta 43 zcmV+`0M!40paX!Q1F+(c9!mxQ+(Q7M6o3@~1@PzV0RXU{cli7N-RHmibN&UQkU*K6 B6(s-w delta 43 zcmV+`0M!40paX!Q1F+(c9wh?+?jZnB3cw100yyyX007v}I}H5)=JVhEIsXEpkU)ZJ B6UqPp diff --git a/quantum_simulator.py b/quantum_simulator.py index 25ef6a0..cb62afd 100644 --- a/quantum_simulator.py +++ b/quantum_simulator.py @@ -25,7 +25,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Dict, Iterable, List, Mapping, Optional, Sequence +from typing import Dict, Mapping, Optional, Sequence import numpy as np @@ -53,7 +53,8 @@ def most_likely(self) -> str: returning the lexicographically smallest string. """ - return max(self.counts.items(), key=lambda item: (item[1], item[0]))[0] + best_count = max(self.counts.values()) + return min(k for k, v in self.counts.items() if v == best_count) def total_shots(self) -> int: """Return the total number of measurement shots.""" diff --git a/tests/test_quantum_simulator.py b/tests/test_quantum_simulator.py index de7661e..9631593 100644 --- a/tests/test_quantum_simulator.py +++ b/tests/test_quantum_simulator.py @@ -133,10 +133,10 @@ def test_pauli_z_then_hadamard(self): assert probs["1"] == pytest.approx(1.0, abs=1e-8) def test_identity_gate(self): - I = np.eye(2, dtype=np.complex128) + identity = np.eye(2, dtype=np.complex128) circuit = QuantumCircuit(1) circuit.hadamard(0) - circuit.apply_custom(I, [0]) + circuit.apply_custom(identity, [0]) probs = circuit.probabilities() assert probs["0"] == pytest.approx(0.5, abs=1e-8) @@ -247,3 +247,86 @@ def test_partial_probabilities_normalized(self): circuit.hadamard(1) probs = circuit.probabilities(qubits=[0]) assert sum(probs.values()) == pytest.approx(1.0) + + +# ── Additional coverage ──────────────────────────────────────────────────── + +class TestEmptyQubits: + def test_probabilities_empty_list_returns_full_distribution(self): + """probabilities(qubits=[]) takes the early-return path and matches full dist.""" + circuit = QuantumCircuit(2) + circuit.hadamard(0) + probs_empty = circuit.probabilities(qubits=[]) + probs_full = circuit.probabilities() + for key in probs_full: + assert probs_empty[key] == pytest.approx(probs_full[key], abs=1e-8) + + def test_measure_empty_qubits_collapses_to_single_basis_state(self): + """measure(qubits=[]) collapses the full state; _collapse_state empty path.""" + rng = np.random.default_rng(seed=0) + circuit = QuantumCircuit(2) + circuit.hadamard(0) + circuit.cnot(0, 1) + result = circuit.measure(qubits=[], shots=1, rng=rng) + assert result.total_shots() == 1 + probs = circuit.probabilities() + nonzero = [v for v in probs.values() if v > 1e-9] + assert len(nonzero) == 1 + assert nonzero[0] == pytest.approx(1.0, abs=1e-8) + + +class TestNonSquareUnitary: + def test_non_square_matrix_raises(self): + """A non-square matrix must raise ValueError (covers the ndim/shape check).""" + circuit = QuantumCircuit(1) + with pytest.raises(ValueError, match="square"): + circuit.apply_custom(np.zeros((2, 3), dtype=complex), [0]) + + +class TestMostLikelyTieBreaking: + def test_tie_broken_by_lexicographically_smallest(self): + """Ties in counts must resolve to the lexicographically smallest string.""" + result = MeasurementResult(counts={"01": 5, "10": 5}) + assert result.most_likely() == "01" + + def test_tie_three_way(self): + result = MeasurementResult(counts={"11": 3, "00": 3, "01": 3}) + assert result.most_likely() == "00" + + +class TestAdditionalCircuits: + def test_pauli_y_gate(self): + """Pauli-Y: Y|0⟩ = i|1⟩, so probability of measuring |1⟩ is 1.""" + Y = np.array([[0, -1j], [1j, 0]], dtype=np.complex128) + circuit = QuantumCircuit(1) + circuit.apply_custom(Y, [0]) + probs = circuit.probabilities() + assert probs["0"] == pytest.approx(0.0, abs=1e-8) + assert probs["1"] == pytest.approx(1.0, abs=1e-8) + + def test_five_qubit_ghz_state(self): + """5-qubit GHZ state: equal superposition of |00000⟩ and |11111⟩.""" + circuit = QuantumCircuit(5) + circuit.hadamard(0) + for i in range(1, 5): + circuit.cnot(0, i) + probs = circuit.probabilities() + assert probs["00000"] == pytest.approx(0.5, abs=1e-8) + assert probs["11111"] == pytest.approx(0.5, abs=1e-8) + for key, val in probs.items(): + if key not in ("00000", "11111"): + assert val == pytest.approx(0.0, abs=1e-8) + + def test_bell_state_qubit1_collapses_qubit0(self): + """Measuring qubit 0 of a Bell state collapses qubit 1 to the same value.""" + circuit = QuantumCircuit(2) + circuit.hadamard(0) + circuit.cnot(0, 1) + rng = np.random.default_rng(seed=999) + result = circuit.measure(qubits=[0], shots=1, rng=rng) + outcome = result.most_likely() + probs = circuit.probabilities() + if outcome == "0": + assert probs["00"] == pytest.approx(1.0, abs=1e-8) + else: + assert probs["11"] == pytest.approx(1.0, abs=1e-8)