Qx (Qx - Quantum Computing Simulator v0.5.0)
View SourceQx - 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.
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.Qubit- Functions for qubit creation and manipulationQx.QuantumCircuit- Quantum circuit creation and managementQx.Operations- Quantum gate operationsQx.Simulation- Circuit execution and simulationQx.Draw- Visualization of resultsQx.Math- Core mathematical functions for quantum mechanicsQx.Export.OpenQASM- Export circuits to OpenQASM for real quantum hardware
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
Creates a Bell state circuit (maximally entangled two-qubit state).
Applies gates conditionally based on a classical bit value.
Applies a controlled-controlled-X (CCNOT/Toffoli) 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-X (CNOT) gate.
Applies a controlled-Z (CZ) gate.
Visualizes probability distribution from simulation results.
Visualizes a single qubit state on the Bloch sphere.
Visualizes measurement counts as a bar chart.
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 a GHZ state circuit (three-qubit entangled state).
Applies a Hadamard gate to the specified qubit.
Creates a histogram from a raw probability tensor.
Adds a measurement operation to the circuit.
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).
Creates a superposition state on a single qubit.
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.
Returns version information for the Qx library.
Applies a Pauli-X gate (bit flip) to the specified qubit.
Applies a Pauli-Y gate to the specified qubit.
Applies a Pauli-Z gate (phase flip) to the specified qubit.
Types
@type circuit() :: Qx.QuantumCircuit.t()
@type simulation_result() :: Qx.Simulation.simulation_result()
Functions
@spec bell_state() :: circuit()
Creates a Bell state circuit (maximally entangled two-qubit state).
Returns a circuit that prepares the |Φ+⟩ = (|00⟩ + |11⟩)/√2 Bell state.
Examples
iex> bell_circuit = Qx.bell_state()
iex> bell_circuit.num_qubits
2
@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 circuitclassical_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))
2See Also
- OpenQASM 3.0 if-statements for hardware compatibility
- Quantum teleportation example in documentation
@spec ccx(circuit(), non_neg_integer(), non_neg_integer(), non_neg_integer()) :: circuit()
Applies a controlled-controlled-X (CCNOT/Toffoli) gate.
Flips target qubit if and only if both control qubits are |1⟩
Parameters
circuit- Quantum circuitcontrol1- First control qubit indexcontrol2- Second control qubit indextarget- Target qubit index
Examples
iex> qc = Qx.create_circuit(3) |> Qx.ccx(0, 1, 2)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1
@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
0Raises
FunctionClauseError- If num_qubits <= 0
@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
2Raises
FunctionClauseError- If num_qubits <= 0 or num_classical_bits < 0
@spec cx(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()
Applies a controlled-X (CNOT) gate.
Flips target qubit if and only if control qubit is |1⟩
Parameters
circuit- Quantum circuitcontrol_qubit- Control qubit indextarget_qubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(2) |> Qx.cx(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1Raises
FunctionClauseError- If qubit indices are out of range or equal
@spec cz(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()
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 circuitcontrol_qubit- Control qubit indextarget_qubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(2) |> Qx.cz(0, 1)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1
@spec draw( simulation_result(), keyword() ) :: VegaLite.t() | String.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 histogram/2 instead.
Parameters
Options
:format-:vega_lite(default) or:svg:title- Plot title:width- Plot width (default: 400):height- Plot height (default: 300)
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_map(plot) or is_binary(plot)
trueSee Also
histogram/2- For plotting raw probability tensorsdraw_counts/2- For plotting measurement counts
@spec draw_bloch( Nx.Tensor.t(), keyword() ) :: VegaLite.t() | String.t()
Visualizes a single qubit state on the Bloch sphere.
The Bloch sphere provides a geometric representation of a pure qubit state. This visualization is particularly useful for understanding single-qubit gates and state transformations in calculation mode.
Parameters
qubit- Single qubit state tensor (fromQx.Qubit)options- Optional plotting parameters
Options
:format-:vega_lite(default) or:svg:title- Plot title (default: "Bloch Sphere"):size- Sphere size (default: 400)
Examples
# Visualize |0⟩ state
iex> q = Qx.Qubit.new()
iex> plot = Qx.draw_bloch(q)
iex> is_map(plot) or is_binary(plot)
true
# Visualize superposition state
iex> q = Qx.Qubit.new() |> Qx.Qubit.h()
iex> plot = Qx.draw_bloch(q, title: "Superposition State")
iex> is_map(plot) or is_binary(plot)
trueSee Also
Qx.Qubit- Calculation mode for single qubitsdraw_state/2- Display multi-qubit state as table
@spec draw_counts( simulation_result(), keyword() ) :: VegaLite.t() | String.t()
Visualizes measurement counts as a bar chart.
Parameters
result- Simulation result containing measurement dataoptions- Optional plotting parameters
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_map(plot) or is_binary(plot)
true
@spec draw_state( Qx.Register.t() | Nx.Tensor.t(), keyword() ) :: String.t()
Displays a quantum state as a formatted table.
Shows basis states with their amplitudes and probabilities. Useful for inspecting multi-qubit states in calculation mode.
Parameters
register_or_state-Qx.Register.t()or state tensoroptions- Optional display parameters
Options
:format-:text(default) or:html:precision- Decimal places (default: 3):hide_zeros- Hide zero-amplitude states (default: false)
Examples
# Display Bell state
iex> reg = Qx.Register.new(2) |> Qx.Register.h(0) |> Qx.Register.cx(0, 1)
iex> table = Qx.draw_state(reg)
iex> is_binary(table)
true
# Hide zero states
iex> reg = Qx.Register.new(3) |> Qx.Register.h(0)
iex> table = Qx.draw_state(reg, hide_zeros: true)
iex> is_binary(table)
trueSee Also
Qx.Register- Calculation mode for multi-qubit systemsdraw_bloch/2- Bloch sphere visualization for single qubits
@spec get_probabilities( circuit(), keyword() ) :: Nx.Tensor.t()
Gets probability distribution for computational basis states.
Parameters
circuit- Quantum circuitoptions- Optional parameters
Options
:backend- Nx backend to use (e.g.,{EXLA.Backend, client: :host})
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
Qx.MeasurementError- If circuit contains measurements or conditionals
@spec get_state( circuit(), keyword() ) :: Nx.Tensor.t()
Executes a circuit and returns only the final quantum state.
Parameters
circuit- Quantum circuit to executeoptions- Optional parameters
Options
:backend- Nx backend to use (e.g.,{EXLA.Backend, client: :host})
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
Qx.MeasurementError- If circuit contains measurements or conditionals
@spec ghz_state() :: circuit()
Creates a GHZ state circuit (three-qubit entangled state).
Returns a circuit that prepares |GHZ⟩ = (|000⟩ + |111⟩)/√2.
Examples
iex> ghz_circuit = Qx.ghz_state()
iex> ghz_circuit.num_qubits
3
@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 circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1Raises
FunctionClauseError- If qubit index is out of range
@spec histogram( Nx.Tensor.t(), keyword() ) :: VegaLite.t() | String.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/1without 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.histogram(probs)
iex> is_map(hist) or is_binary(hist)
trueSee Also
draw/2- For plotting from simulation resultsget_probabilities/1- To obtain probability tensors
@spec measure(circuit(), non_neg_integer(), non_neg_integer()) :: circuit()
Adds a measurement operation to the circuit.
Parameters
circuit- Quantum circuitqubit- Qubit index to measureclassical_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))
1Raises
FunctionClauseError- If qubit or classical_bit index is out of range
@spec phase(circuit(), non_neg_integer(), float()) :: circuit()
Applies a phase gate with specified phase.
Parameters
circuit- Quantum circuitqubit- Target qubit indexphi- 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
@spec run(circuit(), pos_integer() | keyword()) :: simulation_result()
Executes the quantum circuit and returns simulation results.
Parameters
circuit- Quantum circuit to executeoptions- 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.,{EXLA.Backend, client: :host})
Returns
A map containing:
:probabilities- Probability amplitudes for all states:classical_bits- List of measurement results:state- Final quantum state vector:shots- Number of shots performed:counts- Frequency count of measurement outcomes
Examples
iex> qc = Qx.create_circuit(1) |> Qx.h(0)
iex> result = Qx.run(qc)
iex> is_map(result)
true
iex> Map.has_key?(result, :probabilities)
true
# Specify backend at runtime
# Qx.run(qc, backend: {EXLA.Backend, client: :host})
# Backward compatible: pass shots as integer
# Qx.run(qc, 2048)
@spec rx(circuit(), non_neg_integer(), float()) :: circuit()
Applies a rotation around the X-axis.
Parameters
circuit- Quantum circuitqubit- Target qubit indextheta- 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
@spec ry(circuit(), non_neg_integer(), float()) :: circuit()
Applies a rotation around the Y-axis.
Parameters
circuit- Quantum circuitqubit- Target qubit indextheta- 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
@spec rz(circuit(), non_neg_integer(), float()) :: circuit()
Applies a rotation around the Z-axis.
Parameters
circuit- Quantum circuitqubit- Target qubit indextheta- 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
@spec s(circuit(), non_neg_integer()) :: circuit()
Applies an S gate (phase gate with π/2 phase).
Parameters
circuit- Quantum circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.s(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1
@spec superposition() :: circuit()
Creates a superposition state on a single qubit.
Returns a circuit with a Hadamard gate applied to qubit 0.
Examples
iex> sup_circuit = Qx.superposition()
iex> sup_circuit.num_qubits
1
@spec t(circuit(), non_neg_integer()) :: circuit()
Applies a T gate (phase gate with π/4 phase).
Parameters
circuit- Quantum circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.t(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1
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)
@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)
@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)
@spec version() :: String.t()
Returns version information for the Qx library.
Examples
iex> version = Qx.version()
iex> is_binary(version)
true
@spec x(circuit(), non_neg_integer()) :: circuit()
Applies a Pauli-X gate (bit flip) to the specified qubit.
Flips |0⟩ ↔ |1⟩
Parameters
circuit- Quantum circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.x(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1Raises
FunctionClauseError- If qubit index is out of range
@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 circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.y(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1Raises
FunctionClauseError- If qubit index is out of range
@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 circuitqubit- Target qubit index
Examples
iex> qc = Qx.create_circuit(1) |> Qx.z(0)
iex> length(Qx.QuantumCircuit.get_instructions(qc))
1Raises
FunctionClauseError- If qubit index is out of range