Quick start
CUDA-Q Logical expands CUDA-Q with an open, extensible logical layer for developing and evaluating fault-tolerant quantum applications. It lets you keep a workload fixed while comparing QEC codes and system architectures. This quick start walks through installation and a first logical resource estimate: define the same Bell-pair workload as a CUDA-Q kernel or a standalone logical program, and obtain identical resource counts.
Use a CUDA-Q kernel
Install CUDA-Q, which includes CUDA-Q Logical:
pip install cudaq
Once installed, you can write a quantum program as a CUDA-Q kernel and use CUDA-Q Logical to estimate its resources at different compilation layers. Start with logical-qubit and operation counts; with a QEC realization and device configured, you can also estimate physical qubits and runtime. Use CUDA-Q kernels when you want to build on CUDA-Q’s libraries and infrastructure or estimate an existing CUDA-Q application.
The kernel example below defines a kernel that allocates two
qubits, applies a Hadamard and a controlled-X to prepare a Bell pair, and
measures both qubits in the Z basis. It selects cql.targets.estimator, which
stops at a portable logical representation of the program, then calls
cudaq.estimate(bell_pair) to report the peak logical-qubit count and the
Hadamard and controlled-X action counts:
import cudaq
import cudaq.logical as cql
# %%
# Author the workload as an ordinary CUDA-Q kernel.
@cudaq.kernel
def bell_pair():
qubits = cudaq.qvector(2)
h(qubits[0])
x.ctrl(qubits[0], qubits[1])
mz(qubits)
# %%
# Select the target that stops at a portable logical representation.
cudaq.set_target(cql.targets.estimator)
# %%
# Estimate the kernel. For logical targets, `estimate.resources` reports the
# logical program: gate counts, qubits, and depth. Measurements appear as `mz`
# and qubit preparations as `reset`.
estimate = cudaq.estimate(bell_pair)
resources = estimate.resources
assert resources.num_qubits == 2
assert resources.count("h") == 1
assert resources.count_controls("x", 1) == 1
assert resources.count("mz") == 2
print("Logical Bell-pair resources:")
print(f" peak logical qubits: {resources.num_qubits}")
print(f" logical operations: {resources.count()}")
print(f" logical depth (serial upper bound): {resources.depth}")
print(f" logical gate counts: {resources.to_dict()}")
# %%
# Going deeper: `resources` is a summary of the full logical estimate stored in
# `estimate.annotations["LOGICAL"]`. That annotation also carries details that
# `Resources` cannot express, such as idle sites, discards, synthesis demand,
# and resource requests. Rehydrate it as a typed object when you need them.
logical = cql.estimate.LogicalEstimate.from_annotations(estimate.annotations)
assert logical.logical_qubits_peak == resources.num_qubits
print(f" logical estimate: {logical}")
To run this example, copy the code into bell_kernel.py and run
python3 bell_kernel.py. The example is also available in a checked-out
repository; from its root, run
python3 preview/logical/examples/00_logical_resource_estimate.py.
The program prints:
Logical Bell-pair resources:
peak logical qubits: 2
Hadamard actions: 1
controlled-X actions: 1
Write a standalone logical program
Use the standalone cudaq.logical API when integrating other frontend tools
without depending on the CUDA-Q kernel frontend. Both authoring routes support
placement, QEC codes, gadgets, and the same compilation and estimation stages.
To install CUDA-Q Logical separately, run:
pip install "cudaq-logical[cu13]"
Use [cu12] for CUDA 12. These extras include the CUDA-Q runtime and kernel API,
so both examples work with either extra. The bare cudaq-logical package
requires an existing CUDA-Q runtime. If you installed cudaq above, no second
install is needed.
The standalone example defines the same Bell-pair
preparation and measurement using logical operations. It explicitly compiles the
program with cql.compile(bell), then asks cql.estimate for a logical-tier
estimate that reports the same resource counts as the kernel example:
import cudaq.logical as cql
# %%
# Define a Bell program directly with linear logical-qubit values.
@cql.program
def bell() -> tuple[bool, bool]:
qubits = cql.allocate(2, state=cql.types.zero)
qubits[0] = cql.h(qubits[0])
qubits[0], qubits[1] = cql.cx(qubits[0], qubits[1])
return cql.measure_z(qubits[0]), cql.measure_z(qubits[1])
# %%
# Compile the program and request its logical resource counts directly.
build = cql.compile(bell)
resources = cql.estimate(build, tier=cql.estimate.Tier.LOGICAL)
assert build.stage == cql.stages.P0
assert resources.logical_qubits_peak == 2
assert resources.actions == {
"qlx_standard_h": 1,
"qlx_standard_cx": 1,
}
print("Logical Bell-pair resources:")
print(f" peak logical qubits: {resources.logical_qubits_peak}")
print(f" Hadamard actions: {resources.actions['qlx_standard_h']}")
print(f" controlled-X actions: {resources.actions['qlx_standard_cx']}")
To run this example, copy the code into bell_logical.py and run
python3 bell_logical.py. The example is also available in a checked-out
repository; from its root, run
python3 preview/logical/examples/standalone/00_logical_program.py.
The program prints:
Logical Bell-pair resources:
peak logical qubits: 2
Hadamard actions: 1
controlled-X actions: 1
Both examples estimate the logical resources needed to prepare and measure a
Bell pair. The output reports resource counts, not sampled measurement results.
To get measurement results for each shot, compile a logical program that
returns Booleans to a physical build and run it with cudaq.run, as in the
Steane Bernstein–Vazirani example.
Build from source
Use the nightly CUDA-Q development container; see CUDA-Q development setup for setup instructions. From the repository root, build and install CUDA-Q and CUDA-Q Logical with:
bash scripts/build_cudaq.sh -L
For prerequisites and other source-installation options, see CUDA-Q installation from source.
Where to go next
These are logical counts; neither example chooses a QEC code or a device. For placement, QEC, and physical estimates, continue with The logical programming stack or browse the examples.
To author and verify gadgets, see Gadgets and verification. For annotated Stim export and detector error models, continue with Stim emission and Derive an error model from gadgets.