Conversation
- IsUnitary compared floats with exact equality, so any gate built from 1/sqrt(2) was rejected. qc.Custom(hadamard, 0) threw "not unitary" — the first thing anyone tries with Custom(). Now compares with tolerance. - Simulator.Run mutated the caller's gate list via Gates.RemoveAt(0), so a second Run(qc, shots) replayed a circuit stripped of its own gates and reported 100% |0..0>. Now works on a local copy. - The copy constructor documented a deep copy but assigned references, so gates applied to the copy mutated the original. Now clones. - Initialize accepted unnormalized states silently, making GetProbabilities return probabilities summing to >1. Now validates |a|^2 + |b|^2 == 1. - Toffoli rendered its second control as a target marker in circuit diagrams. - Qubit limit was documented as 30, which cannot allocate (2^30 Complex = 17 GB, and the CLR caps one array at 2 GB). Lowered to a real 26. - GetStringResult threw on an all-zero count array. - Renamed ApplayGate -> ApplyGate; dropped two unused usings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
The library could not previously be consumed by anyone: the README told people to clone the repo and copy .cs files, and the net8.0-only target meant Unity — the flagship use case — could not import it at all. - Multi-target netstandard2.1 (Unity 2021.2+), net8.0 and net10.0 from a single package; NuGet picks the right one per consumer. Replaced BitOperations.PopCount, which netstandard2.1 lacks, with a plain power-of-two test. - Full package metadata: id, license, icon, README, tags, XML docs, Source Link and a symbol package. - Added CI, plus a tag-driven workflow that packs and pushes to NuGet. - Console-free rendering: ToDiagram() and QuantumGates.Format() return strings, so the drawer works under Unity, ASP.NET and tests. Draw() and Print() keep their console behaviour and colors by wrapping them. - Moved Examples.Example into the public API as Circuits.BellStates, so the library no longer ships a namespace called Examples. - Documented every public gate matrix and the State enum, clearing 82 undocumented-member warnings now that XML docs are generated. - Spelled out Enumerable.Reverse: under C# 14 first-class spans, array.Reverse() binds to MemoryExtensions.Reverse and returns void. - Added a CHANGELOG. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
…e drawer 84 tests, previously zero. A simulation library that nobody can verify is a library nobody will depend on. - Gate algebra: every matrix is unitary, SX/SY/SZ square to their parent gate, H*H = I, S*S = Z, T^4 = Z, each dagger inverts its gate, Rx(2pi) = -I, and U3 reproduces H at the standard angles. - Circuits: all four Bell states and GHZ match their textbook amplitudes; Toffoli, SWAP and the controlled gates behave on basis states. - Measurement: with a fixed random source, measuring a Bell state yields only "00" or "11" across 50 seeds, collapse leaves the state normalized, and measuring one half of a Bell pair determines the other. - Drawer: golden strings for alignment, plus a check that ToDiagram needs no console and that Draw prints exactly what ToDiagram returns. - One regression test per bug fixed in the previous two commits. The tests live in namespace QubitNet.Tests rather than Qubit.NET.Tests, because the latter puts Qubit.NET.Math in scope where it shadows System.Math. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
Every gate used to allocate a fresh state vector. At 20 qubits that is 16 MB per gate, so a GHZ chain allocated 328 MB to produce a 16 MB result — the kind of GC pressure that makes the library unusable inside a Unity frame loop. - Single-qubit gates apply in place: each amplitude pair is visited once from the member whose target bit is 0, so no scratch vector is needed. - Controlled gates get their own in-place route. CNOT, CY, CZ, CH, the controlled rotations and Toffoli all reduce to a 2x2 operator applied only where the controls are set, extracted from the trailing block of the gate matrix. SWAP, Fredkin and Custom stay on the general path. - Gate application is parallelized above 2^16 amplitudes. - The general multi-qubit path no longer calls Array.IndexOf per amplitude, and skips zero amplitudes and zero coefficients. 20-qubit GHZ: 83 ms and 328 MB -> 34 ms and 16 MB. Also collapsed the 24 copy-pasted gate methods onto two helpers, which cuts a third of QuantumCircuit.cs without touching any signature or doc comment, and made Gate.TargetQubits/ControlQubits non-nullable. Build warnings: 170 -> 0. Added ControlledGateTests, which pins the new fast path against the general matrix path for all eight controlled gates and Toffoli, and a BenchmarkDotNet project so the README's performance numbers are measured rather than claimed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
Adds the one thing Qiskit's ClassicalRegister buys that this library could not express: conditioning a gate on a measurement outcome. Bit layout, one result per Measure call, and QASM's creg mapping were already covered. - MeasureInto(qubit, classicalBit) stores an outcome; ClassicalBit(i) reads it; When(bit, value, body) runs the body's gates only when the bit matches. Plain Measure fills the classical bit matching each qubit it measured. - Conditional gates are always recorded, so Simulator.Run re-evaluates the condition per shot against that shot's own outcomes. - No separate ClassicalRegister type: it would break every call site and tax the common case of "measure everything, get a bitstring" for no gain here. - Simulator.Run now returns MeasurementResult, with Counts, Shots, Probability(outcome) and MostFrequent, instead of an (int[], int) tuple. It also no longer allocates 2^n counters for a 2-qubit partial measurement. - Added QuantumCircuit.Reset(). - The drawer lays a conditional gate out after the measurement that wrote its bit, and connects the two, instead of showing the correction before its cause. Teleportation is the proof: tested across five input states and twenty measurement branches, the message always arrives on the third qubit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
- OpenQASM 2.0 export via circuit.ToQasm(), covering every standard gate, measurement into creg, and conditional gates as `if (c[i]==v)`. Circuits now load straight into Qiskit and run on real hardware. Custom gates raise NotSupportedException rather than exporting something subtly wrong. - Gates record the angles they were constructed from. Recovering them from the matrix afterwards is lossy around wrapping and sign, and storing them deleted more code than it added. - Algorithms: Grover, Deutsch-Jozsa, Bernstein-Vazirani, teleportation, superdense coding, and an in-place QFT extension. - State inspection: BlochVector(qubit) gives Bloch sphere coordinates, sitting at the origin for a maximally entangled qubit; QubitProbability(qubit) gives a single-qubit marginal; ToHistogram() draws an ASCII bar chart. These are what a Unity integration needs to render anything. Grover finds its marked item with probability 1.000, and Bernstein-Vazirani recovers the secret in one query, both verified across every input. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
nuget.org now recommends OIDC-based trusted publishing over long-lived API keys. The workflow exchanges a GitHub OIDC token for a key valid for one hour, so there is no persistent credential to leak, and an intercepted token cannot be replayed. - Added id-token: write for OIDC, and contents: write, which the job no longer inherits once permissions are declared explicitly. - NuGet/login@v1 runs immediately before the push so the hour-long key cannot expire mid-run. - Replaced the NUGET_API_KEY secret with NUGET_USER, holding the nuget.org profile name. - Documented the one-time nuget.org policy setup in the README, including that the workflow file name must be given without its path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
Releases are done by the maintainer only, so the publishing steps do not belong in public docs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
The release workflow triggers on any v* tag, and a tag can point at any commit on any branch, so a tag pushed on a feature branch would have published from it. nuget.org's policy filters cannot catch this either: a tag push carries ref_type "tag", so a branch filter never matches, and a tag filter cannot tell whether the tag sits on main. Verified before anything is built or any credential is exchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prepares the first packaged release. The library previously could not be consumed by
anyone: it was not on NuGet, targeted
net8.0only so Unity could not import it, had notests, and carried several correctness bugs.
Breaking release, so read the CHANGELOG before merging.
Bugs fixed
IsUnitarycompared floats exactlyqc.Custom(hadamard, 0)threw "not unitary" — the first thing anyone triesSimulator.Runmutated its inputInitializeaccepted unnormalized statesGetStringResultthrew on empty countsPackaging
netstandard2.1(Unity 2021.2+),net8.0andnet10.0from one packageToDiagram()andQuantumGates.Format()return strings, so rendering works without a consolePerformance
Gates apply in place. A 20-qubit GHZ chain went from 83 ms and 328 MB allocated to 34 ms
and 16 MB — one state vector instead of one per gate. Build warnings: 170 → 0.
New
MeasureInto,When,ClassicalBitMeasurementResultreplacing the(int[], int)tupleBlochVector,QubitProbability,ToHistogramReset()Tests
0 → 149, covering gate algebra, textbook states, measurement statistics, the drawer,
QASM output, and one regression test per bug above. Teleportation is verified across five
input states and twenty measurement branches.
Known limitation
Grover's diffusion operator supports up to 3 qubits and throws
NotSupportedExceptionbeyond that. Lifting it needs ancilla-based multi-controlled decomposition — worth doing
before someone's first experiment is a 4-qubit search.
Not yet verified
The
netstandard2.1build compiles, which guarantees nonet8+-only API leaked, but ithas not been run inside Unity. Worth confirming before leaning on the Unity pitch
publicly.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Mhwc4XeGtzM2UQx9sNLb3r