ket.base¶
Base classes for quantum programming.
This module provides the base classes for quantum programming in Ket, including the
Process, which is the gateway for qubit allocation and quantum execution,
and the Quant, which stores the qubit’s reference.
With the exception of Process, the classes in this module are not
intended to be instantiated directly by the user. Instead, they are meant to be
created through provided functions.
Modules ket.base¶
Classes ket.base¶
A differentiable scalar parameter for variational quantum circuits. |
|
Quantum program process. |
- class Parameter(process, index, value, multiplier=1)¶
A differentiable scalar parameter for variational quantum circuits.
This class wraps a floating-point value and registers it with a
Processso that the runtime can compute analytical gradients (e.g., using the parameter-shift rule). It supports scalar arithmetic (*,/, unary-) so that a single registered parameter can be reused across multiple gates with different multipliers.Example
from math import pi from ket import Process, RX, RZ p = Process(gradient=True) theta = p.param(pi / 3) q = p.alloc() RX(theta, q) # use the parameter directly RZ(theta / 2, q) # reuse with a scaled copy
- property value: float¶
The effective (scaled) value of this parameter.
Returns the product of the original registered value and any multiplier applied via arithmetic operators.
- Returns:
param * multiplier.
- property param: float¶
The original, unscaled value as registered with the process.
- Returns:
The raw value passed when creating this parameter via
param.
- property grad: float | None¶
The gradient of the circuit output with respect to this parameter.
Lazily fetched from the process after circuit execution. Returns
Noneif the gradient has not yet been computed (i.e., the process has not executed) or if gradient computation was not enabled on theProcess.- Returns:
The gradient value, or
Noneif unavailable.
Example
from math import pi import ket p = ket.Process(gradient=True) theta = p.param(pi / 4) q = p.alloc() ket.RX(theta, q) with ket.gates.obs(): h = ket.Z(q) ev = ket.exp_value(h) ev.get() # execute and retrieve expected value print(theta.grad) # d<Z>/d(theta)
- class Process(execution_target: BatchExecution | LiveExecution | None = None, num_qubits: int | None = None, simulator: Literal['sparse', 'dense', 'dense gpu'] | None = None, execution: Literal['live', 'batch'] | None = None, gradient: bool = False, **kwargs)¶
Quantum program process.
A
Processin Ket is responsible for preparing and executing quantum circuits. It serves as a direct interface to the underlying Rust runtime library. The primary way to interact with a process is through theallocmethod to allocate qubits.Example
from ket import Process p = Process() qubits = p.alloc(10) # Allocate 10 qubits
By default, quantum execution is handled by the KBW simulator in sparse mode with support for up to 32 qubits. In sparse mode, qubits are represented using a data structure similar to a sparse matrix. This mode performs well when the quantum state involves the superposition of a small number of basis states, such as GHZ states, and is suitable as a general default when the number of qubits is unknown.
The dense simulation mode, on the other hand, has exponential time complexity in the number of qubits. It leverages CPU parallelism more effectively, but requires careful management of the number of qubits, defaulting to 12. The choice between sparse and dense simulation depends on the specific quantum algorithm being implemented, as each mode has trade-offs in performance and scalability.
The execution mode of the simulator can be set to either
"live"or"batch":Live (default): Quantum instructions are executed immediately upon invocation. This mode is ideal for interactive simulation.
Batch: Quantum instructions are queued and executed only at the a measurement result is requested. This mode better reflects the behavior of real quantum hardware and is recommended for preparing code for deployment to QPUs.
Batch Execution Example:
from ket import * p = Process(execution="batch") a, b = p.alloc(2) CNOT(H(a), b) # Prepare a Bell state d = sample(a + b) print(d.get()) # Execution happens here CNOT(a, b) # Raises an error: process already executed
Live Execution Example:
from ket import * p = Process(execution="live") a, b = p.alloc(2) CNOT(H(a), b) # Prepare a Bell state print(sample(a + b).get()) # Output is available immediately CNOT(a, b) H(a) print(sample(a + b).get())
- Simulators:
KBW provides four simulators with different performance characteristics. The best simulator to use depends on the number of qubits being simulated and on the specific quantum algorithm. Benchmarking is recommended to determine the most suitable simulator for a given workload.
"sparse": Sparse simulator with limited multithreading capabilities."dense": Dense simulator with good multithreaded performance."dense gpu": Dense simulator designed to run on most GPUs, including integrated Intel, AMD, and Apple GPUs, as well as NVIDIA GPUs. This simulator is generally recommended for simulations involving a large number of qubits.
- Parameters:
execution_target – Quantum execution target object. If not provided, the KBW simulator is used.
num_qubits – Number of qubits for the KBW simulator. Defaults to 32 for sparse mode, or 12 for dense mode.
simulator – Simulation mode for the KBW simulator. Options are
"sparse","dense", and"dense gpu". Defaults to"sparse".execution – Execution mode for the KBW simulator, either
"live"or"batch". Defaults to"live".
- alloc(num_qubits: int = 1) Quant¶
Allocate qubits and return a
Quantobject.Each qubit is assigned a unique index, and the resulting
Quantobject encapsulates the allocated qubits along with a reference to the parentProcessobject.Example
from ket import Process p = Process() qubits = p.alloc(3) print(qubits) # <Ket 'Quant' [0, 1, 2] pid=0x...>
- Parameters:
num_qubits – The number of qubits to allocate. Defaults to 1.
- Returns:
A list like object representing the allocated qubits.
- alloc_aux(num_qubits: int = 1, depends_on: list | None = None) Quant¶
Allocate auxiliary qubits managed by the process for uncomputation.
Auxiliary (ancilla) qubits are temporary qubits used in intermediate computation steps, such as in multi-controlled gates or temporary registers. The process tracks them separately and prevents accidental measurement. When the returned
Quantobject goes out of scope or is used as a context manager, the auxiliary qubits are automatically freed and returned to the internal qubit pool for reuse.The recommended usage is as a context manager with the
withstatement:Example
from ket import Process, ctrl, X p = Process() c, t = p.alloc(), p.alloc() with p.alloc_aux() as aux: # aux is freed on exit with around(ctrl(c, X), aux): # use aux as intermediate ctrl(aux, X)(t) # fanout to target
- Parameters:
num_qubits – The number of auxiliary qubits to allocate. Defaults to 1.
depends_on – A list of objects (such as other
Quantinstances) that the allocated auxiliary register depends on. This prevents early uncomputation; for example, ifbdepends ona,acannot be uncomputed beforeb. Defaults toNone.
- Returns:
A
Quantobject containing the auxiliary qubits. It supports use as a context manager: on exit the qubits are returned to the pool.- Raises:
ValueError – If
num_qubitsis less than 1.
- param(*param: float) list[Parameter] | Parameter¶
Register one or more differentiable parameters for gradient computation.
Each numeric value is wrapped in a
Parameterobject that can be scaled (via multiplication or division) and passed directly to parameterized gates such asRX,RY, orP. After circuit execution, the gradient of the expected value with respect to each parameter can be retrieved viagrad.Note
Gradient computation requires creating the process with
gradient=True.Example
from math import pi from ket import Process, RX, exp_value from ket.gates import obs import ket p = Process(gradient=True) theta = p.param(pi / 4) q = p.alloc() RX(theta, q) with obs(): h = ket.Z(q) ev = ket.exp_value(h) ev.get() # trigger execution theta.grad # d<Z>/d(theta)
- gates()¶
Return the gate sequence of the process as a parsed JSON object.
Serializes the internal circuit representation into a JSON-compatible Python object (typically a list of gate dictionaries). This is primarily used for inspection, debugging, or exporting the circuit.
- Returns:
A JSON-parsed object representing the gate sequence of the current process.
Example
from ket import Process, H, X p = Process() q = p.alloc(2) H(q[0]) X(q[1]) circuit = p.gates() print(type(circuit)) # <class 'list'>
- append_block(block, check_qubits=True)¶
Append a pre-built gate block to the process circuit.
Caution
This is an internal method and is not intended for direct use by library consumers. Use quantum gate functions instead.
If there is a currently active nested block (e.g., inside a
block_buildercontext), the block is appended to the innermost active block. Otherwise, it is appended directly to the process circuit.When
check_qubitsisTrue(the default), the method validates that no uncomputation rules are violated, for example, it prevents applying non-diagonal operations to auxiliary qubits that are currently blocked.- Parameters:
block – The compiled gate block to append.
check_qubits – If
True, validate qubit operation rules before appending. Defaults toTrue.
- Raises:
RuntimeError – If
check_qubitsisTrueand the block contains an operation that violates uncomputation constraints.
- block_builder(inverse=False, control: list[int] | None = None, append: bool = True, diagonal=False, permutation=False)¶
Context manager for constructing and optionally appending a gate block.
Caution
This is an internal method. Prefer using the high-level gate API and context managers (
control,inverse,around).All quantum operations performed inside the
withblock are collected into a single circuitBlockobject. On exit, the block can optionally be inverted, wrapped in a controlled operation, and/or appended to the process circuit.This is the primary building primitive used internally by gate functions and higher-level operations like
aroundandinverse.- Parameters:
inverse – If
True, invert the block before appending. Defaults toFalse.control – If provided, wrap the block in a multi-qubit controlled operation on the specified qubit indices. Defaults to
None.append – If
True, append the block to the process circuit on exit. Set toFalseto obtain the block without appending it. Defaults toTrue.diagonal – If
True, mark the block as a diagonal operation. Defaults toFalse.permutation – If
True, mark the block as a permutation operation. Defaults toFalse.
- Yields:
Block – The in-progress gate block being constructed.