Qx (Qx - Quantum Computing Simulator v0.10.0)

View Source

Qx - A Quantum Computing Simulator for Elixir

Qx provides a simple and intuitive API for quantum computing simulations. It supports up to 20 qubits with statevector simulation using Nx as the computational backend for efficient processing.

Applying gates

Gate functions like Qx.h/2 record onto a Qx.QuantumCircuit; run the finished circuit with Qx.run/2.

Want the state after each operation? Qx.steps/2 replays the circuit one instruction at a time, and Qx.Step.show/1 turns any step into amplitudes plus probabilities.

Earlier releases also documented a calc mode (eager per-gate state evolution, no circuit). Those modules are now an internal engine: still present and functional, hidden from these docs, no stability guarantee.

Example Usage

# Create a Bell state circuit
qc = Qx.create_circuit(2, 2)
|> Qx.h(0)
|> Qx.cx(0, 1)
|> Qx.measure(0, 0)
|> Qx.measure(1, 1)

result = Qx.run(qc)
Qx.draw(result)

Modules

The Qx library consists of several modules:

  • Qx - Main API (this module)
  • Qx.QuantumCircuit - Quantum circuit creation and management
  • Qx.Operations - Quantum gate operations, including basis-explicit measurement (measure_x/measure_y/measure_z) and controlled rotations (cy, crx, cry, crz)
  • Qx.Patterns - Composite circuit-building patterns. Each _all helper accepts either no second arg (whole-circuit) or a list/range of qubit indices (sub-register) — e.g. Qx.h_all(qc, 0..2).
  • Qx.Simulation - Circuit execution and simulation
  • Qx.SimulationResult - Struct returned by Qx.run/2 (statevector + counts)
  • Qx.Step - One executed operation from Qx.steps/2 (step-through inspection of a running circuit, mid-circuit measurement included)
  • Qx.Draw - Visualization of results
  • Qx.Math - Core mathematical functions for quantum mechanics
  • Qx.StateInit - State-vector constructors (basis, Bell, GHZ, W states)
  • Qx.Export.OpenQASM - Export circuits to OpenQASM for real quantum hardware
  • Qx.Hardware - Run circuits on cloud QPUs (e.g. IBM Quantum)
  • Qx.Hardware.Config - Hardware backend configuration (IBM Quantum via qxportal)
  • Qx.Behaviours.QuantumState - Behaviour contract for quantum-state types

Exporting to Real Quantum Hardware

Qx can export circuits to OpenQASM format for execution on real quantum computers:

# Create a Bell state circuit
circuit = Qx.create_circuit(2, 2)
  |> Qx.h(0)
  |> Qx.cx(0, 1)
  |> Qx.measure(0, 0)
  |> Qx.measure(1, 1)

# Export to OpenQASM 3.0
qasm = Qx.Export.OpenQASM.to_qasm(circuit)
File.write!("bell_state.qasm", qasm)

See Qx.Export.OpenQASM for more details and examples.

Summary

Functions

Adds a barrier across the given qubits.

Adds a single barrier instruction spanning every qubit.

Adds a single barrier spanning the given list or range of qubits. See Qx.Patterns.barrier_all/2. Empty list/range is a no-op.

Creates one of the four Bell-state circuits (maximally entangled two-qubit states). See Qx.Patterns.bell_state_circuit/1.

Applies gates conditionally based on a classical bit value.

Applies a controlled-controlled-X (CCNOT/Toffoli) gate.

Applies a controlled-phase (CP) gate.

Creates a new quantum circuit with only qubits (no classical bits).

Creates a new quantum circuit with specified qubits and classical bits.

Applies a controlled rotation about the X-axis. See Qx.Operations.crx/4.

Applies a controlled rotation about the Y-axis. See Qx.Operations.cry/4.

Applies a controlled rotation about the Z-axis. See Qx.Operations.crz/4.

Applies a Fredkin (controlled-SWAP) gate.

Applies a controlled-X (CNOT) gate.

Applies a linear cascade of CNOTs along qubits.

Applies a controlled-Y (CY) gate. See Qx.Operations.cy/3.

Applies a controlled-Z (CZ) gate.

Visualizes probability distribution from simulation results.

Visualizes a single qubit state on the Bloch sphere.

Draws a quantum circuit diagram.

Visualizes measurement counts as a bar chart.

Creates a histogram from a raw probability tensor.

Displays a quantum state as a formatted table.

Gets probability distribution for computational basis states.

Executes a circuit and returns only the final quantum state.

Creates an n-qubit GHZ-state preparation circuit. Default is 3 qubits.

Applies a Hadamard gate to the specified qubit.

Applies a Hadamard gate to every qubit in the circuit.

Applies a Hadamard gate to every qubit in the given list or range. See Qx.Patterns.h_all/2.

Applies an iSWAP gate, exchanging qubit states while applying an i phase factor to the swapped components.

Adds a measurement operation to the circuit.

Measures every qubit into its same-index classical bit.

Measures every qubit in the given list or range into its same-index classical bit. See Qx.Patterns.measure_all/2.

Performs a Z-basis (computational) measurement. Alias of measure/3 for symmetry with measure_x/3 and measure_y/3. See Qx.Operations.measure_z/3.

Applies a phase gate with specified phase.

Executes the quantum circuit and returns simulation results.

Applies a rotation around the X-axis.

Applies a rotation around the Y-axis.

Applies a rotation around the Z-axis.

Applies an S gate (phase gate with π/2 phase).

Applies an S† (S-dagger) gate (-π/2 phase on |1⟩).

Steps through a circuit: a lazy stream of Qx.Step structs, one per executed operation.

Creates an n-qubit equal-superposition circuit (Hadamard on every qubit). Default is 1 qubit. See Qx.Patterns.superposition_circuit/1.

Applies a SWAP gate, exchanging the quantum states of two qubits.

Applies a T gate (phase gate with π/4 phase).

Inspects the circuit without breaking the pipeline.

Inspects measurement probabilities without breaking the pipeline.

Inspects the current quantum state without breaking the pipeline.

Applies the general single-qubit unitary gate U(θ,φ,λ).

Returns version information for the Qx library.

Applies a Pauli-X gate (bit flip) to the specified qubit.

Applies a Pauli-X gate to every qubit in the circuit.

Applies a Pauli-X gate to every qubit in the given list or range. See Qx.Patterns.x_all/2.

Applies a Pauli-Y gate to the specified qubit.

Applies a Pauli-Y gate to every qubit in the circuit.

Applies a Pauli-Y gate to every qubit in the given list or range. See Qx.Patterns.y_all/2.

Applies a Pauli-Z gate (phase flip) to the specified qubit.

Applies a Pauli-Z gate to every qubit in the circuit.

Applies a Pauli-Z gate to every qubit in the given list or range. See Qx.Patterns.z_all/2.

Types

bell_state_type()

@type bell_state_type() :: Qx.Patterns.bell_state_type()

circuit()

@type circuit() :: Qx.QuantumCircuit.t()

simulation_result()

@type simulation_result() :: Qx.Simulation.simulation_result()

Functions

barrier(circuit, qubits)

@spec barrier(circuit(), [non_neg_integer()]) :: circuit()

Adds a barrier across the given qubits.

Barriers are pure visualisation markers with no effect on the quantum state. Used to group logical sections of a circuit when generating diagrams.

See Qx.Operations.barrier/2. For a barrier across every qubit, see Qx.barrier_all/1.

Examples

iex> qc = Qx.create_circuit(3) |> Qx.barrier([0, 2])
iex> Qx.QuantumCircuit.get_instructions(qc)
[{:barrier, [0, 2], []}]

barrier_all(circuit)

@spec barrier_all(circuit()) :: circuit()

Adds a single barrier instruction spanning every qubit.

Examples

iex> qc = Qx.create_circuit(3) |> Qx.barrier_all()
iex> Qx.QuantumCircuit.get_instructions(qc)
[{:barrier, [0, 1, 2], []}]

barrier_all(circuit, qubits)

@spec barrier_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Adds a single barrier spanning the given list or range of qubits. See Qx.Patterns.barrier_all/2. Empty list/range is a no-op.

bell_state(which \\ :phi_plus)

@spec bell_state(bell_state_type()) :: circuit()

Creates one of the four Bell-state circuits (maximally entangled two-qubit states). See Qx.Patterns.bell_state_circuit/1.

AtomState
:phi_plus`Φ+⟩ = (00⟩ +11⟩)/√2` (default)
:phi_minus`Φ-⟩ = (00⟩ -11⟩)/√2`
:psi_plus`Ψ+⟩ = (01⟩ +10⟩)/√2`
:psi_minus`Ψ-⟩ = (01⟩ -10⟩)/√2`

Examples

iex> bell_circuit = Qx.bell_state()
iex> bell_circuit.num_qubits
2

iex> bell_circuit = Qx.bell_state(:psi_minus)
iex> bell_circuit.num_qubits
2

See Also

c_if(circuit, classical_bit, value, gate_fn)

@spec c_if(circuit(), non_neg_integer(), 0 | 1, (circuit() -> circuit())) :: circuit()

Applies gates conditionally based on a classical bit value.

Enables mid-circuit measurement with classical feedback - a key capability for quantum error correction, quantum teleportation, and adaptive algorithms.

Parameters

  • circuit - Quantum circuit
  • classical_bit - Classical bit index to check (must have been measured)
  • value - Value to compare (0 or 1)
  • gate_fn - Function that applies gates when condition is true

Examples

# Apply X gate to qubit 1 if classical bit 0 equals 1
iex> qc = Qx.create_circuit(2, 2)
...> |> Qx.h(0)
...> |> Qx.measure(0, 0)
...> |> Qx.c_if(0, 1, fn c -> Qx.x(c, 1) end)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
3

# Multiple gates in conditional block
iex> qc = Qx.create_circuit(3, 2)
...> |> Qx.measure(0, 0)
...> |> Qx.c_if(0, 1, fn c ->
...>      c |> Qx.x(1) |> Qx.h(2)
...>    end)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
2

See Also

  • OpenQASM 3.0 if-statements for hardware compatibility
  • Quantum teleportation example in documentation

ccx(circuit, control1, control2, target)

Applies a controlled-controlled-X (CCNOT/Toffoli) gate.

Flips target qubit if and only if both control qubits are |1⟩

Parameters

  • circuit - Quantum circuit
  • control1 - First control qubit index
  • control2 - Second control qubit index
  • target - Target qubit index

Examples

iex> qc = Qx.create_circuit(3) |> Qx.ccx(0, 1, 2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

cp(circuit, control_qubit, target_qubit, theta)

Applies a controlled-phase (CP) gate.

Applies a phase of e^(i*theta) to the |11⟩ basis state only. All other basis states are unchanged.

Parameters

  • circuit - Quantum circuit
  • control_qubit - Control qubit index
  • target_qubit - Target qubit index
  • theta - Phase angle in radians

Examples

iex> qc = Qx.create_circuit(2) |> Qx.cp(0, 1, :math.pi())
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

create_circuit(num_qubits)

@spec create_circuit(pos_integer()) :: circuit()

Creates a new quantum circuit with only qubits (no classical bits).

Parameters

  • num_qubits - Number of qubits (1-20 recommended)

Examples

iex> qc = Qx.create_circuit(3)
iex> qc.num_qubits
3
iex> qc.num_classical_bits
0

Raises

create_circuit(num_qubits, num_classical_bits)

@spec create_circuit(pos_integer(), non_neg_integer()) :: circuit()

Creates a new quantum circuit with specified qubits and classical bits.

Parameters

  • num_qubits - Number of qubits (1-20 recommended)
  • num_classical_bits - Number of classical bits for measurements

Examples

iex> qc = Qx.create_circuit(2, 2)
iex> qc.num_qubits
2
iex> qc.num_classical_bits
2

Raises

crx(circuit, control_qubit, target_qubit, theta)

Applies a controlled rotation about the X-axis. See Qx.Operations.crx/4.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.crx(0, 1, :math.pi() / 2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

cry(circuit, control_qubit, target_qubit, theta)

Applies a controlled rotation about the Y-axis. See Qx.Operations.cry/4.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.cry(0, 1, :math.pi() / 2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

crz(circuit, control_qubit, target_qubit, theta)

Applies a controlled rotation about the Z-axis. See Qx.Operations.crz/4.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.crz(0, 1, :math.pi() / 2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

cswap(circuit, control, target_a, target_b)

Applies a Fredkin (controlled-SWAP) gate.

Swaps the quantum states of target_a and target_b when the control qubit is |1⟩. When the control qubit is |0⟩, the targets are unchanged.

Parameters

  • circuit - Quantum circuit
  • control - Control qubit index (0-based)
  • target_a - First target qubit index (0-based)
  • target_b - Second target qubit index (0-based)

Examples

iex> qc = Qx.create_circuit(3) |> Qx.cswap(0, 1, 2)
iex> [{:cswap, [0, 1, 2], []}] = Qx.QuantumCircuit.get_instructions(qc)
iex> :ok
:ok

Raises

cx(circuit, control_qubit, target_qubit)

Applies a controlled-X (CNOT) gate.

Flips target qubit if and only if control qubit is |1⟩

Parameters

  • circuit - Quantum circuit
  • control_qubit - Control qubit index
  • target_qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(2) |> Qx.cx(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

cx_chain(circuit, qubits)

@spec cx_chain(circuit(), [non_neg_integer()]) :: circuit()

Applies a linear cascade of CNOTs along qubits.

For qubits = [q0, q1, …, qk], emits cx(q0, q1) → cx(q1, q2) → …. Empty and single-element lists are no-ops. See Qx.Patterns.cx_chain/2.

Examples

iex> qc = Qx.create_circuit(3) |> Qx.h(0) |> Qx.cx_chain([0, 1, 2])
iex> length(Qx.QuantumCircuit.get_instructions(qc))
3

Raises

cy(circuit, control_qubit, target_qubit)

Applies a controlled-Y (CY) gate. See Qx.Operations.cy/3.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.cy(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

cz(circuit, control_qubit, target_qubit)

Applies a controlled-Z (CZ) gate.

Applies a Z gate to the target qubit if and only if the control qubit is |1⟩. This is a symmetric two-qubit gate that applies a phase flip when both qubits are |1⟩.

Parameters

  • circuit - Quantum circuit
  • control_qubit - Control qubit index
  • target_qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(2) |> Qx.cz(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

draw(result, options \\ [])

@spec draw(
  simulation_result(),
  keyword()
) :: VegaLite.t()

Visualizes probability distribution from simulation results.

Convenience function for quickly plotting the probability distribution from a simulation result. The probabilities are automatically extracted from the result map.

For plotting raw probability tensors (e.g., from get_probabilities/1), use draw_histogram/2 instead.

Returns a VegaLite.t() chart spec in every environment; Livebook renders it via kino_vega_lite, standalone apps feed it to any Vega renderer.

Parameters

  • result - Simulation result from run/1 or run/2
  • options - Optional plotting parameters

Options

  • :title - Plot title
  • :width - Plot width (default: 400)
  • :height - Plot height (default: 300)

Returns

A VegaLite.t() chart specification.

Raises

Examples

iex> qc = Qx.create_circuit(2) |> Qx.h(0) |> Qx.cx(0, 1)
iex> result = Qx.run(qc)
iex> plot = Qx.draw(result)
iex> is_struct(plot, VegaLite)
true

See Also

draw_bloch(qubit, options \\ [])

@spec draw_bloch(
  Nx.Tensor.t(),
  keyword()
) :: Qx.Draw.Image.t()

Visualizes a single qubit state on the Bloch sphere.

The Bloch sphere provides a geometric representation of a pure qubit state. Handy for seeing what a single-qubit gate did to the state.

Parameters

  • qubit - Single-qubit state tensor (2 amplitudes), e.g. from Qx.get_state/1 on a 1-qubit circuit or from a Qx.Step
  • options - Optional plotting parameters

Returns a Qx.Draw.Image artifact in every environment: Livebook renders it inline (via Kino.Render), standalone applications read the SVG from image.svg.

Options

  • :title - Plot title (default: "Bloch Sphere")
  • :size - Sphere size (default: 400)

Returns

A Qx.Draw.Image struct carrying the SVG.

Examples

# Visualize |0⟩ state
iex> state = Qx.create_circuit(1) |> Qx.get_state()
iex> image = Qx.draw_bloch(state)
iex> is_struct(image, Qx.Draw.Image)
true

# Visualize superposition state
iex> state = Qx.create_circuit(1) |> Qx.h(0) |> Qx.get_state()
iex> image = Qx.draw_bloch(state, title: "Superposition State")
iex> String.contains?(image.svg, "<svg")
true

See Also

draw_circuit(circuit, title \\ nil)

@spec draw_circuit(circuit(), String.t() | nil) :: Qx.Draw.Image.t()

Draws a quantum circuit diagram.

Returns a Qx.Draw.Image artifact in every environment: Livebook renders the diagram inline (via Kino.Render — a cell that simply returns a circuit already renders it), standalone applications read the SVG from image.svg.

Parameters

  • circuit - The quantum circuit to visualize
  • title - Optional diagram title (default: nil)

Returns

A Qx.Draw.Image struct carrying the SVG diagram.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.h(0) |> Qx.cx(0, 1)
iex> image = Qx.draw_circuit(qc, "Bell")
iex> String.contains?(image.svg, "<svg")
true

See Also

draw_counts(result, options \\ [])

@spec draw_counts(
  simulation_result(),
  keyword()
) :: VegaLite.t()

Visualizes measurement counts as a bar chart.

Returns a VegaLite.t() chart spec in every environment; Livebook renders it via kino_vega_lite. Works with results from both local simulation and Qx.Hardware execution.

Parameters

  • result - Simulation result containing measurement data
  • options - Optional plotting parameters (:title, :width, :height)

Returns

A VegaLite.t() chart specification.

Raises

Examples

iex> qc = Qx.create_circuit(2, 2) |> Qx.h(0) |> Qx.measure(0, 0)
iex> result = Qx.run(qc)
iex> plot = Qx.draw_counts(result)
iex> is_struct(plot, VegaLite)
true

draw_histogram(probabilities, options \\ [])

@spec draw_histogram(
  Nx.Tensor.t(),
  keyword()
) :: VegaLite.t()

Creates a histogram from a raw probability tensor.

Use this function when you have a probability tensor and want to visualize it. This is useful for:

  • Plotting probabilities from get_probabilities/1 without running simulation
  • Visualizing custom or theoretical probability distributions
  • Comparing different probability distributions

For quick visualization of simulation results, use draw/2 instead.

Parameters

  • probabilities - Nx tensor of probabilities (should sum to 1.0)
  • options - Optional plotting parameters

Examples

# Visualize probabilities without full simulation
iex> qc = Qx.create_circuit(2) |> Qx.h(0)
iex> probs = Qx.get_probabilities(qc)
iex> hist = Qx.draw_histogram(probs)
iex> is_struct(hist, VegaLite)
true

See Also

draw_state(register_or_state, options \\ [])

@spec draw_state(
  Nx.Tensor.t(),
  keyword()
) :: Qx.Draw.StateTable.t()

Displays a quantum state as a formatted table.

Shows basis states with their amplitudes and probabilities. Useful for inspecting a multi-qubit state without running the full circuit.

Returns a Qx.Draw.StateTable artifact in every environment: Livebook renders the markdown table (via Kino.Render), IEx prints the text form, and the :text/:markdown/:html fields carry the renderings for standalone use.

Parameters

  • state - state tensor, e.g. from Qx.get_state/1
  • options - Optional display parameters

Options

  • :precision - Decimal places (default: 3)
  • :hide_zeros - Hide zero-amplitude states (default: false)

Returns

A Qx.Draw.StateTable struct.

Examples

# Display Bell state
iex> state = Qx.create_circuit(2) |> Qx.h(0) |> Qx.cx(0, 1) |> Qx.get_state()
iex> table = Qx.draw_state(state)
iex> table.text =~ "Basis State"
true

# Hide zero states
iex> state = Qx.create_circuit(3) |> Qx.h(0) |> Qx.get_state()
iex> table = Qx.draw_state(state, hide_zeros: true)
iex> table.text =~ "|111⟩"
false

See Also

get_probabilities(circuit, options \\ [])

@spec get_probabilities(
  circuit(),
  keyword()
) :: Nx.Tensor.t()

Gets probability distribution for computational basis states.

Parameters

  • circuit - Quantum circuit
  • options - Optional parameters

Options

  • :backend - Nx backend to use, e.g. Nx.BinaryBackend (default) or {EXLA.Backend, client: :host} if EXLA is added to your deps (see README)

Examples

iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> probs = Qx.get_probabilities(qc)
iex> Nx.shape(probs)
{2}

# Specify backend at runtime
# Qx.get_probabilities(qc, backend: {EXLA.Backend, client: :host})

Raises

get_state(circuit, options \\ [])

@spec get_state(
  circuit(),
  keyword()
) :: Nx.Tensor.t()

Executes a circuit and returns only the final quantum state.

Parameters

  • circuit - Quantum circuit to execute
  • options - Optional parameters

Options

  • :backend - Nx backend to use, e.g. Nx.BinaryBackend (default) or {EXLA.Backend, client: :host} if EXLA is added to your deps (see README)

Examples

iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> state = Qx.get_state(qc)
iex> Nx.shape(state)
{2}

# Specify backend at runtime
# Qx.get_state(qc, backend: {EXLA.Backend, client: :host})

Raises

ghz_state(num_qubits \\ 3)

@spec ghz_state(pos_integer()) :: circuit()

Creates an n-qubit GHZ-state preparation circuit. Default is 3 qubits.

Returns a circuit that prepares |GHZ⟩ = (|0…0⟩ + |1…1⟩)/√2 on a |0…0⟩ input. See Qx.Patterns.ghz_state_circuit/1.

Examples

iex> ghz_circuit = Qx.ghz_state()
iex> ghz_circuit.num_qubits
3

iex> ghz_circuit = Qx.ghz_state(5)
iex> ghz_circuit.num_qubits
5

See Also

h(circuit, qubit)

@spec h(circuit(), non_neg_integer()) :: circuit()

Applies a Hadamard gate to the specified qubit.

Creates superposition: |0⟩ → (|0⟩ + |1⟩)/√2, |1⟩ → (|0⟩ - |1⟩)/√2

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

h_all(circuit)

@spec h_all(circuit()) :: circuit()

Applies a Hadamard gate to every qubit in the circuit.

Convenience for the recurring Enum.reduce(0..(n - 1), qc, &Qx.h(&2, &1)) motif (Grover diffuser, Bernstein-Vazirani oracle, equal-superposition preparation). See Qx.Patterns for the full set of composite patterns.

Examples

iex> qc = Qx.create_circuit(3) |> Qx.h_all()
iex> length(Qx.QuantumCircuit.get_instructions(qc))
3

h_all(circuit, qubits)

@spec h_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Applies a Hadamard gate to every qubit in the given list or range. See Qx.Patterns.h_all/2.

Examples

iex> qc = Qx.create_circuit(5) |> Qx.h_all(0..2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
3

iswap(circuit, qubit_a, qubit_b)

@spec iswap(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Applies an iSWAP gate, exchanging qubit states while applying an i phase factor to the swapped components.

Native to superconducting qubit hardware (Google Sycamore, Rigetti). Unlike SWAP, applying iSWAP twice is not the identity — it produces a -1 phase.

Parameters

  • circuit - Quantum circuit
  • qubit_a - Index of the first qubit
  • qubit_b - Index of the second qubit

Examples

iex> qc = Qx.create_circuit(2) |> Qx.iswap(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

measure(circuit, qubit, classical_bit)

@spec measure(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Adds a measurement operation to the circuit.

Parameters

  • circuit - Quantum circuit
  • qubit - Qubit index to measure
  • classical_bit - Classical bit index to store result

Examples

iex> qc = Qx.create_circuit(2, 2) |> Qx.measure(0, 0)
iex> length(Qx.QuantumCircuit.get_measurements(qc))
1

Raises

measure_all(circuit)

@spec measure_all(circuit()) :: circuit()

Measures every qubit into its same-index classical bit.

Raises Qx.ClassicalBitError if circuit.num_classical_bits < num_qubits — the caller owns the circuit shape (see Qx.Patterns.measure_all/1).

Examples

iex> qc = Qx.create_circuit(3, 3) |> Qx.measure_all()
iex> length(Qx.QuantumCircuit.get_measurements(qc))
3

Raises

measure_all(circuit, qubits)

@spec measure_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Measures every qubit in the given list or range into its same-index classical bit. See Qx.Patterns.measure_all/2.

Raises

measure_x(circuit, qubit, classical_bit)

@spec measure_x(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Performs an X-basis measurement. See Qx.Operations.measure_x/3.

Examples

iex> qc = Qx.create_circuit(1, 1) |> Qx.measure_x(0, 0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
2

Raises

measure_y(circuit, qubit, classical_bit)

@spec measure_y(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Performs a Y-basis measurement. See Qx.Operations.measure_y/3.

Examples

iex> qc = Qx.create_circuit(1, 1) |> Qx.measure_y(0, 0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
3

Raises

measure_z(circuit, qubit, classical_bit)

@spec measure_z(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Performs a Z-basis (computational) measurement. Alias of measure/3 for symmetry with measure_x/3 and measure_y/3. See Qx.Operations.measure_z/3.

Examples

iex> qc = Qx.create_circuit(1, 1) |> Qx.measure_z(0, 0)
iex> length(Qx.QuantumCircuit.get_measurements(qc))
1

phase(circuit, qubit, phi)

@spec phase(circuit(), non_neg_integer(), float()) :: circuit()

Applies a phase gate with specified phase.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index
  • phi - Phase angle in radians

Examples

iex> qc = Qx.create_circuit(1) |> Qx.phase(0, :math.pi/4)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

run(circuit, options \\ [])

Executes the quantum circuit and returns simulation results.

Parameters

  • circuit - Quantum circuit to execute
  • options - Optional parameters (can be keyword list or integer for backward compatibility)

Options

  • :shots - Number of measurement shots (default: 1024)
  • :backend - Nx backend to use, e.g. Nx.BinaryBackend (default) or {EXLA.Backend, client: :host} if EXLA is added to your deps (see README)
  • :renormalize - Counter unitary float drift (default: false). false = off; true = renormalize at measurement-time; positive integer N = renormalize every N gates and at measurement-time. Other values raise Qx.OptionError. See Qx.Simulation.run/2 for the float32 accuracy note.

Returns

A Qx.SimulationResult struct with these fields:

  • :probabilities - real probability tensor |ψ|² over all 2^n basis states
  • :classical_bits - one classical-bit vector per shot, each a list of 0 / 1 values
  • :state - final statevector (complex-valued :c64 tensor)
  • :shots - number of shots simulated
  • :counts - frequency map of outcome strings to counts (keys are binary strings like "01")

Helpers on the struct: Qx.SimulationResult.most_frequent/1, Qx.SimulationResult.outcomes/1, Qx.SimulationResult.probability/2.

Examples

iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> %Qx.SimulationResult{shots: shots} = Qx.run(qc)
iex> shots
1024

# Specify backend at runtime
# Qx.run(qc, backend: {EXLA.Backend, client: :host})

# Backward compatible: pass shots as integer
# Qx.run(qc, 2048)

rx(circuit, qubit, theta)

@spec rx(circuit(), non_neg_integer(), float()) :: circuit()

Applies a rotation around the X-axis.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.create_circuit(1) |> Qx.rx(0, :math.pi/2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

ry(circuit, qubit, theta)

@spec ry(circuit(), non_neg_integer(), float()) :: circuit()

Applies a rotation around the Y-axis.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.create_circuit(1) |> Qx.ry(0, :math.pi/2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

rz(circuit, qubit, theta)

@spec rz(circuit(), non_neg_integer(), float()) :: circuit()

Applies a rotation around the Z-axis.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.create_circuit(1) |> Qx.rz(0, :math.pi/2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

s(circuit, qubit)

@spec s(circuit(), non_neg_integer()) :: circuit()

Applies an S gate (phase gate with π/2 phase).

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.s(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

sdg(circuit, qubit)

@spec sdg(circuit(), non_neg_integer()) :: circuit()

Applies an S† (S-dagger) gate (-π/2 phase on |1⟩).

Rotates the Y-basis back to the X-basis. The inverse of s/2.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.sdg(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

steps(circuit, opts \\ [])

@spec steps(
  circuit(),
  keyword()
) :: Enumerable.t()

Steps through a circuit: a lazy stream of Qx.Step structs, one per executed operation.

Each step carries the operation just applied, the statevector right after it, its probabilities, and the classical bits so far. Print a step to get a readable one-liner; call Qx.Step.show/1 for the full display map.

Unlike get_state/2, stepping works on circuits with mid-circuit measurement and c_if, so you can walk through teleportation:

Qx.create_circuit(3, 3)
|> Qx.x(0)                                # the state to teleport
|> Qx.h(1)
|> Qx.cx(1, 2)                            # Bell pair
|> Qx.cx(0, 1)
|> Qx.h(0)                                # Bell measurement basis
|> Qx.measure(0, 0)
|> Qx.measure(1, 1)
|> Qx.c_if(1, 1, fn c -> Qx.x(c, 2) end)  # corrections
|> Qx.c_if(0, 1, fn c -> Qx.z(c, 2) end)
|> Qx.measure(2, 2)
|> Qx.steps()
|> Enum.to_list()
# one readable line per step, e.g.
# #Qx.Step<5: measure q0 → c0 ⇒ 0.707|010⟩ + 0.707|011⟩  cbits: [0, 0, 0]>

Measurement steps show the collapsed state and record the outcome in classical_bits; each gate inside a taken c_if block yields its own step, and a block that doesn't run yields one step flagged :not_taken.

One trajectory at a time

A circuit with measurements is stochastic. Each materialisation of the stream samples one fresh trajectory, so two Enum.to_list/1 calls can collapse differently, and a single trajectory is a different thing from the 1024-shot ensemble run/2 reports. Pass seed: when you need the same trajectory every time (slides, doctests, regression tests). Seeding never touches your process's :rand state.

One caveat for measure_x/3 and measure_y/3: they lower to basis-change gates plus a Z-measurement, and the post-measurement state deliberately stays Z-aligned. Mid-circuit that means a step shows |1⟩ where the math says |−⟩. See Qx.Operations.measure_x/3.

Options

  • :seed - integer; reproduces the trajectory (default: fresh entropy per materialisation)
  • :backend - Nx backend, same pass-through as run/2
  • :renormalize - same contract as run/2 (default: false)

Examples

iex> qc = Qx.create_circuit(2) |> Qx.h(0) |> Qx.cx(0, 1)
iex> steps = qc |> Qx.steps() |> Enum.to_list()
iex> Enum.map(steps, & &1.operation)
[{:h, [0], []}, {:cx, [0, 1], []}]
iex> steps |> List.last() |> Qx.Step.show() |> Map.get(:state)
"0.707|00⟩ + 0.707|11⟩"

superposition(num_qubits \\ 1)

@spec superposition(pos_integer()) :: circuit()

Creates an n-qubit equal-superposition circuit (Hadamard on every qubit). Default is 1 qubit. See Qx.Patterns.superposition_circuit/1.

Examples

iex> sup_circuit = Qx.superposition()
iex> sup_circuit.num_qubits
1

iex> sup_circuit = Qx.superposition(3)
iex> length(Qx.QuantumCircuit.get_instructions(sup_circuit))
3

swap(circuit, qubit_a, qubit_b)

@spec swap(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()

Applies a SWAP gate, exchanging the quantum states of two qubits.

Both qubits are treated symmetrically — there is no control/target distinction.

Parameters

  • circuit - Quantum circuit
  • qubit_a - Index of the first qubit
  • qubit_b - Index of the second qubit

Examples

iex> qc = Qx.create_circuit(2) |> Qx.swap(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

t(circuit, qubit)

@spec t(circuit(), non_neg_integer()) :: circuit()

Applies a T gate (phase gate with π/4 phase).

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.t(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

tap_circuit(circuit, fun)

@spec tap_circuit(circuit(), (circuit() -> any())) :: circuit()

Inspects the circuit without breaking the pipeline.

See Qx.Operations.tap_circuit/2 for full documentation.

Examples

# Inspect instructions while building circuit
circuit = Qx.create_circuit(2)
  |> Qx.h(0)
  |> Qx.tap_circuit(fn c -> IO.puts("Gates: #{length(c.instructions)}") end)
  |> Qx.cx(0, 1)

tap_probabilities(circuit, fun)

@spec tap_probabilities(circuit(), (Nx.Tensor.t() -> any())) :: circuit()

Inspects measurement probabilities without breaking the pipeline.

See Qx.Operations.tap_probabilities/2 for full documentation.

Examples

# Inspect probabilities while building circuit
circuit = Qx.create_circuit(2)
  |> Qx.h(0)
  |> Qx.tap_probabilities(fn p -> IO.puts("Probs: #{inspect(Nx.shape(p))}") end)
  |> Qx.cx(0, 1)

tap_state(circuit, fun)

@spec tap_state(circuit(), (Nx.Tensor.t() -> any())) :: circuit()

Inspects the current quantum state without breaking the pipeline.

See Qx.Operations.tap_state/2 for full documentation.

Examples

# Inspect quantum state while building circuit
circuit = Qx.create_circuit(1)
  |> Qx.h(0)
  |> Qx.tap_state(fn s -> IO.puts("State shape: #{inspect(Nx.shape(s))}") end)
  |> Qx.z(0)

u(circuit, qubit, theta, phi, lambda)

@spec u(circuit(), non_neg_integer(), number(), number(), number()) :: circuit()

Applies the general single-qubit unitary gate U(θ,φ,λ).

U(θ,φ,λ) = [[cos(θ/2), -e^(iλ)·sin(θ/2) ],

         [e^()·sin(θ/2),  e^(i(φ+λ))·cos(θ/2) ]]

Follows the OpenQASM 3.0 specification built-in U gate / Qiskit qiskit.circuit.library.UGate convention.

Decomposition identity: U(θ,φ,λ) = RZ(φ)·RY(θ)·RZ(λ) up to the global phase e^{i(φ+λ)/2}. For the X/H/I/Y special cases the result is exact — Qiskit's UGate carries no extra global phase.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index (0-based)
  • theta (θ) - Polar rotation angle in radians
  • phi (φ) - Phase angle in radians
  • lambda (λ) - Phase angle in radians

Examples

iex> qc = Qx.create_circuit(1) |> Qx.u(0, :math.pi(), 0, :math.pi())
iex> [{:u, [0], params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> length(params)
3

Raises

version()

@spec version() :: String.t()

Returns version information for the Qx library.

Examples

iex> version = Qx.version()
iex> is_binary(version)
true

x(circuit, qubit)

@spec x(circuit(), non_neg_integer()) :: circuit()

Applies a Pauli-X gate (bit flip) to the specified qubit.

Flips |0⟩ ↔ |1⟩

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.x(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

x_all(circuit)

@spec x_all(circuit()) :: circuit()

Applies a Pauli-X gate to every qubit in the circuit.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.x_all()
iex> length(Qx.QuantumCircuit.get_instructions(qc))
2

x_all(circuit, qubits)

@spec x_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Applies a Pauli-X gate to every qubit in the given list or range. See Qx.Patterns.x_all/2.

y(circuit, qubit)

@spec y(circuit(), non_neg_integer()) :: circuit()

Applies a Pauli-Y gate to the specified qubit.

Combines bit flip and phase flip transformations.

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.y(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

y_all(circuit)

@spec y_all(circuit()) :: circuit()

Applies a Pauli-Y gate to every qubit in the circuit.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.y_all()
iex> length(Qx.QuantumCircuit.get_instructions(qc))
2

y_all(circuit, qubits)

@spec y_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Applies a Pauli-Y gate to every qubit in the given list or range. See Qx.Patterns.y_all/2.

z(circuit, qubit)

@spec z(circuit(), non_neg_integer()) :: circuit()

Applies a Pauli-Z gate (phase flip) to the specified qubit.

Leaves |0⟩ unchanged, applies -1 phase to |1⟩

Parameters

  • circuit - Quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.create_circuit(1) |> Qx.z(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1

Raises

z_all(circuit)

@spec z_all(circuit()) :: circuit()

Applies a Pauli-Z gate to every qubit in the circuit.

Examples

iex> qc = Qx.create_circuit(2) |> Qx.z_all()
iex> length(Qx.QuantumCircuit.get_instructions(qc))
2

z_all(circuit, qubits)

@spec z_all(circuit(), Qx.Patterns.qubits()) :: circuit()

Applies a Pauli-Z gate to every qubit in the given list or range. See Qx.Patterns.z_all/2.