Backends
The backends module provides concrete execution engines for running functionals (quantum processes).
Currently, three backends are supported:
Note
Backends other than QiliSim are optional; to install one, include its extra when installing QILISDK:
pip install qilisdk[<backend_name>]
For more information check the Installation page.
Once installed, any primitive functional can be executed by passing it to the backend’s execute() method:
from qilisdk.backends import CudaBackend
from qilisdk.functionals import Sampling
# ... build your circuit and Sampling functional ...
results = CudaBackend().execute(sampling_functional)
print(results)
Architecture Overview
All concrete backends subclass Backend, which centralizes the execution workflow used
across the SDK. The execute() dispatches a primitive functional (e.g. Sampling or TimeEvolution)
to the appropriate simulation routine and returns the functional-specific result object (see the Functionals chapter). The Execute method is also used to optimize variational programs via repeated calls to
the underlying parameterized primitive functional.
Backends register handlers for the functionals they support. If a functional is not implemented, execute() raises
NotImplementedError to surface the mismatch early.
Hardware & Dependencies
Installing a backend extra pulls in the library stack required for that simulator. GPU backends also expect compatible drivers to be present on the system.
Backend |
Extra |
Key dependency |
Notes |
|---|---|---|---|
|
None |
CPU based; no special hardware needs. |
|
|
Requires NVIDIA hardware with recent drivers. |
||
|
CPU based; no special hardware needs. |
Functional Support
The table below summarizes which primitive functionals each backend can execute.
Backend |
Sampling |
TimeEvolution |
VariationalProgram |
|---|---|---|---|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
|
✓ |
✓ |
✓ |
QiliSim Backend
The QiliSim backend is a CPU-based simulator developed by Qilimanjaro and written in C++, providing efficient simulation of both digital and analog quantum functionals. It is designed for ease of use and does not require any special hardware or dependencies. There is no need to install QiliSim separately, as it is included with the core QILISDK installation.
Initialization
from qilisdk.backends import QiliSim
backend = QiliSim()
Capabilities
Sampling of digital circuits with efficient state-vector simulation.
TimeEvolution driven by
Schedule.Compatible with
VariationalProgramfor classical optimization loops.
Parameters
evolution_method(str, optional): The method for simulating time evolution. Options include ‘direct’, ‘arnoldi’ and ‘integrate’. Default is ‘integrate’.arnoldi_dim(int, optional): Dimension of the Krylov subspace for the Arnoldi method. Default is 10.num_arnoldi_substeps(int, optional): Number of substeps for the Arnoldi method per timestep. Default is 1.num_integration_substeps(int, optional): Number of integration steps for the integrate method per timestep. Default is 2.monte_carlo(bool, optional): Whether to use the Monte Carlo wavefunction method. Default is False.num_monte_carlo_trajectories(int, optional): Number of trajectories to simulate when using the Monte Carlo method. Default is 100.
Example
import numpy as np
from qilisdk.digital import Circuit, H, CNOT
from qilisdk.backends import QiliSim
from qilisdk.functionals import Sampling
# Build a simple circuit
circuit = Circuit(5)
circuit.add(H(0))
circuit.add(CNOT(0, 1))
# Create Sampling functional
sampling = Sampling(circuit=circuit, nshots=500)
# Execute with the QiliSim backend
qilisim_backend = QiliSim()
result = qilisim_backend.execute(sampling)
print(result.samples)
CUDA Backend
The CUDA backend leverages NVIDIA GPUs via the cuda-quantum framework for both digital and analog simulations.
When no compatible GPU is detected it automatically falls back to cpu-based targets, so you can prototype on
commodity hardware before moving to accelerated machines.
Installation
pip install qilisdk[cuda]
Initialization
from qilisdk.backends import CudaBackend, CudaSamplingMethod
backend = CudaBackend(
sampling_method=CudaSamplingMethod.STATE_VECTOR
)
Capabilities
Digital circuits through
Sampling.Analog dynamics for
TimeEvolution, powered bycudaq.evolve.Hybrid execution when paired with
VariationalProgram.
Sampling methods
STATE_VECTOR: Full state-vector simulation (switches to CPU if a GPU is unavailable).TENSOR_NETWORK: Tensor-network contraction, suited for shallow yet wide circuits.MATRIX_PRODUCT_STATE: Matrix-product-state simulation for low-entanglement workloads.
Example
import numpy as np
from qilisdk.digital import Circuit, H, RX, CNOT
from qilisdk.backends import CudaBackend, CudaSamplingMethod
from qilisdk.functionals import Sampling
# Build a simple circuit
circuit = Circuit(2)
circuit.add(RX(0, theta=np.pi / 4))
circuit.add(H(0))
circuit.add(CNOT(0, 1))
# Create Sampling functional
sampling = Sampling(circuit=circuit, nshots=500)
# Execute with the chosen sampling method (GPU if available)
cuda_backend = CudaBackend(sampling_method=CudaSamplingMethod.STATE_VECTOR)
result = cuda_backend.execute(sampling)
print(result.samples)
Output
{'11': 237, '00': 263}
Qutip Backend
The Qutip backend uses the qutip library for simulation on CPU, supporting both digital and analog functionals.
It is the most lightweight option, ideal for local development or environments without NVIDIA GPUs.
Installation
pip install qilisdk[qutip]
Initialization
from qilisdk.backends import QutipBackend
backend = QutipBackend()
Capabilities
Sampling of digital circuits via QuTiP’s state-vector solvers.
TimeEvolution driven by
Schedule.Compatible with
VariationalProgramfor fully classical optimization loops.
Example
import numpy as np
from qilisdk.analog import Schedule, X, Z, Y
from qilisdk.core import ket, tensor_prod
from qilisdk.backends import QutipBackend
from qilisdk.core.interpolator import Interpolation
from qilisdk.functionals import TimeEvolution
# Define total time and timestep
T = 10.0
dt = 0.5
nqubits = 1
# Define Hamiltonians
Hx = sum(X(i) for i in range(nqubits))
Hz = sum(Z(i) for i in range(nqubits))
# Build a time‑dependent schedule
schedule = Schedule(
hamiltonians={"driver": Hx, "problem": Hz},
coefficients={
"driver": {(0.0, T): lambda t: 1 - t / T},
"problem": {(0.0, T): lambda t: t / T},
},
dt=dt,
interpolation=Interpolation.LINEAR,
)
# Prepare an equal superposition initial state
initial_state = tensor_prod([(ket(0) - ket(1)).unit() for _ in range(nqubits)]).unit()
# Create the TimeEvolution functional
time_evolution = TimeEvolution(
schedule=schedule,
initial_state=initial_state,
observables=[Z(0), X(0), Y(0)],
nshots=100,
store_intermediate_results=False,
)
# Execute on Qutip backend and inspect results
backend = QutipBackend()
results = backend.execute(time_evolution)
print(results)
Output
TimeEvolutionResult(
final_expected_values=array([-0.99388223, 0.0467696 , -0.10005353]),
final_state=QTensor(shape=2x1, nnz=2, format='csr')
[[0.05506547-0.00516502j]
[0.3364973 -0.94005887j]]
)