Qx.Operations (Qx - Quantum Computing Simulator v0.5.0)

View Source

Quantum gate operations for quantum circuits.

This module provides functions for applying quantum gates to quantum circuits, including single-qubit gates (H, X, Y, Z), two-qubit gates (CNOT), and three-qubit gates (CCNOT/Toffoli).

Summary

Functions

Adds a barrier to the circuit for visualization purposes.

Applies gates conditionally based on a classical bit value.

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

Applies a controlled-X (CNOT) gate.

Applies a controlled-Z (CZ) gate.

Applies a Hadamard gate to the specified qubit.

Adds a measurement operation to the circuit.

Applies a phase gate with the specified phase.

Applies a rotation around the X-axis by the specified angle.

Applies a rotation around the Y-axis by the specified angle.

Applies a rotation around the Z-axis by the specified angle.

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

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 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.

Functions

barrier(circuit, qubits)

Adds a barrier to the circuit for visualization purposes.

Barriers are used to group operations and improve circuit readability. They do not affect the quantum state.

Parameters

  • circuit - The quantum circuit
  • qubits - List of qubit indices the barrier spans

Examples

iex> qc = Qx.QuantumCircuit.new(3, 0)
iex> qc = Qx.Operations.barrier(qc, [0, 1, 2])
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:barrier, [0, 1, 2]}

c_if(circuit, classical_bit, value, gate_fn)

Applies gates conditionally based on a classical bit value.

The conditional block executes during simulation only if the specified classical bit equals the given value at runtime.

Parameters

  • circuit - The quantum circuit
  • classical_bit - Classical bit index to check (0-based)
  • value - Value to compare (0 or 1)
  • gate_fn - Function that applies gates: (circuit -> circuit)

Examples

iex> qc = Qx.QuantumCircuit.new(2, 2)
iex> qc = qc |> Qx.Operations.h(0) |> Qx.Operations.measure(0, 0)
iex> qc = Qx.Operations.c_if(qc, 0, 1, fn c -> Qx.Operations.x(c, 1) end)
iex> instructions = Qx.QuantumCircuit.get_instructions(qc)
iex> length(instructions)
2

Constraints

  • Classical bit must be valid for the circuit
  • Value must be 0 or 1
  • Gates in conditional block cannot contain measurements
  • No nesting of conditional blocks

ccx(circuit, control1, control2, target)

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

The CCNOT gate flips the target qubit if and only if both control qubits are |1⟩.

Parameters

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

Examples

iex> qc = Qx.QuantumCircuit.new(3, 0)
iex> qc = Qx.Operations.ccx(qc, 0, 1, 2)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:ccx, [0, 1, 2]}

cx(circuit, control_qubit, target_qubit)

Applies a controlled-X (CNOT) gate.

The CNOT gate flips the target qubit if and only if the control qubit is |1⟩.

Parameters

  • circuit - The quantum circuit
  • control_qubit - Control qubit index
  • target_qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.cx(qc, 0, 1)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:cx, [0, 1]}

cz(circuit, control_qubit, target_qubit)

Applies a controlled-Z (CZ) gate.

The CZ gate applies a phase of -1 if and only if both qubits are |1⟩.

Parameters

  • circuit - The quantum circuit
  • control_qubit - Control qubit index
  • target_qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.cz(qc, 0, 1)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:cz, [0, 1]}

h(circuit, qubit)

Applies a Hadamard gate to the specified qubit.

The Hadamard gate creates superposition, transforming |0⟩ to (|0⟩ + |1⟩)/√2 and |1⟩ to (|0⟩ - |1⟩)/√2.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.h(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:h, [0]}

measure(circuit, qubit, classical_bit)

Adds a measurement operation to the circuit.

Parameters

  • circuit - The quantum circuit
  • qubit - Qubit index to measure
  • classical_bit - Classical bit index to store the result

Examples

iex> qc = Qx.QuantumCircuit.new(2, 2)
iex> qc = Qx.Operations.measure(qc, 0, 0)
iex> [{qubit, classical_bit}] = Qx.QuantumCircuit.get_measurements(qc)
iex> {qubit, classical_bit}
{0, 0}

phase(circuit, qubit, phi)

Applies a phase gate with the specified phase.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index
  • phi - Phase angle in radians

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0)
iex> qc = Qx.Operations.phase(qc, 0, :math.pi/4)
iex> [{gate, qubits, params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits, length(params)}
{:phase, [0], 1}

rx(circuit, qubit, theta)

Applies a rotation around the X-axis by the specified angle.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0)
iex> qc = Qx.Operations.rx(qc, 0, :math.pi/2)
iex> [{gate, qubits, params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits, length(params)}
{:rx, [0], 1}

ry(circuit, qubit, theta)

Applies a rotation around the Y-axis by the specified angle.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0)
iex> qc = Qx.Operations.ry(qc, 0, :math.pi/2)
iex> [{gate, qubits, params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits, length(params)}
{:ry, [0], 1}

rz(circuit, qubit, theta)

Applies a rotation around the Z-axis by the specified angle.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index
  • theta - Rotation angle in radians

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0) iex> qc = Qx.Operations.rz(qc, 0, :math.pi/2) iex> [{gate, qubits, params}] = Qx.QuantumCircuit.get_instructions(qc) iex> {gate, qubits, length(params)}

s(circuit, qubit)

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

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0)
iex> qc = Qx.Operations.s(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:s, [0]}

t(circuit, qubit)

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

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(1, 0)
iex> qc = Qx.Operations.t(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:t, [0]}

tap_circuit(circuit, fun)

@spec tap_circuit(Qx.QuantumCircuit.t(), (Qx.QuantumCircuit.t() -> any())) ::
  Qx.QuantumCircuit.t()

Inspects the circuit without breaking the pipeline.

The provided function receives the circuit and can perform any side-effect (logging, printing, assertions), but the return value is ignored and the original circuit is returned.

Parameters

  • circuit - The quantum circuit
  • fun - Function to execute: (circuit -> any())

Examples

iex> circuit = Qx.QuantumCircuit.new(2, 0)
...> |> Qx.Operations.h(0)
...> |> Qx.Operations.tap_circuit(&IO.inspect(&1.instructions, label: "After H"))
...> |> Qx.Operations.cx(0, 1)
After H: [{:h, [0], []}]
%Qx.QuantumCircuit{...}

# Create circuit and inspect depth/qubits
circuit = Qx.QuantumCircuit.new(3, 0)
  |> Qx.Operations.h(0)
  |> Qx.Operations.tap_circuit(fn circ ->
       IO.puts("Depth: #{Qx.QuantumCircuit.depth(circ)}")
       IO.puts("Qubits: #{circ.num_qubits}")
     end)
  |> Qx.Operations.x(1)
# Outputs:
# Depth: 1
# Qubits: 3

See Also

tap_probabilities(circuit, fun)

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

Inspects measurement probabilities without breaking the pipeline.

Convenience function that computes probabilities and passes them to your inspection function.

Parameters

  • circuit - The quantum circuit
  • fun - Function to execute: (Nx.Tensor.t() -> any())

Examples

iex> circuit = Qx.QuantumCircuit.new(2, 2)
...> |> Qx.Operations.h(0)
...> |> Qx.Operations.cx(0, 1)
...> |> Qx.Operations.tap_probabilities(&IO.inspect/1)
...> |> Qx.Operations.measure(0, 0)
#Nx.Tensor<
  f32[4]
  [0.5, 0.0, 0.0, 0.5]
>
%Qx.QuantumCircuit{...}

# Create circuit and inspect probabilities
circuit = Qx.QuantumCircuit.new(1, 0)
  |> Qx.Operations.h(0)
  |> Qx.Operations.tap_probabilities(fn probs ->
       prob_list = Nx.to_list(probs)
       IO.puts("P(|0⟩) = #{Enum.at(prob_list, 0)}")
       IO.puts("P(|1⟩) = #{Enum.at(prob_list, 1)}")
     end)
# Outputs:
# P(|0⟩) = 0.5
# P(|1⟩) = 0.5

See Also

tap_state(circuit, fun)

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

Inspects the current quantum state without breaking the pipeline.

Important: This executes all instructions so far to get the current state. Use sparingly in performance-critical code.

Parameters

  • circuit - The quantum circuit
  • fun - Function to execute: (Nx.Tensor.t() -> any())

Examples

iex> circuit = Qx.QuantumCircuit.new(1, 0)
...> |> Qx.Operations.h(0)
...> |> Qx.Operations.tap_state(&IO.inspect(&1, label: "After H gate"))
...> |> Qx.Operations.z(0)
After H gate: #Nx.Tensor<...>
%Qx.QuantumCircuit{...}

iex> circuit = Qx.QuantumCircuit.new(2, 0)
...> |> Qx.Operations.h(0)
...> |> Qx.Operations.tap_state(fn state ->
...>      probs = Qx.Math.probabilities(state)
...>      IO.inspect(Nx.to_list(probs), label: "Probabilities")
...>    end)
...> |> Qx.Operations.cx(0, 1)
Probabilities: [0.5, 0.5, 0.0, 0.0]
%Qx.QuantumCircuit{...}

See Also

x(circuit, qubit)

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

The X gate flips |0⟩ to |1⟩ and |1⟩ to |0⟩.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.x(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:x, [0]}

y(circuit, qubit)

Applies a Pauli-Y gate to the specified qubit.

The Y gate applies both bit flip and phase flip transformations.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.y(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:y, [0]}

z(circuit, qubit)

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

The Z gate leaves |0⟩ unchanged and applies a phase of -1 to |1⟩.

Parameters

  • circuit - The quantum circuit
  • qubit - Target qubit index

Examples

iex> qc = Qx.QuantumCircuit.new(2, 0)
iex> qc = Qx.Operations.z(qc, 0)
iex> [{gate, qubits, _params}] = Qx.QuantumCircuit.get_instructions(qc)
iex> {gate, qubits}
{:z, [0]}