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

Measurement

Quantum measurement result.

Parameter

Parameter for gradient calculation.

Process

Quantum program process.

Quant

List of qubits.

Samples

Quantum state measurement samples.

class Measurement(qubits: Quant)

Quantum measurement result.

This class holds a reference for a measurement result. The result may not be available right after the measurement call, especially in batch execution.

To read the value, access the attribute value. If the value is not available, the measurement will return None; otherwise, it will return an unsigned integer.

You can instantiate this class by calling the measure function.

Example

from ket import *

p = Process()
q = p.alloc(2)
CNOT(H(q[0]), q[1])
result = measure(q)
print(result.value)  # 0 or 3
property value: int | None

Retrieve the measurement value if available.

get() int

Retrieve the measurement value.

If the value is not available, the quantum process will execute to get the result.

class Parameter(process, index, value, multiplier=1)

Parameter for gradient calculation.

This class represents a parameter for gradient calculation in a quantum process. It should not be instanced directly, but rather obtained from the param method.

property value: float

Retrieve the parameter actual value.

property param: float

Retrieve the original value of the parameter.

property grad: float | None

Retrieve the gradient value if available.

class Process(execution_target: BatchExecution | LiveExecution | None = None, num_qubits: int | None = None, simulator: Literal['sparse', 'dense', 'dense v2'] | None = None, execution: Literal['live', 'batch'] | None = None, gradient: bool = False, **kwargs)

Quantum program process.

A Process in 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 the alloc method 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())
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, either "sparse" or "dense". 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 Quant object.

Each qubit is assigned a unique index, and the resulting Quant object encapsulates the allocated qubits along with a reference to the parent Process object.

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.

get_instructions() list[dict]

Retrieve quantum instructions from the process.

The format of the instructions is defined in the runtime library Libket.

Example

>>> from ket import *
>>> p = Process()
>>> a, b = p.alloc(2)
>>> CNOT(H(a), b)
>>> # Get quantum instructions
>>> instructions = p.get_instructions()
>>> pprint(instructions)
[{'Gate': {'control': [],
           'gate': 'Hadamard',
           'target': {'Main': {'index': 0}}}},
 {'Gate': {'control': [{'Main': {'index': 0}}],
           'gate': 'PauliX',
           'target': {'Main': {'index': 1}}}}]
Returns:

A list of dictionaries containing quantum instructions extracted from the process.

get_mapped_instructions() list[dict] | None

Retrieve quantum instructions after the circuit mapping.

The format of the instructions is defined in the runtime library Libket.

The instructions are extracted after the circuit mapping step, which is performed during the compilation process. A qubit coupling graph must be passed to the process for the quantum circuit mapping to happen. Note that at this point, the single qubit gates have not been translated to the native gate set of the quantum hardware yet.

Example

>>> n = 4
>>> coupling_graph = [(0, 1), (1, 2), (2, 3), (3, 0)]
>>> p = Process(num_qubits=n, coupling_graph=coupling_graph)
>>> q = p.alloc(n)
>>> ctrl(H(q[0]), X)(q[1:])
>>> m = measure(q)
>>> p.prepare_for_execution()
>>> pprint(p.get_mapped_instructions())
[{'Gate': {'control': [], 'gate': 'Hadamard', 'target': {'index': 0}}},
 {'Gate': {'control': [{'index': 0}],
           'gate': 'PauliX',
           'target': {'index': 1}}},
 'Identity',
 {'Gate': {'control': [{'index': 3}],
           'gate': 'PauliX',
           'target': {'index': 0}}},
 {'Gate': {'control': [{'index': 0}],
           'gate': 'PauliX',
           'target': {'index': 3}}},
 {'Gate': {'control': [{'index': 3}],
           'gate': 'PauliX',
           'target': {'index': 2}}},
 {'Measure': {'index': 0,
              'qubits': [{'index': 3},
                         {'index': 1},
                         {'index': 0},
                         {'index': 2}]}}]
Returns:

A list of dictionaries containing quantum instructions extracted from the process if the process has been transpiled, otherwise None.

get_metadata() dict[str, Any]

Retrieve metadata from the quantum process.

Example

>>> n = 4
>>> coupling_graph = [(0, 1), (1, 2), (2, 3), (3, 0)]
>>> p = Process(num_qubits=n, coupling_graph=coupling_graph)
>>> q = p.alloc(n)
>>> ctrl(H(q[0]), X)(q[1:])
>>> m = measure(q)
>>> p.prepare_for_execution()
>>> pprint(p.get_mapped_instructions())
{'allocated_qubits': 4,
 'decomposition': {'NoAuxCX': 3},
 'logical_circuit_depth': 3,
 'logical_gate_count': {'1': 1, '2': 3},
 'physical_circuit_depth': 5,
 'physical_gate_count': {'1': 1, '2': 4},
 'terminated': True}
Returns:

A dictionary containing metadata information extracted from the process.

param(*param) list[Parameter] | Parameter

Register a parameter for gradient calculation.

Parameters:

*param – Variable-length argument list of floats.

Returns:

A list of Parameter objects.

save()

Save the quantum simulation state.

This method saves the current state of the quantum simulation. It works only in simulated execution, provided that the simulator supports state saving. If state saving is not available, no error will be raised.

The structure of the saved state depends on the simulator and is not interchangeable between different simulators. Note that the saved state does not retain information about the order of qubit allocation. Using the saved state may result in unintended behavior if the qubit allocation order differs when the state is loaded.

load(data)

Loads the given data into the simulation state.

The data must be generated by a call to the method save with the same simulator. If the state load fails in the simulator, the state is not altered, and it may raise no error.

Parameters:

data – Quantum state. The data format depends on the simulator.

class Quant(*, qubits: list[int], process: Process)

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 alloc method.

A Quant serves 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 two Quant objects. The processes must be the same.

  • Indexing ([index]): Returns a new Quant object with selected qubits based on the provided index.

  • Iteration (for q in qubits): Allows iterating over qubits in a Quant object.

  • Reversal (reversed(qubits)): Returns a new Quant object with reversed qubits.

  • Length (len(qubits)): Returns the number of qubits in the Quant object.

at(index: list[int]) Quant

Return a subset of qubits at specified indices.

Create a new Quant object with qubit references at the positions defined by the provided index list.

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])
Parameters:

index – List of indices specifying the positions of qubits to be included in the new Quant.

Returns:

A new Quant object containing the selected qubits.

class Samples(qubits: Quant, shots: int = 2048)

Quantum state measurement samples.

This class holds a reference for a measurement sample result. The result may not be available right after the sample call, especially in batch execution.

To read the value, access the attribute value. If the value is not available, the measurement will return None; otherwise, it will return a dictionary mapping measurement outcomes to their respective counts.

You can instantiate this class by calling the sample function.

Example

from ket import *

p = Process()
q = p.alloc(2)
CNOT(H(q[0]), q[1])
results = sample(q)

print(results.value)
# {0: 1042, 3: 1006}
Parameters:
  • qubits – Qubits for which the measurement samples are obtained.

  • shots – Number of measurement shots (default is 2048).

property value: dict[int, int] | None

Retrieve the measurement samples if available.

get() dict[int, int]

Retrieve the measurement samples.

If the value is not available, the quantum process will execute to get the result.

histogram(**kwargs) go.Figure

Generate a histogram representing the sample.

This method creates a histogram visualizing the probability distribution of the sample.

Note

This method requires additional dependencies from ket-lang[plot].

Install with: pip install ket-lang[plot].

Returns:

Histogram of sample measurement.