Changelog

View Source

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

[0.10.1] - 2026-07-04

Fixed

  • README corrected against the released 0.10 surface (docs-only release; no code changes). The install snippets were broken outright ({:qx_sim, "~> 0.8.0"} is the wrong app atom; Livebook cells pinned ~> 0.6.0), the Bell quick-start showed counts a measurement-free circuit can never produce, the GHZ example showed pre-0.10 list-shaped counts keys, and the from_qasm_function/1 recipe predated the 0.9 full-module source shape. Also: nonexistent RemoteError replaced with MissingDependencyError, Nx floor corrected to 0.12, VegaLite documented as optional, and the stale QxServer naming replaced with Qx.Hardware + Qx Portal. Every corrected example was executed against 0.10.0 before landing.

[0.10.0] - 2026-07-04

The "Step-Through API & Public-API Streamlining" minor: circuit mode becomes inspectable one operation at a time, and the public surface is cleaned up around it, including a clean-break rework of the Draw layer decided while v0.10 was still unpublished.

Added

Changed

  • Breaking (pre-release, v0.10 never shipped): every draw function now returns one static type in every environment. Qx.draw/2, draw_counts/2, draw_histogram/2 return VegaLite.t() only; draw_bloch/2 returns Qx.Draw.Image; draw_state/2 returns Qx.Draw.StateTable. The :format option is gone everywhere, and draw_state no longer sniffs for Kino at runtime (the same call returned %Kino.Markdown{} in Livebook and a string in a release).
  • Breaking: tier-2 Qx.Draw names now follow the facade (Qx.draw_X delegates to Qx.Draw.X): plot_counts is now counts, bloch_sphere is now bloch.
  • vega_lite is now an optional dependency: only the three VegaLite-returning chart functions need it, and they raise Qx.MissingDependencyError when it's absent.
  • The docs and README now teach one path: build a circuit, run it, and step through it with Qx.steps/2 / Qx.Step.show/1 when you want to see the state evolve. The "Which h am I calling?" grid, the two-modes framing, and the calc-mode README sections are gone; a short migration note in the README maps old calc-mode pipelines onto the stepper.
  • Breaking (pre-release): Qx.Draw.circuit/2 (and the new Qx.draw_circuit/2) returns a Qx.Draw.Image instead of a raw SVG string. Where 0.8.x docs said File.write!(path, svg), write image.svg.

Deprecated

  • Qx.Qubit and Qx.Register (calc mode) are demoted to an internal engine: hidden from the generated docs, dropped from the declared public surface, and carrying no stability guarantee from here on. Both modules still compile, run, and pass their tests, so existing notebooks keep working. Removal or restructuring is deferred to v1.0.

Removed

  • The hand-rolled SVG chart renderer (format: :svg on draw/draw_counts/draw_histogram): a usage inventory found no consumer anywhere. SVG remains the artifact for Bloch spheres and circuit diagrams via Qx.Draw.Image.

  • The VegaLite Bloch-sphere projection: the SVG Bloch renderer is the single Bloch path (its visual upgrade is scheduled for v0.11).

  • Qx.steps/1,2 and %Qx.Step{}: a lazy stream of per-operation steps for walking through a circuit's execution. Works on circuits with mid-circuit measurement and c_if (one stochastic trajectory per materialisation; seed: reproduces a trajectory without touching the caller's process :rand state). Each step carries the operation, the statevector after it, its probabilities, the classical bits so far, and a taken/not-taken flag on conditional steps.

  • Qx.Step.show/1: the Qx.Register.show_state/1 display map (Dirac string, amplitudes, probabilities) for any step, plus a readable single-line Inspect rendering.

  • Qx.tap_state/2 and Qx.tap_probabilities/2 now share the stepper's execution path internally. No behaviour change: same values, same Qx.MeasurementError contract on measured/conditional prefixes.

  • Qx.StateInit.bell_state/0,1,2 and Qx.StateInit.ghz_state/1,2, the deprecated state-vector aliases: use bell_state_vector/0,1,2 and ghz_state_vector/1,2. The circuit-returning Qx.bell_state/0,1 and Qx.ghz_state/0 facades are unaffected. Deprecated through 0.8.x; window closed.

  • Qx.Math.basis_state/2, the f32 shim deprecated in 0.8.x: use Qx.StateInit.basis_state/3 (c64, matching the quantum-state surface).

  • Qx.histogram/1,2: use Qx.draw_histogram/1,2, in place since one minor before this release.

Fixed

  • SimulationResult.counts keys are now the binary strings the docs always promised (%{"10" => 507}), instead of the bit-lists (%{[1, 0] => 507}) the simulator actually emitted since its first release. The type, every doc, and every doctest specified string keys; the engine never produced them, and helpers like Qx.SimulationResult.probability(result, "0") could never match a real result. Hardware (QPU) results already used string keys, so the two run paths now share one contract. Keys join each shot's classical bits in measurement order — identical to the labels Qx.draw_counts/2 renders, so charts are unchanged. Behaviour change: code that pattern-matches list keys out of result.counts must switch to strings (counts[[1, 0]]counts["10"]). result.classical_bits still holds per-shot bit lists, as documented.

  • Circuits containing a barrier no longer raise Qx.GateError: Unsupported gate: :barrier when executed. Every barrier producer (Qx.barrier/2, Qx.barrier_all/1,2, OpenQASM import) stores the instruction with the spanned qubit list, but the simulation engine only treated the 0-qubit shape (which nothing produces) as a no-op, so run/2, get_state/2, and steps/2 all raised. Barriers are now a no-op at any arity and do not advance the renormalize: n gate counter; Qx.steps/2 still yields a step for them with the state unchanged.

  • Qx.tap_state/2 and Qx.tap_probabilities/2 (and their Qx.Operations counterparts) now execute the circuit's instructions before invoking the inspection function. Previously they read the circuit's stored initial state, so the tap always saw |0...0⟩ regardless of the gates applied before it, contradicting their documentation. Both taps now raise Qx.MeasurementError when the circuit so far contains measurements or conditionals, matching Qx.Simulation.get_state/2; tap before the first measure/3 or c_if/4 in the pipeline.

0.9.0 - 2026-07-01

Changed

  • The IBM Quantum client now retries transient GET failures (retry: :safe_transient) instead of failing on the first blip, and raises the /results receive timeout to 60 s for multi-MB Sampler V2 results. Qx.Hardware.Portal GETs retry transiently too; POST requests (IAM exchange, job submission, transpile) are never auto-replayed.

  • Raised minimum runtime dependency versions: nx ~> 0.12 (was ~> 0.10), complex ~> 0.7 (was ~> 0.6, now required by nx 0.12), and req ~> 0.6 (was ~> 0.5). No Qx API or numerical changes; Nx 0.12 renders float32 tensors at native precision, so two Qx.Math doctests were updated to match.

Security

  • Qx.Hardware.Ibm and Qx.Hardware.Portal HTTP error tuples ({:error, {:http, status, body}}) no longer echo the full decoded response body, which could carry echoed request context. The body is reduced to a recognised error message or a generic marker, bounded to ~256 characters. Behaviour change: the third element is now a bounded string rather than the raw response map/body.

  • Qx.Hardware.Config now rejects plaintext http:// URLs to non-loopback hosts for portal_url, base_url, and iam_url. These carry the portal bearer token and route IBM IAM token exchange, so a remote http URL would expose them in cleartext. http:// stays valid for loopback hosts (localhost, 127.0.0.1, ::1) so local mocks and dev are unaffected. Behaviour change: a config with a remote http URL now raises Qx.Hardware.ConfigError instead of being accepted.

  • The OpenQASM importer now caps parenthesis nesting depth (64) before parsing. The expression grammar recurses one frame per (, so a ((((…)))) chain within the 1 MB source cap could exhaust the stack (:enomem); deeply nested input now raises Qx.QasmParseError.

  • Qx.Export.OpenQASM.from_qasm_function/1 now wraps the generated code in a unique Qx.Generated.<Name>_<hash> module instead of returning a bare def, so a downstream Code.compile_string/1 can no longer inject an attacker-named helper into the caller's module. Behaviour change: the returned source is a defmodule, and the result map gains a :module key.

0.8.1 - 2026-06-27

Added

Deprecated

  • Qx.StateInit.bell_state/2 and Qx.StateInit.ghz_state/2 in favour of the _vector-suffixed names above. Both keep working through 0.8.x — they delegate verbatim to the new functions — and are scheduled for removal in v0.9.

Changed

Deprecated

  • Qx.histogram/2 is deprecated. Use Qx.draw_histogram/2 instead. Emits a compile-time warning and is hidden from ExDoc. Scheduled for removal in v1.0.

0.8.0 - 2026-05-22

Added

  • Full calc-mode gate parity on Qx.Register (plan: api-cleanup-phase-b). Eight gates that previously existed only in circuit mode are now available on Qx.Register as direct state-vector evolutions:

    • Qx.Register.cy/3: controlled-Y
    • Qx.Register.crx/4, cry/4, crz/4: controlled rotations
    • Qx.Register.cp/4: controlled-phase
    • Qx.Register.swap/3, iswap/3: two-qubit swaps
    • Qx.Register.cswap/4: Fredkin (controlled-SWAP)
    • Qx.Register.u/5: general single-qubit unitary

    All five controlled-target gates (cy, crx, cry, crz, cp) share an apply_controlled_target/4 internal helper that lifts a 2×2 gate matrix into the full controlled two-qubit unitary via the internal Qx.Gates.controlled_gate factory and applies it to register.state.

  • Basis-explicit measurement on Qx.Qubit (plan: api-cleanup-phase-b). Qx.Qubit.measure_x/1, measure_y/1, and measure_z/1 return the probability distribution in the X, Y, and Z bases respectively (the Z form is an alias of the existing measure_probabilities/1 for symmetry). Maps directly to QAAL Mx, My, Mz. Implementation reuses the existing single-qubit gate pipeline (H for X-basis, Sdg ; H for Y-basis).

  • Named circuit recipes consolidated under Qx.Patterns (plan: api-cleanup-phase-b). New helpers Qx.Patterns.bell_state_circuit/1, ghz_state_circuit/1, and superposition_circuit/1. The top-level Qx.bell_state, Qx.ghz_state, and Qx.superposition now delegate to these helpers (no break: old call sites continue to work). Two of the three got new optional arguments: Qx.ghz_state(num_qubits \\ 3) and Qx.superposition(num_qubits \\ 1): previously hardcoded to 3 and 1 qubits respectively.

Changed

  • Qx.Behaviours.QuantumState now has callbacks (and an implementor). Previously a dead behaviour that no module implemented: Qx.Register now declares @behaviour Qx.Behaviours.QuantumState and provides every required callback. The callback list grew to cover the full Phase B gate surface (sdg, u, cy, swap, iswap, cp, crx, cry, crz, ccx, cswap); the new callbacks are listed under @optional_callbacks so future implementors with smaller surfaces (e.g. a 2-qubit-only subset) don't need to provide them. Going forward, adding a gate to the multi-qubit calc-mode surface is compile-time-enforced to update both the behaviour and Qx.Register.

    Single-qubit Qx.Qubit is not an implementor: its functions take (state) rather than (state, qubit_index), so the signature is structurally incompatible. Unifying both paradigms under one behaviour is a v1.0 redesign and is documented in the behaviour's @moduledoc.

  • Qx.Validation.validate_gate_name!/1 removed. It was dead code (called only from its own tests) with a stale known-gates list (missing CY/CRx/CRy/CRz/CP added in qaal-parity). The corresponding test block is removed from validation_test.exs.

  • Qx.Validation removed from mix.exs groups_for_modules. After Phase A's @doc false sweep, only valid_qubit?/2 and valid_register?/2 remain visible: too thin to warrant a top-level group. The module page itself is still reachable; the renamed "Utilities" group lists Qx.Math and Qx.StateInit.

  • Internal-only functions hidden from documentation (plan: api-cleanup-phase-a). A public-API audit found that several modules labelled "Low-Level Operations" or "Validation & Utilities" in mix.exs were exporting def functions that are used only inside the library. The following are now @moduledoc false or per-function @doc false:

    • Qx.Gates, Qx.Calc, Qx.CalcFast, Qx.Format, Qx.ResultBuilder: entire modules tagged @moduledoc false. Removed from mix.exs groups_for_modules. Functions remain callable for advanced users; they just no longer appear in ExDoc or IDE auto-complete.
    • Qx.QuantumCircuit.add_gate, add_two_qubit_gate, add_three_qubit_gate, add_measurement: @doc false. The user-facing API is Qx.h(qc, 0) etc.; these are internal helpers.
    • Qx.Validation validate_*! family (10 functions): @doc false. Internal Iron Law #7 contracts. The user-facing predicates Qx.Validation.valid_qubit?/2 and valid_register?/2 remain public.
    • Qx.Math.complex_to_tensor, tensor_to_complex, complex_matrix: @doc false. The rest of Qx.Math (complex/2, identity/1, unitary?/1, probabilities/1) stays public.

    No call site breaks. Existing tests pass unchanged.

  • Qx.Qubit.draw_bloch/2 converted from a def wrapper to a defdelegate. Behaviour unchanged.

  • Qx.Error @moduledoc rewritten to be accurate. The previous text described it as a "base exception" Qx users could rescue to catch any Qx error. Elixir exceptions do not inherit, so rescue Qx.Error catches nothing today: the docstring now says so explicitly and lists every typed exception users actually need to rescue.

Deprecated

  • Qx.Math.basis_state/2 is deprecated: use Qx.StateInit.basis_state/3 instead. The two functions returned different types (Math was f32, StateInit is c64), and the StateInit form is the canonical one. The deprecated function emits a compile-time warning and is hidden from ExDoc; it will be removed in v1.0.

Fixed

  • Qx.QuantumCircuit.new now enforces the documented 1..20-qubit cap at both bounds (plan: api-cleanup-phase-a, finding D3). Previously: new(25) silently created an over-cap circuit (upper bound unchecked on this path); new(0) raised FunctionClauseError via a guard (lower bound untyped). Both paths now raise Qx.QubitCountError consistently: the function calls the internal validate_num_qubits! validator on every input. Closes Iron Law #7 holes at both bounds, surfaced by .claude/plans/public-api-audit/plan.md.

BREAKING

Added

  • Qx.StateShapeError: new exception type raised by Qx.QuantumCircuit.set_state/2 when the supplied state vector's shape doesn't match {2^num_qubits}. Carries :actual and :expected size fields.

  • Qx.QubitIndexError {:duplicate, qubits} constructor. New exception/1 clause to raise on distinct-indices violations (e.g. CNOT with control == target, Toffoli with repeated qubits). Message: "Qubit indices must be distinct, got: [...]".

  • Controlled rotations: Qx.cy/3, Qx.crx/4, Qx.cry/4, Qx.crz/4 (plan: qaal-parity). Standard controlled-Pauli-Y and controlled rotation gates, mapping directly to QAAL CY/CRx/CRy/CRz and OpenQASM 3 cy/crx/cry/crz. Simulation handlers reuse the existing two-qubit controlled_gate/4 contraction. The OpenQASM importer (Qx.Export.OpenQASM.from_qasm/1) now also recognises these gates: previously they were in the unsupported-stdgates set.

  • Basis-explicit measurement: Qx.measure_x/3, Qx.measure_y/3, Qx.measure_z/3 (plan: qaal-parity). Match QAAL Mx/My/Mz classical-outcome semantics: measure_x lowers to H ; Mz, measure_y lowers to Sdg ; H ; Mz, measure_z is an alias of measure/3 for symmetry. Note: Qx's simulator samples in the computational basis at end-of-circuit, so the post-measurement quantum state stays Z-basis-aligned (not rotated back into the X-/Y-basis eigenstate): the classical outcome is what tutorials care about and matches QAAL.

  • Qx.Patterns sub-register overload (/2 arity) (plan: qaal-parity). h_all/2, x_all/2, y_all/2, z_all/2, measure_all/2, barrier_all/2 accept a list or Range of qubit indices in addition to the existing whole-circuit /1 form. Lets tutorials operate on a sub-register without re-deriving qubit ranges by hand: Qx.h_all(qc, 0..2), Qx.measure_all(qc, [0, 2]). Empty list/range is a no-op.

  • Qx.Patterns: composite circuit-building helpers. New module providing seven thin wrappers over Qx.Operations for the recurring "apply to every qubit" / "CNOT chain" motifs that appear in tutorials (Grover diffuser, Bernstein-Vazirani oracle, GHZ preparation):

    All seven are also exposed at the top level (Qx.h_all/1, Qx.measure_all/1, …) via defdelegate. Purely additive: no breaking change. Out-of-range qubit indices propagate the existing typed Qx.QubitIndexError inherited from Qx.QuantumCircuit.add_* / Qx.Validation.

  • Configurable statevector renormalization + dev/test norm-drift guard in Qx.Simulation.run/2 / Qx.run/2 (qx-53v). New :renormalize option (default false: fully backwards compatible, zero cost when off): true renormalizes at measurement-time; a positive integer N renormalizes every N gates and at measurement-time; any other value raises the new typed Qx.OptionError. A compile-time-gated assertion (Application.compile_env(:qx, :assert_norm, false), on in :test, off in :prod/:dev) fails fast if a circuit's total probability drifts beyond 1.0e-6. Note: states are :c64 (float32), so the practical norm-accuracy floor is ~1e-7; renormalization bounds drift rather than eliminating it.

0.7.1 - 2026-05-16

Fixed

  • Qx.Hardware.connect/2 now supports discovery before a backend is chosen. It previously hard-validated backend ∈ backends_list and returned a Qx.Hardware.ConfigError on a blank backend, which broke the connect-then-pick flow (e.g. a UI populating a backend dropdown from the connect result). Blank (nil/""/whitespace) backend now skips the membership check and returns the populated config; a set backend is still validated (catches typos early). run/3, run!/3, and submit_qasm/3 now reject a blank backend up front with a clear ConfigError instead of failing deep in an IBM call.

Security

  • Qx.Hardware.Config no longer leaks credentials via inspect/1 (qx-o9h). Added @derive {Inspect, except: [...]} so :portal_token, :ibm_api_key, :ibm_crn, and :access_token are redacted in all inspect output (Logger, BEAM crash reports, error tuples embedding the struct). Non-secret fields remain visible.

0.7.0 - 2026-05-15

BREAKING

  • Removed Qx.Remote, Qx.Remote.Config, and Qx.RemoteError. The qx_server-based hardware path has been replaced by direct-to-IBM execution via Qx.Hardware. There is no shim; the credential shape and call sites are different.

    Migration:

    # before (0.6.x)
    config = Qx.Remote.Config.new!(url: "...", api_key: "...")
    {:ok, result} = Qx.Remote.run(circuit, config, backend: "ibm_brisbane", shots: 4096)
    
    # after (0.7.x)
    config =
      Qx.Hardware.Config.new!(
        portal_url: "https://api.qxquantum.com",
        portal_token: "<qxportal token>",
        ibm_api_key: "<ibm cloud api key>",
        ibm_crn: "<ibm quantum service crn>",
        ibm_region: "us-east",
        backend: "ibm_brisbane",
        shots: 4096
      )
    
    {:ok, result} = Qx.Hardware.run(circuit, config)

    The new Qx.Hardware.Config.from_env!/1 reads QX_PORTAL_URL, QX_PORTAL_TOKEN, QX_IBM_API_KEY, QX_IBM_CRN, QX_IBM_REGION, QX_IBM_BACKEND for a one-liner setup.

Added

  • Qx.Hardware: public namespace owning the full direct-to-IBM execution pipeline (IAM exchange → qxportal transpile → IBM submit → poll → result-build).
  • Qx.Hardware.Config: credential + execution-preference struct (portal_url, portal_token, ibm_api_key, ibm_crn, ibm_region, backend, optimization_level, shots). Validates region against the IBM allowlist, optimization_level 0..3, shots 1..100_000, portal URL scheme.
  • Qx.Hardware.Ibm: Req-based client for IBM Quantum (Qiskit Runtime REST API). IAM exchange + 401-refresh-retry, backends list, backend configuration, Sampler V2 submission, poll loop with Pascal-Case status allowlist, sample-aggregation to counts, best-effort cancel.
  • Qx.Hardware.Portal: Req-based client for the qxportal /api/v1/me and /api/v1/transpile endpoints. Atomize allowlist for response keys.
  • Qx.Hardware.NoMeasurementsError: raised when a circuit submitted to hardware has no measure/2 instructions.
  • Qx.Hardware.ConfigError: typed validation error for Qx.Hardware.Config.
  • Privacy invariant: two independent HTTP clients; the portal token never reaches IBM, and the IBM API key/CRN never reach the portal.
  • Status callback: pipeline progress events ({:portal, :transpiling}, {:ibm, :polling, status}, …). All atoms literal (Iron Law #1: no String.to_atom on caller input).

Removed

  • Qx.Remote, Qx.Remote.Config, Qx.RemoteError (see BREAKING above).
  • examples/remote/: superseded by examples/hardware/run_on_ibm.exs.

Dependencies

  • New test-only dep: {:bypass, "~> 2.1"} (HTTP stubbing for portal + IBM tests).
  • Explicit pin: {:jason, "~> 1.4"} (previously transitive via Req/Plug).

0.6.0 - 2026-05-04

Added

  • OpenQASM 3.0 import: Qx.Export.OpenQASM.from_qasm/1 and from_qasm!/1 parse OpenQASM 3 source produced by Qx itself, by Qiskit, or by IBM Quantum and return a %Qx.QuantumCircuit{}. Round-trips with to_qasm/1 (statevectors match within 1e-10).
  • Gate definition codegen: Qx.Export.OpenQASM.from_qasm_function/1 (and the bang variant) parses a gate name(p1, …) a, b { … } definition and returns %{name, arity, source}, where source is an Elixir def … string that compiles via Code.compile_string/1. Function signature: (circuit, params…, qubits…).
  • Supported gate set on import: direct mappings for h, x, y, z, s, sdg, t, rx, ry, rz, p, phase, u, u3, cx, CX, cz, swap, iswap, cp, cphase, ccx, cswap. Decompositions for tdg → phase(-π/4), sx → u(π/2, -π/2, π/2), u1(λ) → phase(λ), u2(φ, λ) → u(π/2, φ, λ). id is dropped.
  • Typed import errors: Qx.QasmParseError (line/column/snippet) and Qx.QasmUnsupportedError (feature/line/hint) for grammar failures and out-of-scope features respectively.
  • Qx.cp/4: controlled-phase gate applying e^(i·θ) to the |11⟩ basis state, required for QFT and QPE circuits
  • Qx.swap/3: SWAP gate exchanging the quantum states of two qubits; includes circuit diagram rendering (× symbols connected by a line) and OpenQASM 3 export
  • Qx.iswap/3: iSWAP gate exchanging qubit states with an i phase factor on the swapped components; native to superconducting hardware; includes circuit diagram rendering (labelled iSW boxes) and OpenQASM 3 export
  • Qx.u/5: general single-qubit unitary gate U(θ,φ,λ) per IBM/OpenQASM 3 convention; subsumes X, Y, Z, H, RX, RY, RZ as special cases; includes circuit diagram rendering and OpenQASM 3 export
  • Qx.cswap/4: Fredkin (controlled-SWAP) gate; swaps two target qubits when the control is |1⟩; universal reversible gate used in quantum multiplexers and arithmetic circuits; includes circuit diagram rendering and OpenQASM 3 export

Not supported on import (raises Qx.QasmUnsupportedError)

  • Multi-register programs (Qx models a single quantum + single classical register)
  • else branches on if (refactor as two if statements)
  • Gate modifiers inv @, pow(N) @, ctrl @, negctrl @
  • def, for, while, switch, classical types beyond bit, defcal, let, pragma, extern, box, delay, reset
  • stdgates cy, ch, crx, cry, crz, cu (no Qx equivalent yet)
  • Qiskit-extension gates rxx, ryy, rzz, rzx (not in stdgates.inc)
  • Discarded measure q[i]; (Qx requires a classical bit target)
  • Complex boolean conditions (&&, ||)

Dependencies

  • New runtime dependency: nimble_parsec ~> 1.4 (compile-time parser generator)

0.5.2 - 2026-04-11

Added

  • Bell State Extensions - Qx.bell_state/2 now supports all four Bell states: phi_plus (default), phi_minus, psi_plus, and psi_minus

Fixed

  • Circuit diagram: measurement arrowhead now terminates at the classical register double line instead of extending 8 px past it
  • Bloch sphere rendering: improved wireframe contrast, white halos on axis and state labels, and equatorial projection indicator for clearer visualization

0.5.1 - 2026-03-07

Added

  • S-dagger (Sdg) Gate - New sdg gate implementing the S† operation (-π/2 phase rotation on |1⟩)
    • Qx.sdg/2 for Circuit Mode: adds an sdg gate to a quantum circuit
    • Qx.Operations.sdg/2 for direct operations API
    • Qx.Qubit.sdg/1 for Calculation Mode on single qubits
    • Qx.Register.sdg/2 for Calculation Mode on multi-qubit registers
    • Full simulation support in Qx.Simulation (mapped to Qx.Gates.s_dagger/0)
    • OpenQASM 3.0 export support (sdg q[0];)
    • Validation support for the :sdg gate atom
    • Full test coverage including matrix correctness, S·S† = I identity verification, and circuit export
  • New LiveBook Tutorials - Expanded tutorial collection at examples/tutorials/
    • quantum_state_and_qubit.livemd - Introduction to quantum states and single-qubit operations
    • quantum_measurement.livemd - Quantum measurement concepts and examples
    • systems_of_qubits_and_entanglement.livemd - Multi-qubit systems and entanglement
    • quantum_algorithms.livemd - Common quantum algorithms with Qx

0.5.0 - 2026-02-17

Added

  • Remote Execution via QxServer - Run quantum circuits on real hardware through QxServer
    • New Qx.Remote module for submitting circuits, polling job status, and retrieving results
    • New Qx.Remote.Config for configuring QxServer connection (URL, API key, timeout)
    • New Qx.ResultBuilder for constructing Qx.SimulationResult structs from hardware counts data
    • New Qx.RemoteError exception type for remote execution errors
    • Example script at examples/remote/run_on_hardware.exs
  • Quantum Operations Tutorial - New comprehensive LiveBook tutorial covering quantum gates, Bloch sphere, and two-qubit operations at examples/tutorials/quantum_operations_tutorial.livemd

Changed

  • README Restructure - Major reorganization for better new-user experience
    • Moved Performance & Acceleration section (~380 lines) from between Installation and Quick Start to the end
    • Removed API Reference section (duplicated by hexdocs) and Module Structure section
    • Added "Understanding the Two Modes" orientation section with comparison table
    • Added consolidated Visualization section
    • Added links to hexdocs and LiveBook guides
    • Reduced README from 1,308 to ~750 lines

Fixed

  • Qx.Qubit.draw_bloch/2 now correctly defaults to :vega_lite format (was ignoring the default and using :svg)
  • Draw functions (Qx.Draw.plot_counts/2, Qx.Draw.plot/2) now correctly handle SimulationResult structs from hardware backends where counts keys are binary strings
  • OpenQASM export formatting improvements

0.4.0 - 2025-12-23

Added

  • OpenQASM 3.0 Export - Export quantum circuits to OpenQASM format for real quantum hardware execution
    • Full support for OpenQASM 3.0 syntax including conditionals
    • Export via Qx.Export.OpenQASM.to_qasm/2 with customizable options
    • Supports all quantum gates, measurements, barriers, and conditional operations
    • Enables seamless integration with IBM Quantum, Rigetti, and other quantum hardware platforms
  • Error Handling Documentation (qx-gd5) - Comprehensive CONTRIBUTING.md with error handling philosophy and best practices
    • Detailed guidelines for error types, messages, and recovery strategies
    • Error handling patterns for library developers
    • Examples of proper error propagation and context enrichment
    • Documentation of all custom error types and their use cases
  • Test Coverage Integration (qx-xbf) - Complete test coverage metrics and CI/CD integration
    • Added ExCoveralls dependency for code coverage reporting
    • Achieved 66.4% test coverage across the codebase
    • Integrated coverage reporting into CI/CD pipeline
    • Configured multiple coverage output formats (HTML, JSON, Cobertura)
    • Added GitHub Actions integration for coverage tracking

Changed

  • Predicate Function Conventions (qx-7iw) - Enhanced predicate naming and specifications
    • Added @spec type specifications to all predicate functions
    • Improved naming conventions for boolean-returning functions
    • Enhanced documentation for predicate function usage patterns
    • Better consistency across module APIs
  • Module Documentation (qx-sdb) - Verified comprehensive module documentation
    • Confirmed all modules have proper @moduledoc documentation
    • Ensured consistent documentation style across the codebase
    • Enhanced module-level descriptions and usage examples

Fixed

  • Credo strict mode compliance in OpenQASM export module
    • Refactored complex pattern matching to reduce cyclomatic complexity
    • Used Enum.map_join/3 for better performance
    • Added inline Credo exception for legitimate gate mapping complexity

Improved

  • Development workflow with better error handling guidelines
  • Code quality with comprehensive type specifications
  • Test coverage visibility and tracking
  • Documentation completeness and consistency
  • Hardware integration capabilities via OpenQASM export

0.3.0 - 2025-12-21

Added

  • Runtime Backend Selection - Major new feature allowing backend specification at runtime without compile-time configuration
    • Added :backend option to Qx.run/2, Qx.get_state/2, and Qx.get_probabilities/2
    • Users can now specify different backends for different circuits: Qx.run(circuit, backend: EXLA.Backend)
    • Supports all Nx backends including EXLA (CPU/CUDA/ROCm) and EMLX (Apple Silicon GPU)
    • Combines with other options: Qx.run(circuit, backend: EXLA.Backend, shots: 2048)
    • Maintains full backward compatibility with existing code
    • Implemented using Nx.with_default_backend/2 for proper scoped execution
    • Comprehensive documentation added to README.md with usage examples and best practices

Changed

  • Draw Module Refactoring - Reorganized visualization code for better maintainability and clarity
    • Split large 1,531-line Qx.Draw module into 5 focused sub-modules:
      • Qx.Draw.VegaLite - VegaLite chart generation for LiveBook (178 lines)
      • Qx.Draw.SVG.Charts - SVG histogram and bar charts (199 lines)
      • Qx.Draw.SVG.Bloch - Bloch sphere visualization with 3D projection (267 lines)
      • Qx.Draw.SVG.Circuit - Quantum circuit diagrams with IEEE notation (596 lines)
      • Qx.Draw.Tables - State table formatting with Kino support (196 lines)
    • Qx.Draw now serves as a clean API facade, delegating to specialized sub-modules
    • Maintains 100% backward compatibility - no API changes required
    • Improved code organization following single responsibility principle
    • Better separation of concerns between rendering formats and visualization types
    • All 557 tests continue to pass

Fixed

  • Fixed Nx backend configuration anti-pattern where library imposed compile-time backend choices on users
  • Eliminated warnings about undefined Nx.default_backend/2 function

0.2.5 - 2025-12-16

Fixed

  • More fixes to the pipeline

0.2.4 - 2025-12-16

Fixed

  • More automation of the release and build process
  • pipeline fixes

0.2.3 - 2025-12-14

Fixed

  • Simplified application configuration to resolve Nx.Defn compilation issues
  • Removed unnecessary application dependencies that were causing compile-time conflicts

0.2.2 - 2025-12-14

Fixed

  • Added nx and complex to extra_applications in mix.exs to fix compilation errors when using the Hex package
  • Ensures dependencies are loaded before qx_sim compiles

Changed

  • Published to Hex.pm as qx_sim (package name "qx" was already taken)
  • Updated installation instructions to use Hex.pm syntax
  • Added Hex.pm badges to README

0.2.1 - 2025-11-26

Changed

  • Improved readability of Bloch sphere labels
  • Refactored code and tidied up documentation
  • Cleaned up old test files
  • Updated README.md

Fixed

  • Fixed CNOT gate error
  • Fixed dependencies
  • Fixed mix.exs configuration

0.2.0 - 2025-11-01

Added

Core Quantum Computing Features

  • Full quantum circuit API with chainable operations via Qx module
  • Support for 20+ quantum gates including:
    • Single-qubit gates: H, X, Y, Z, S, T, Sdg, Tdg
    • Parametric rotation gates: RX, RY, RZ with arbitrary angles
    • Two-qubit gates: CNOT (CX), CZ (Controlled-Z), SWAP
    • Multi-qubit support up to 20 qubits
  • Measurement operations with classical bit storage and reset capabilities
  • Conditional operations based on classical measurement results
  • Statevector simulation using Nx tensors with Complex64 format
  • Direct state access via Qx.get_state/1

Visualization

  • Circuit diagram generation with Qx.Draw.circuit/2 for publication-quality SVG output
  • State visualization using VegaLite: bar charts, probability distributions, Bloch sphere
  • SVG export capability for all visualization types
  • Example visualization scripts in examples/ directory including circuit_visualization_example.exs

Performance & Acceleration

  • EXLA backend integration for CPU acceleration (~100x speedup vs Binary)
  • EMLX backend support for Apple Silicon GPU acceleration (M1/M2/M3/M4)
  • Automatic backend detection and configuration
  • JIT compilation support via Nx.Defn

Benchmarking Suite

  • Professional benchmarking infrastructure using Benchee
  • GHZ state scaling benchmarks (5, 10, 15, 20 qubits)
  • Backend comparison benchmarks (Binary, EXLA CPU, EMLX GPU, EXLA CUDA/ROCm)
  • HTML report generation with interactive graphs
  • Statistical analysis with warmup, iterations, and memory profiling
  • Safe GPU backend detection with graceful fallback

Documentation & Examples

  • Comprehensive API documentation with examples
  • Example files demonstrating:
    • Basic quantum circuit operations
    • Complex number handling
    • Bell state creation
    • Quantum teleportation protocol
    • Conditional circuit operations
    • Grover's search algorithm
    • Circuit visualization techniques
  • Performance benchmarking guide
  • Backend configuration documentation

Error Handling

Changed

  • Updated state representation to use :c64 (Complex64) tensor format for improved performance
  • Migrated from Torchx to EMLX for Apple Silicon GPU acceleration (pure Elixir, no Python)
  • Enhanced error messages with context and suggestions
  • Improved documentation structure with module grouping
  • Updated examples to work with latest Complex number API

Performance

  • ~100x speedup with EXLA CPU backend compared to Binary backend
  • Additional 2-10x speedup with GPU acceleration (hardware dependent)
  • Efficient statevector manipulation with direct tensor operations
  • Optimized gate application avoiding unnecessary matrix construction

Fixed

  • Complex number handling in example files for :c64 format
  • CZ gate now properly exposed in main Qx module API
  • Backend detection error handling for unavailable GPU platforms
  • Output directory creation in visualization examples
  • Grover's algorithm now uses proper CZ gates instead of H-CX-H decomposition

Developer Experience

  • Added :usage_rules dependency for better development ergonomics
  • Comprehensive test suite with 549 passing tests
  • Modular architecture separating concerns (Circuit, Operations, Simulation, etc.)
  • Clean API design following Elixir conventions

0.1.0 - 2024-10-05

Added

  • Initial release
  • Basic quantum circuit functionality
  • Core gate operations
  • Statevector simulation
  • Nx backend integration