ket.base#

Base quantum programming classes.

This module provides base classes for handling quantum programming in the Ket library. It includes for handle and store quantum states, measurements.

Classes ket.base#

Measurement

Quantum measurement result.

Process

Quantum program process.

Quant

List of qubits.

QuantumState

Snapshot of a quantum state.

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 Process(configuration=None, num_qubits: int | None = None, simulator: Literal['sparse', 'dense'] | None = None, execution: Literal['live', 'batch'] | None = None)#

Quantum program process.

A Process in Ket handles quantum circuit preparation and execution, serving as a direct interface to the Rust library. Its primary usage in quantum programming is to allocate qubits using the alloc method.

Example

from ket import Process

p = Process()
qubits = p.alloc(10) # Allocate 10 qubits

By default, quantum execution is performed by the KBW simulator using sparse mode with 32 qubits. The KBW simulator in sparse mode handles qubits in a representation equivalent to a sparse matrix. This simulator’s execution time is related to the amount of superposition in the quantum execution, making it suitable as a default when the number of qubits is unknown or the quantum state can be represented by a sparse matrix, such as in a GHZ state. The dense simulator mode has exponential time complexity with the number of qubits. While it can better explore CPU parallelism, the number of qubits must be carefully set. The default number of qubits for the dense simulator is 12. The choice of simulator mode depends on the quantum algorithm, as each mode has its pros and cons.

Another parameter for quantum execution on the KBW simulator is between “live” and “batch” execution. This configuration determines when quantum instructions will be executed. If set to “live”, quantum instructions execute immediately after the call. If set to “batch”, quantum instructions execute only at the end of the process. The default configuration is “live”, suitable for quantum simulation where the non-clone theorem of quantum mechanics does not need to be respected. Batch execution is closer to what is expected from a quantum computer and is recommended when preparing quantum code to execute on a QPU.

Example

Batch execution:

from ket import *

p = Process(execution="batch")
a, b = p.alloc(2)

CNOT(H(a), b) # Bell state preparation

d = dump(a + b)

p.execute()
# The value of `d` will only be available after executing the process

print(d.show())

CNOT(a, b)  # This instruction will raise an error since the process
            # has already executed.

Live execution:

from ket import *

p = Process(execution="batch")
a, b = p.alloc(2)

CNOT(H(a), b) # Bell state preparation

# The value of the dump is available right after
print(dump(a + b).show())

CNOT(a, b)  # This instruction can execute normally
H(a)

print(dump(a + b).show())
Parameters:
  • configuration – Configuration definition for third-party quantum execution. Defaults to None.

  • num_qubits – Number of qubits for the KBW simulator. If None and simulator=="sparse", defaults to 32; otherwise, defaults to 12.

  • simulator – Simulation mode for the KBW simulator. If None, defaults to "sparse".

  • execution – Execution mode for the KBW simulator. If None, defaults to "live".

alloc(num_qubits: int = 1) Quant#

Allocate a specified number of qubits and return a Quant object.

Parameters:

num_qubits – The number of qubits to allocate. Defaults to 1.

Returns:

A list like object representing the allocated qubits.

Example

>>> from ket import Process
>>> p = Process()
>>> qubits = p.alloc(3)
>>> print(qubits)
<Ket 'Quant' [0, 1, 2] pid=0x...>

The alloc method allocates the specified number of qubits and returns 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.

execute()#

Force the execution of the quantum circuit.

This method triggers the immediate execution of the prepared quantum circuit. It is essential when live execution is required or when batch execution needs to be initiated.

Example

>>> from ket import Process
>>> p = Process()
>>> # ... (quantum circuit preparation)
>>> # Force the execution of the quantum circuit
>>> p.execute()

Note

The execute method should be used when the quantum instructions are to be executed immediately, either in live mode or to initiate batch execution.

get_instructions() list[dict[str, Any]]#

Retrieve quantum instructions from the quantum process.

Returns:

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

The get_instructions method retrieves the quantum instructions from the prepared quantum process. It internally calls the instructions_json method to obtain the instructions fom the Rust library in JSON format, converts the byte data into a list of dictionaries, and returns the result.

Note

Ensure that the quantum process has been appropriately prepared for execution using the prepare_for_execution method before calling this method. The returned instructions provide insights into the quantum circuit and can be useful for debugging or analysis purposes.

Example

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

Retrieve metadata from the quantum process.

Returns:

A dictionary containing metadata information extracted from the process.

The get_metadata method retrieves metadata from the prepared quantum process. It internally calls the metadata_json method to obtain the metadata in JSON format, converts the byte data into a dictionary, and returns the result.

Note

Ensure that the quantum process has been appropriately prepared for execution using the prepare_for_execution method before calling this method. The returned metadata provides information about the quantum circuit execution, including depth, gate count, qubit simultaneous operations, status, execution time, and timeout.

Example

>>> from ket import Process
>>> p = Process()
>>> a, b = p.alloc(2)
>>> CNOT(H(a), b)
>>> # Get metadata
>>> metadata = p.get_metadata()
>>> pprint(metadata)
{'depth': 2,
 'execution_time': None,
 'gate_count': {'1': 1, '2': 1},
 'qubit_simultaneous': 2,
 'status': 'Live',
 'timeout': None}
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...>

# Free all qubits in a Quant object
result_quant.free()

# Check if all qubits in a Quant object are free
is_free = result_quant.is_free()
print(is_free)  # True

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.

  • Context Manager (with qubits:): Ensures that all qubits are free at the end of the scope.

  • 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.

free()#

Release the qubits.

This method frees the allocated qubits, assuming they are in the state \(\left|0\right>\) before the call.

Warning

No check is performed to verify if the qubits are in the state \(\left|0\right>\). Releasing qubits in other states cause undefined behavior.

is_free() bool#

Check if all allocated qubits are in the ‘free’ state.

Returns:

True if all qubits are free, False otherwise.

class QuantumState(qubits: Quant)#

Snapshot of a quantum state.

This class holds a reference for a snapshot of a quantum state obtained using a quantum simulator. The result may not be available right after the measurement call, especially in batch execution.

You can instantiate this class by calling the dump function.

Example:
from ket import *

p = Process()
a, b = p.alloc(2)
with around(cat(kron(H, I), CNOT), a, b):
    Y(a)
    inside = dump(a+b)

outside = dump(a+b)
print(inside.show())
# |01⟩    (50.00%)
#  -0.707107i     ≅     -i/√2
# |10⟩    (50.00%)
#  0.707107i     ≅      i/√2

print(outside.show())
# |11⟩    (100.00%)
#  -1.000000i     ≅     -i/√1
Parameters:

qubits – Qubits from which to capture a quantum state snapshot.

property states: dict[int, complex] | None#

Get the quantum state.

Returns a dictionary mapping base states to their corresponding probability amplitudes.

Returns:

The quantum state, or None if the quantum state information is not available.

get() dict[int, complex]#

Get the quantum state.

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

property probabilities: dict[int, float] | None#

Get the measurement probabilities.

Returns a dictionary mapping base states to their corresponding measurement probabilities.

Returns:

The measurement probabilities, or None if the quantum state information is not available.

sample(shots=4096, seed=None) dict[int, int] | None#

Get the quantum execution shots.

The parameters shots and seed are used to generate the sample from the quantum state snapshot.

Parameters:
  • shots – Number of shots. Defaults to 4096.

  • seed – Seed for the RNG.

Returns:

A dictionary mapping measurement outcomes to their frequencies in the generated sample, or None if the quantum state information is not available.

show(format_str: str | None = None) str | None#

Return the quantum state as a string.

Use the format string to change the print format of the basis states:

  • i: print the state in the decimal base

  • b: print the state in the binary base (default)

  • i|b<n>: separate the n first qubits; the remaining print in the binary base

  • i|b<n1>:i|b<n2>[:i|b<n3>...]: separate the n1, n2, n3, ... first qubits

Example:
from ket import *

p = Process()

q = p.alloc(19)
X(ctrl(H(q[0]), X, q[1:])[1::2])
d = dump(q)

print(d.show('i'))
# |87381⟩ (50.00%)
#  0.707107               ≅      1/√2
# |436906⟩        (50.00%)
#  0.707107               ≅      1/√2
print(d.show('b'))
# |0010101010101010101⟩   (50.00%)
#  0.707107               ≅      1/√2
# |1101010101010101010⟩   (50.00%)
#  0.707107               ≅      1/√2
print(d.show('i4'))
# |2⟩|101010101010101⟩    (50.00%)
#  0.707107               ≅      1/√2
# |13⟩|010101010101010⟩   (50.00%)
#  0.707107               ≅      1/√2
print(d.show('b5:i4'))
# |00101⟩|5⟩|0101010101⟩  (50.00%)
#  0.707107               ≅      1/√2
# |11010⟩|10⟩|1010101010⟩ (50.00%)
#  0.707107               ≅      1/√2
Parameters:

format_str – Format string that matches (i|b)\d*(:(i|b)\d+)*.

Returns:

The formatted quantum state as a string, or None if the quantum state information is not available.

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.

Functions ket.base#

set_default_process_configuration([...])

Set default process configurations.

set_default_process_configuration(configuration=None, num_qubits: int | None = None, simulator: Literal['sparse', 'dense'] | None = None, execution: Literal['live', 'batch'] | None = None, force_configuration: bool = False)#

Set default process configurations.

Configures default parameters for quantum processes using the specified options.

Parameters:
  • configuration – Configuration definition for third-party quantum execution. Defaults to None.

  • num_qubits – Number of qubits for the KBW simulator.Defaults to None.

  • simulator – Simulation mode for the KBW simulator. Defaults to None.

  • execution – Execution mode for the KBW simulator. Defaults to None.

  • force_configuration – If set to True, the parameters defined in the Process constructor will overwrite those that are not None. Defaults to False.