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.
Classes ket.base¶
A differentiable scalar parameter for variational quantum circuits. |
|
Quantum program process. |
|
List of qubits. |
- 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) 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.
- Returns:
A
Quantobject containing the auxiliary qubits. It supports use as a context manager: on exit the qubits are returned to the qubit 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.
- class Quant(*, qubits: list[int], process: Process, undo=None, source=None)¶
List of qubits.
This class represents a list of qubit indices within a quantum process. Direct instantiation of this class is not recommended. Instead, it should be created by calling the
allocmethod.A
Quantserves as a fundamental quantum object where quantum operations should be applied.Example
from ket import * # Create a quantum process p = Process() # Allocate 2 qubits q1 = p.alloc(2) # Apply a Hadamard gates on the first qubit of `q1` H(q1[0]) # Allocate more 2 qubits q2 = p.alloc(2) # Concatenate two Quant objects result_quant = q1 + q2 print(result_quant) # <Ket 'Quant' [0, 1, 2, 3] pid=0x...> # Use the fist qubit to control the application of # a Pauli X gate on the other qubits ctrl(result_quant[0], X)(result_quant[1:]) # Select qubits at specific indexes selected_quant = result_quant.at([0, 1]) print(selected_quant) # <Ket 'Quant' [0, 1] pid=0x...>
Supported operations:
Addition (
+): Concatenates twoQuantobjects. The processes must be the same.Indexing (
[index]): Returns a newQuantobject with selected qubits based on the provided index.Iteration (
for q in qubits): Allows iterating over qubits in aQuantobject.Reversal (
reversed(qubits)): Returns a newQuantobject with reversed qubits.Length (
len(qubits)): Returns the number of qubits in theQuantobject.
- at(index: list[int]) Quant¶
Return a subset of qubits at specified indices.
Create a new
Quantobject with qubit references at the positions defined by the providedindexlist.Example
from ket import * # Create a quantum process p = Process() # Allocate 5 qubits q = p.alloc(5) # Select qubits at odd indices (1, 3) odd_qubits = q.at([1, 3])
- as_int(number: int = 0)¶
Interpret and initialize this quantum register as a quantum integer.
Wraps the register as a
Qint, enabling quantum arithmetic operations (addition, subtraction, comparison, etc.) on the underlying qubits. The register is initialized to the given classical integer value usingXgates.The
Qintuses a two’s-complement signed representation internally.Example
from ket import Process, measure p = Process() q = p.alloc(5) qi = q.as_int(5) # register initialized to |5⟩ qi += 3 # in-place addition: |5⟩ → |8⟩ print(measure(qi).value) # 8
- Parameters:
number – The initial classical integer value to encode into the quantum register. Defaults to
0.- Returns:
A quantum integer wrapping this register, initialized to
number.
- as_real(exp: int, number: float = 0.0)¶
Interpret and initialize this quantum register as a fixed-point quantum real number.
Wraps the register as a
Qreal, enabling quantum arithmetic operations on floating-point values encoded in a fixed-point binary representation.The real number is stored internally as an integer scaled by \(2^{\texttt{exp}}\):
A positive
expincreases fractional precision (smaller representable step size of \(2^{-\texttt{exp}}\)).A negative
expincreases the representable magnitude at the cost of precision.
Example
from ket import Process, measure p = Process() q = p.alloc(8) # 8 qubits for fixed-point qr = q.as_real(4, 1.5) # precision: 1/16, initialized to 1.5 qr += 0.25 # in-place addition print(measure(qr).value) # 1.75
- Parameters:
exp – The exponent defining the fixed-point scale. The stored integer
nrepresents the real valuen / 2**exp.number – The initial classical float value to encode into the quantum register. Defaults to
0.0.
- Returns:
A quantum real number wrapping this register, initialized to
number.
- dump_format()¶
Return the state-formatting callable used by
dump.Provides a function that converts a raw integer basis-state index into a zero-padded binary string of the correct width for this register. This is used internally by
QuantumStateto display multi-register states with per-register labels.- Returns:
A function that accepts an integer basis-state value and returns its binary string representation (zero-padded to
len(self)bits).