Creating a Circuit
QniverseCircuit is the core circuit-construction class in the Qniverse SDK.
It is responsible for defining quantum circuits, managing registers, applying
gates and modules, visualizing circuits, and executing them on supported
backends.
A circuit acts as a container that holds all quantum and classical components, along with the sequence of quantum operations to be executed.
Before adding qubits, classical bits, or gates, you must initialize an empty QniverseCircuit instance.
from Qniverse.QniverseCircuit import *
qc = QniverseCircuit()
When the circuit is first created, it contains no quantum or classical elements. No qubits are allocated automatically, and no classical bits are available to store measurement results.
This means that quantum and classical registers must be explicitly defined by the user before any gates or measurements can be applied.
Registers
Registers define the storage resources of a quantum circuit. They specify how many quantum bits (qubits) and classical bits (clbits) are available and provide a structured way to reference individual wires within the circuit.
QuantumRegister
A QuantumRegister represents a collection of qubits that will participate
in quantum operations.
It holds qubits size with a prefix. It attaches to a circuit upon add_register.
from Qniverse.QniverseCircuit import QuantumRegister
q = QuantumRegister(5, 'q')
In this example, a quantum register named q is created with 5 qubits.
Each qubit is indexed starting from 0 and can later be accessed individually
once the register is attached to a circuit.
ClassicalRegister
A ClassicalRegister represents a collection of classical bits used to store
measurement outcomes and other classical data.
It holds classical bits size with a prefix. It attaches to a circuit upon add_register.
from Qniverse.QniverseCircuit import ClassicalRegister
c = ClassicalRegister(3, 'c')
This creates a classical register named c containing 3 classical bits. Classical registers are typically used as targets for measurement operations, where quantum state information is converted into classical values.
A valid register name:
Must contain alphabetic characters only
Must not conflict with Gates or Python reserved keywords
Must be unique within a given circuit
Keep register sizes positive integers
Adding Registers to a Circuit
Registers become active only after being explicitly added to a QniverseCircuit. Until then, they exist as standalone objects and cannot be used in quantum operations.
qc.add_register(q)
qc.add_register(c)
After registers are attached to the circuit, individual qubits and classical bits can be accessed using dot notation followed by index-based addressing.
# You can now do:
qc.add_gate(x,q[0])
Warning
Quantum gates and measurements can only be applied to wires that belong to a circuit. Attempting to use qubits or classical bits that have not been added to a QniverseCircuit will result in an error.
Applying Gates
Use add_gate to insert a quantum gate.
Description : Adds a standard quantum gate to the specified qubits in the circuit.
Syntax :
qc.add_gate(gate, *indices)
Parameters :
gate* (Gate object) — The name of the quantum gate (e.g., H, X, CX).
- indices* (objects) - The indices of the target bits. A mix of Qubit and/or Clbit objects depending on the gate.
For standard gates (H, X, RX, etc.) → all indices must be Qubit objects.
For the MEASURE gate → exactly 2 indices: first a Qubit, then a Clbit.
Single-Qubit Gates
from Qniverse.gates import *
qc.add_gate(h, q[0])
qc.add_gate(y, q[1])
Validations performed:
Wire must belong to the circuit
Duplicate wires are rejected
Controlled Gates
from Qniverse.gates import *
qc.add_gate(ch, q[3], q[2])
qc.add_gate(cx, q[1], q[4])
The last qubit is always the target.
Multi-Controlled Gates
from Qniverse.gates import *
qc.add_gate(ccx, q[1], q[2], q[4])
qc.add_gate(c3x, q[1], q[2], q[3], q[4])
Parameterized Gates
from Qniverse.gates import *
import numpy as np
qc.add_gate(rx(np.pi / 2), q[1])
qc.add_gate(u3(np.pi / 2, 0, 0), q[2])
Conditional Gate
Conditional gate operations allow quantum gates to be applied based on the measured state of a classical register. This is a core feature of mid-circuit measurement and classical feedback, enabling dynamic quantum circuits where subsequent gate operations depend on prior measurement outcomes.
add_conditional_gate : Adds a classically-controlled quantum gate to the circuit. The gate is only applied during simulation if the value of the specified classical register matches the given condition.
syntax :
qc.add_conditional_gate(classical_reg, condition, gate, *qubits)
parameters :
classical_reg* (ClassicalRegister object) : The classical register whose measured value is checked as the condition.
condition* (int or str(binary string)) : The trigger value. Accepts a non-negative integer (e.g. 3) or a binary string matching the register size (e.g. “011”).
gate* (Gate object) : The quantum gate instance to apply conditionally. Cannot be MEASURE, BARRIER, or RESET.
qubits* (Qubit object) : One or more target qubits for the gate. Must belong to this circuit.
Example:
# Conditionally apply H to q[1] if c == 1
qc.add_conditional_gate(c, 1, h, q[1])
(or)
# Conditionally apply X to q[1] if c == '011'
qc.add_conditional_gate(c, "011", x, q[1])
Measurement
Measurement maps a qubit to a classical bit. It acts as a bridge between quantum and classical systems, determining probabilities based on the amplitudes of the quantum state.
A measurement operation incorporated in the same way as any other quantum gate within the circuit. The first argument specifies the qubit to be measured, and the second argument designates the classical bit in which the measurement outcome will be stored.
from Qniverse.gates import *
qc.add_gate(measure, q[1], c[2])
Rules:
Measure gate requires Quantum and classical bits
Both wires must belong to the circuit
measure_all() automatically measures all qubits in the circuit, providing a convenient alternative to explicitly adding individual MEASURE gates.
The mapping of measurement results to classical bits depends on how the classical registers are defined. It first assigns results to any existing classical registers,such as a user-defined classical register, and then creates a meas register for any remaining qubits that do not yet have corresponding classical targets.
# Assume qc has 3 qubits and a classical register c with 2 bits
qc.measure_all()
# Resulting mapping:
# q[0] -> c[0]
# q[1] -> c[1]
# q[2] -> meas[0] # created automatically
Modules
Modules represent a simple abstraction for multi-qubit quantum operations, such as the Quantum Fourier Transform (QFT) and its inverse (IQFT). It provides a flexible Module class for naming and grouping parameterized gates, also two predefined instances, qft and iqft, for ease of use across the library.
Use add_module to apply composite circuits.
Syntax :
add_module(module,*qubits)
parameters :
module* (object) — The module object to be integrated.
qubits* (Qubit object) — One or more target qubits, ex:q[0],q[1] .
from Qniverse.module import *
qc.add_module(qft, q[1], q[2], q[3])
qc.add_module(iqft, q[1], q[3])
Initialization
Initialization is the process of setting a qubit to a specific quantum state before running the circuit. Instead of being just 0 or 1, a qubit can be in a combination of both states at the same time. This is called superposition.
A general single-qubit state is written as:
∣ψ⟩=α∣0⟩+β∣1⟩
Here:
The values must satisfy the normalization rule: ∣α∣2+∣β∣2=1. This condition ensures that the total probability is equal to 1.
initialize() function allows you to manually prepare a qubit in any valid single-qubit state by specifying alpha and beta.
Syntax:
initialize(qubit_name, alpha, beta)
parameters :
qubit_name* (qubit object) : The object of the target qubit.
alpha* , beta* (complex) : The amplitudes of a single-qubit quantum state when you use the Initialize.
import numpy as np
qc.initialize(
q[2],
alpha=complex(np.sqrt(0.5), 0),
beta=complex(0, np.sqrt(0.5))
)
This prepares the qubit in an equal superposition of |0⟩ and |1⟩.
If all qubits in the circuit need to be initialized to the same state, use initialize_all(alpha, beta).
This method sets every qubit across all quantum registers to the specified state.
Syntax:
initialize_all(alpha, beta)
parameters :
alpha* , beta* (complex) : The amplitudes of a single-qubit quantum state when you use the Initialize.
import numpy as np
qc.initialize_all(
alpha=complex(np.sqrt(0.5), 0),
beta=complex(0, np.sqrt(0.5))
)
Note
Alpha and Beta must be complex numbers, You must pass values using Python’s complex() type.
Circuit Visualization
qasm( ): Generates OpenQASM 2.0 code via the transpiler.
draw("filename") : Renders the circuit diagram as an SVG.
download_circuit(filename="circuit.svg") : Saves the circuit drawing directly to a file. Useful for very large circuits that cannot be displayed in Jupyter.
Parameters: filename (str) # optional
# Generate and print QASM
qc.qasm()
# Draw circuit
qc.draw("circuit.svg")
# download circuit
qc.download_circuit()
Plot Histogram
plot_histogram( ): Used to visualize quantum circuit measurement results as a histogram inside a Jupyter Notebook environment.
The function can display the raw measurement counts also the probability percentages of measured states.
Syntax:
plot_histogram(result,probability:bool)
parameters :
result* (dict): The quantum execution result returned by
fetch_job_result()containing measurement probabilities/counts and total shots.- probability (bool, optional)Controls histogram display mode.
False → Displays raw counts.
True → Displays probability percentages.
Default value is False.
result = fetch_job_result(token,job)
plot_histogram(result)
#or
plot_histogram(result,probability=True)
download_histogram( ): Used to save the quantum measurement histogram as an image file.
This function is useful when:
The histogram contains many quantum states.
The graph becomes too wide inside the notebook output cell.
You want to inspect the histogram more clearly.
You want to save the plot for reports or analysis.
If the histogram is too large and difficult to view using plot_histogram(), it is recommended to use download_histogram() to save the image and inspect it separately.
Syntax:
download_histogram(result,probability:bool,filename)
parameters :
result* (dict): The quantum execution result returned by
fetch_job_result()containing measurement probabilities/counts and total shots.probability (bool) # optional : Controls histogram display mode.
filename (str) # optional : Name of the output file. The file format is determined automatically from the file extension. Supported formats depend on matplotlib and commonly includes .png, .svg, .jpg .Default value is
histogram.png.
result = fetch_job_result(token,job)
download_histogram(result,probability=True,filename="histogram.png")