Infrastructure#

Infrastructure concerns components of qlbm that do not directly implement functionality related to QLBM quantum circuits. This includes the circuits’ integration with quantum simulators, compilers, and external tools. Making good use of such tools and their rapidly expanding ecosystems is crucial for accelerating QLBM research, as it allows researchers to focus on algorithm design rather than simulation techniques.

qlbm currently integrates the core quantum components with the following infrastructure:

  1. Runners allow users to efficiently simulate quantum circuits. In addition to basic simulation, qlbm offers features that allow for reinitialization between time steps, among other Performance improvements.

  2. Compilers enable users to convert high-level quantum circuits into hardware- or simulator-specific formats, as well as analyze the scalability of available methods.

  3. Results provide an interface between the counts generated by the quantum simulator and Paraview. Information is parsed into standard formats that increase the accessibility of QLBMs.

  4. Simulation Config provides a convenient interface that ties together simulators, compilers, and quantum circuits.

Runners#

class qlbm.infra.runner.base.CircuitRunner(config, lattice, logger=<Logger qlbm (WARNING)>, device='CPU')[source]#

Base class for all simulator-specific runners.

A CircuitRunner object uses the information provided in a SimulationConfig to efficiently simulate the QLBM circuit. This includes converting the initial conditions into a suitable format, concatenating circuits together, performing reinitialization, and processing results.

Attribute

Summary

config

The SimulationConfig containing the simulation information.

lattice

The Lattice of the simulated system.

reinitializer

The Reinitializer that performs the transition between time steps.

device

Currently ignored.

logger

The performance logger, by default getLogger("qlbm").

Parameters:
abstract run(num_steps, num_shots, output_directory, output_file_name='step', statevector_snapshots=False)[source]#

Simulates the provided configuration.

Parameters:
  • num_steps (int) – The number of time steps to simulate the system for.

  • num_shots (int) – The number of shots to perform for each time step.

  • output_directory (str) – The directory to which output will be stored.

  • output_file_name (str, optional) – The root name for files containing time step artifacts, by default “step”.

  • statevector_snapshots (bool, optional) – Whether to utilize statevector snapshots, by default False.

Returns:

The parsed result of the simulation.

Return type:

QBMResult

new_result(output_directory, output_file_name)[source]#

Get a new result object for the current runner.

Parameters:
  • output_directory (str) – The directory where the result data will be stored.

  • output_file_name (str) – The file name of the result data within the directory.

Returns:

An empty result object.

Return type:

QBMResult

Raises:

ResultsException – If there is no matching result object for the runner’s lattice.

new_reinitializer()[source]#

Creates a new reinitializer for a simulated algorithm.

Returns:

A suitable reinitializer.

Return type:

Reinitializer

Raises:

ResultsException – If the underlying algorithm does not support reinitialization.

statevector_to_circuit(statevector)[source]#

Converts a given statevector to a qiskit quantum circuit representation for seamless circuit assembly.

Parameters:

statevector (Statevector) – The initial condition statevector.

Returns:

The quantum circuit representation of the statevector.

Return type:

QiskitQC

class qlbm.infra.runner.qiskit_runner.QiskitRunner(config, lattice, logger=<Logger qlbm (WARNING)>, device='CPU')[source]#

Qiskit-specific implementation of the CircuitRunner.

A provided simulation configuration is compatible with this runner if the following conditions are met:

  1. The initial_conditions is either a qlbm QuantumComponent, a Qiskit Statevector or a Qiskit``QuantumCircuit``.

  2. The execution_backend is a Qiskit AerBackend.

  3. If enabled, the sampling_backend is a Qiskit AerBackend.

Attribute

Summary

config

The SimulationConfig containing the simulation information.

lattice

The Lattice of the simulated system.

reinitializer

The Reinitializer that performs the transition between time steps.

device

Currently ignored.

logger

The performance logger, by default getLogger("qlbm").

Any simulator with a Qiskit AerBackend interface can be used as either execution_backend or sampling_backend in the config interface.

Parameters:
run(num_steps, num_shots, output_directory, output_file_name='step', statevector_snapshots=False)[source]#

Simulates the provided configuration.

Parameters:
  • num_steps (int) – The number of time steps to simulate the system for.

  • num_shots (int) – The number of shots to perform for each time step.

  • output_directory (str) – The directory to which output will be stored.

  • output_file_name (str, optional) – The root name for files containing time step artifacts, by default “step”.

  • statevector_snapshots (bool, optional) – Whether to utilize statevector snapshots, by default False.

Returns:

The parsed result of the simulation.

Return type:

QBMResult

class qlbm.infra.runner.qulacs_runner.QulacsRunner(config, lattice, logger=<Logger qlbm (WARNING)>, device='CPU')[source]#

Qulacs-specific implementation of the CircuitRunner.

A provided simulation configuration is compatible with this runner if the following conditions are met:

  1. The initial_conditions is either a qlbm QuantumComponent, a Qulacs QuantumState or a Qulacs QuantumCircuit.

  2. The execution_backend is None. A Qulacs QuantumSimulator object is automatically built from the config.

  3. If enabled, the sampling_backend is a Qiskit AerBackend. The Qulacs QuantumState is automatically converted to the appropriate Qiskit interface when performing sampling.

Attribute

Summary

config

The SimulationConfig containing the simulation information.

lattice

The Lattice of the simulated system.

reinitializer

The Reinitializer that performs the transition between time steps.

device

Currently ignored.

logger

The performance logger, by default getLogger("qlbm").

Parameters:
run(num_steps, num_shots, output_directory, output_file_name='step', statevector_snapshots=False)[source]#

Simulates the provided configuration.

Parameters:
  • num_steps (int) – The number of time steps to simulate the system for.

  • num_shots (int) – The number of shots to perform for each time step.

  • output_directory (str) – The directory to which output will be stored.

  • output_file_name (str, optional) – The root name for files containing time step artifacts, by default “step”.

  • statevector_snapshots (bool, optional) – Whether to utilize statevector snapshots, by default False.

Returns:

The parsed result of the simulation.

Return type:

QBMResult

get_counts(qulacs_samples, num_bits)[source]#

Converts qulacs samples to qiskit Counts.

Parameters:
  • qulacs_samples (List[int]) – The samples generated through qulacs sampling.

  • num_bits (int) – The number of bits each sample contains.

Returns:

The qiskit Counts representation of the object.

Return type:

Counts

Performance#

class qlbm.infra.reinitialize.base.Reinitializer(lattice, compiler, logger=<Logger qlbm (WARNING)>)[source]#

Base class for all algorithm-specific reinitializers.

A Reinitializer uses the information at information available at the end of the simulation of 1 or more time steps to new initial conditions for the following time steps. Such information includes the quantum state and counts extracted from it. Novel initial conditions are inferred automatically based on the requirements of the algorithm under simulation, and an on-the-fly CircuitCompiler automatically converts them to the appropriate format to enable compatibility with the already transpiled circuits. For convenience, all reinitializers provide a uniform reinitialize() interface, which takes as input both the quantum state and the counts performed during simulation. Its implementation may choose to ignore one of those inputs, depending on the algorithm and implementation.

Attribute

Summary

lattice

The Lattice of the simulated system.

compiler

The compiler that converts the novel initial conditions circuits.

logger

The performance logger, by default getLogger("qlbm")

Parameters:
abstract reinitialize(statevector, counts, backend, optimization_level=0)[source]#

Parses the input statevector and counts, constructs a new initial conditions circuit, and transpiles it to the given backend.

Parameters:
  • statevector (Statevector) – The statevector at the end of the simulation.

  • counts (Counts) – The counts extracted from the statevector at the end of the simulation.

  • backend (AerBackend | None) – The backend to compile to.k

  • optimization_level (int, optional) – The optimization level to pass to the circuit compiler, by default 0.

Returns:

The compiled initial conditions circuit to use for the next time step.

Return type:

QiskitQC | QulacsQC

abstract requires_statevector()[source]#

Whether the reinitializer requires a copy of the statevector.

Omotting the statevector may significantly increase the perfomance of reinitialization.

Returns:

Whether a statevector is needed.

Return type:

bool

class qlbm.infra.reinitialize.collisionless_reinitializer.CollisionlessReinitializer(lattice, compiler, logger=<Logger qlbm (WARNING)>)[source]#

CQLBM-specific implementation of the Reinitializer.

Compatible with both QiskitRunners and QulacsRunners. To generate a new set of initial conditions for the CQLBM algorithm, the reinitializer simply returns the quantum state computed at the end of the previous simulation. This allows the reuse of a single quantum circuit for the simulation of arbitrarily many time steps. No copy of the statevector is required.

Attribute

Summary

lattice

The CollisionlessLattice of the simulated system.

compiler

The compiler that converts the novel initial conditions circuits.

logger

The performance logger, by default getLogger("qlbm")

Parameters:
reinitialize(statevector, counts, backend=None, optimization_level=0)[source]#

Returns the provided statevector as a new Qiskit Initialize object that can be prepended to the time step circuit to resume simulation.

Parameters:
  • statevector (Statevector) – The statevector at the end of the simulation.

  • counts (Counts) – Ignored.

  • backend (AerBackend | None) – Ignored.

  • optimization_level (int, optional) – Ignored.

Returns:

A Qiskit Initialize object.

Return type:

QiskitQC | QulacsQC

requires_statevector()[source]#

Whether the reinitializer requires a copy of the statevector.

Omotting the statevector may significantly increase the perfomance of reinitialization.

Returns:

Whether a statevector is needed.

Return type:

bool

class qlbm.infra.reinitialize.spacetime_reinitializer.SpaceTimeReinitializer(lattice, compiler, logger=<Logger qlbm (WARNING)>)[source]#

SpaceTimeQLBM-specific implementation of the Reinitializer.

Compatible with both QiskitRunners and QulacsRunners. To generate a new set of initial conditions for the CQLBM algorithm, the reinitializer simply returns the quantum state computed at the end of the previous simulation. This allows the reuse of a single quantum circuit for the simulation of arbitrarily many time steps. No copy of the statevector is required.

Attribute

Summary

lattice

The SpaceTimeLattice of the simulated system.

compiler

The compiler that converts the novel initial conditions circuits.

logger

The performance logger, by default getLogger("qlbm")

Parameters:
reinitialize(statevector, counts, backend, optimization_level=0)[source]#

Converts the input counts into a new PointWiseSpaceTimeInitialConditions object that can be prepended to the time step circuit to resume simulation.

Parameters:
  • statevector (Statevector) – Ignored.

  • counts (Counts) – The counts obtained from SpacetimeGridVelocityMeasurement at the end of the simulation.

  • backend (AerBackend | None) – The backend used for simulation.

  • optimization_level (int, optional) – The compiler optimization level.

Returns:

The suitably compiles initial conditions circuit.

Return type:

QiskitQC | QulacsQC

counts_to_velocity_pairs(counts)[source]#

Converts all counts into their grid and velocity components.

Parameters:

counts (Counts) – The Qiskit Count output of the simulation.

Returns:

The input counts split into their grid position and velocity profile.

Return type:

List[Tuple[Tuple[int, int], Tuple[bool, bool, bool, bool]]]

split_count(count)[source]#

Splits a given Count into its position and velocity components.

Counts are assumed to be obtained from SpacetimeGridVelocityMeasurement objects, and split format is the same as the input to PointWiseSpaceTimeInitialConditions.

Parameters:

count (str) – The Qiskit Count output of the simulation.

Returns:

The input count split into its grid position and velocity profile.

Return type:

Tuple[Tuple[int, int], Tuple[bool, bool, bool, bool]]

requires_statevector()[source]#

Whether the reinitializer requires a copy of the statevector.

Omotting the statevector may significantly increase the perfomance of reinitialization.

Returns:

Whether a statevector is needed.

Return type:

bool

Compilers#

class qlbm.infra.compiler.CircuitCompiler(compiler_type, compiler_target, logger=<Logger qlbm (WARNING)>)[source]#

Wrapper for Qiskit and Tket transpilers with flexible targets.

This class provides a uniform interface for compiling qlbm circuits to any Qiskit backend, as well as Qulacs.

Attribute

Summary

compiler_type

Which transpiler platform to use. Should be either "QISKIT" or "TKET".

compiler_target

The platform to which the circuit should adhere. Should be either "QISKIT" or "QULACS".

logger

The performance logger, by default getLogger("qlbm").

Example usage: we will construct an end-to-end QLBM algorithm and compile it to a qiskit simulator using Tket. We begin by constructing a SpaceTimeQLBM algorithm for a \(4 \\times 8\) lattice, with one time step.

from qlbm.components.spacetime import SpaceTimeQLBM
from qlbm.infra import CircuitCompiler
from qlbm.lattice import SpaceTimeLattice

# Build an example lattice
lattice = SpaceTimeLattice(
    num_timesteps=1,
    lattice_data={
        "lattice": {"dim": {"x": 4, "y": 8}, "velocities": {"x": 2, "y": 2}},
        "geometry": [],
    },
)

(Source code)

We can first visualize the high-level quantum circuit the qlbm infers:

# Build a complex quantum circuit and visualize it
component = SpaceTimeQLBM(lattice=lattice)

component.draw("mpl")

(Source code, png, hires.png, pdf)

../_images/infra-2.png

To construct the compiler, we only need to specify that we intend to use Tket to compile to a Qiskit simulator:

# Construct a compiler that uses Tket to target Qiskit
compiler = CircuitCompiler("TKET", "QISKIT")

(Source code)

Compilation takes a single call to the compile() method:

from qiskit_aer import AerSimulator

# Compiler the circuit to the qiskit AerSimulator
compiled_circuit = compiler.compile(component, backend=AerSimulator())

(Source code)

The result is a Qiskit quantum circuit, which we can visualize the same way we would any qlbm component:

compiled_circuit.draw("mpl")

(Source code)

../_images/infra-5_00.png

(png, hires.png, pdf)#

../_images/infra-5_01.png

(png, hires.png, pdf)#

Raises:
Parameters:
  • compiler_type (str)

  • compiler_target (str)

  • logger (Logger)

compile(compile_object, backend, optimization_level=0)[source]#

Compiles the provided object to the appropriate backend.

Parameters:
  • compile_object (QiskitQC | QuantumComponent) – The object (qlbm component or quantum) circuit to compile.

  • backend (AerBackend | None) – The backend to compile to.

  • optimization_level (int, optional) – The compiler optimization level, by default 0.

Returns:

The compiled circuit.

Return type:

QulacsQC | QiskitQC

Raises:

Results#

class qlbm.infra.result.base.QBMResult(lattice, directory, output_file_name='step')[source]#

Base class for all algorithm-specific results.

A Result object parses the counts extracted from the quantum state at the end of the simulation of some number of time steps. This information is then either translated into a visual representation of the encoded flow field or parsed into a format that is suitable for reinitialization. Results can additionally create visual representations of lattice geometry and save data to disk in compressed formats.

Attribute

Summary

lattice

The Lattice of the simulated system.

directory

The directory to which the results outputs data to.

paraview_dir

The subdirectory under directory which stores the Paraview files.

output_file_name

The root name for files containing time step artifacts, by default “step”.

Parameters:
  • lattice (Lattice)

  • directory (str)

  • output_file_name (str)

visualize_geometry()[source]#

Creates stl files for each block in the lattice.

Output files are formatted as output_dir/paraview_dir/cube_<x>.stl. The output is created through the Block’s Block.stl_mesh() method.

save_timestep_array(numpy_res, timestep, create_vis=True, save_counts_array=False)[source]#

Saves the time step array to a file.

Parameters:
  • numpy_res (np.ndarray) – The result in array format.

  • timestep (int) – The time step to which the result corresponds.

  • create_vis (bool, optional) – Whether to create the visualization, by default True.

  • save_counts_array (bool, optional) – Whether to save the raw counts object to a CSV file, by default False.

create_visualization(data, timestep)[source]#

Creates a vtk visual representation of the data.

Parameters:
  • data (np.ndarray | None) – The np.ndarray representation of the population density at each grid location.

  • timestep (int) – The time step to which the data corresponds.

abstract save_timestep_counts(counts, timestep, create_vis=True, save_array=False)[source]#

Saves the time step counts to a file.

Parameters:
  • counts (Dict[str, float]) – The result in Qiskit Counts format.

  • timestep (int) – The time step to which the result corresponds.k

  • create_vis (bool, optional) – Whether to create the visualization, by default True.

  • save_array (bool, optional) – Whether to save the raw counts object to a CSV file, by default False.

abstract visualize_all_numpy_data()[source]#

Converts all numpy data saved to disk to vti files.

class qlbm.infra.result.collisionless_result.CollisionlessResult(lattice, directory, output_file_name='step')[source]#

CQLBM-specific implementation of the QBMResult.

Processes counts sampled from GridMeasurement primitives.

Attribute

Summary

lattice

The CollisionlessLattice of the simulated system.

directory

The directory to which the results outputs data to.

paraview_dir

The subdirectory under directory which stores the Paraview files.

output_file_name

The root name for files containing time step artifacts, by default “step”.

Parameters:
save_timestep_counts(counts, timestep, create_vis=True, save_array=False)[source]#

Saves the time step counts to a file.

Parameters:
  • counts (Dict[str, float]) – The result in Qiskit Counts format.

  • timestep (int) – The time step to which the result corresponds.k

  • create_vis (bool, optional) – Whether to create the visualization, by default True.

  • save_array (bool, optional) – Whether to save the raw counts object to a CSV file, by default False.

visualize_all_numpy_data()[source]#

Converts all numpy data saved to disk to vti files.

class qlbm.infra.result.spacetime_result.SpaceTimeResult(lattice, directory, output_file_name='step')[source]#

SpaceTimeQLBM-specific implementation of the QBMResult.

Processes counts sampled from SpaceTimeGridVelocityMeasurement primitives.

Attribute

Summary

lattice

The SpaceTimeLattice of the simulated system.

directory

The directory to which the results outputs data to.

paraview_dir

The subdirectory under directory which stores the Paraview files.

output_file_name

The root name for files containing time step artifacts, by default “step”.

Parameters:
save_timestep_counts(counts, timestep, create_vis=True, save_array=False)[source]#

Saves the time step counts to a file.

Parameters:
  • counts (Dict[str, float]) – The result in Qiskit Counts format.

  • timestep (int) – The time step to which the result corresponds.k

  • create_vis (bool, optional) – Whether to create the visualization, by default True.

  • save_array (bool, optional) – Whether to save the raw counts object to a CSV file, by default False.

visualize_all_numpy_data()[source]#

Converts all numpy data saved to disk to vti files.

Simulation Config#

class qlbm.infra.runner.simulation_config.SimulationConfig(initial_conditions, algorithm, postprocessing, measurement, target_platform, compiler_platform, optimization_level, statevector_sampling, execution_backend, sampling_backend, logger=<Logger qlbm (WARNING)>)[source]#

A SimulationConfig ties together algorithmic quantum components, circuit compilers, runners, and performance optimizations.

This is the most convenient access point for performing simulations with qlbm. In total, the config contains 11 relevant class attributes that together allow users to customize their simulations in a declarative manner. For convenience, we split these attributes by the purpose they serve for the simulation workflow.

Algorithmic attributes specify the complete, end-to-end, QLBM algorithm. This includes initial conditions, the time step circuit, an optional postprocessing step, and a final measurement procedure.

Algorithmic attributes#

Attribute

Description

initial_conditions

The initial conditions of the simulations. For example, CollisionlessInitialConditions or PointWiseSpaceTimeInitialConditions.

algorithm

The algorithm that performs the QLBM time step computation. For example, CQLBM or SpaceTimeQLBM.

postprocessing

The quantum component concataned to the algorithm. Usually EmptyPrimitive.

measurement

The circuit that samples the quantum state. For example, GridMeasurement or SpaceTimeGridVelocityMeasurement.

Compiler-related attributes govern how compilers convert algorithmic attributes to the appropriate format. All quantum circuits will be compiled using the same settings.

Compiler-related attributes#

Attribute

Description

target_platform

The platform that the simulation will be carried out on. Either "QISKIT" or "QULACS".

compiler_platform

The platform of the compiler to use. Either "QISKIT" or "TKET".

optimization_level

The compiler optimization level.

Runner-related attributes prescribe how the simulation should proceede. This includes the specific simulators that will execute the circuits, and performance optimization settings.

Runner-related attributes#

Attribute

Description

execution_backend

The specific AerSimulator use (if using Qiskit) or None if using Qulacs.

sampling_backend

The specific AerSimulator to use if statevector_sampling is enabled.

statevector_sampling

Whether statevector sampling should be utilized.

Note

Example configuration: simulating SpaceTimeQLBM with Qiskit. First, we set up the config with the circuits we want to simulate, and the infrastructure we want to use.

SimulationConfig(
    initial_conditions=SpaceTimeInitialConditions(
        lattice, grid_data=[((1, 5), (True, True, True, True))]
    ),
    algorithm=SpaceTimeQLBM(lattice),
    postprocessing=EmptyPrimitive(lattice),
    measurement=SpaceTimeGridVelocityMeasurement(lattice),
    target_platform="QISKIT",
    compiler_platform="QISKIT",
    optimization_level=0,
    statevector_sampling=True,
    execution_backend=AerSimulator(method="statevector"),
    sampling_backend=AerSimulator(method="statevector"),
)

Once constructed, the cfg will figure out the appropriate compiler calls to convert the circuit to the appropriate format. All the user needs to do is call the prepare_for_simulation() method:

cfg.prepare_for_simulation()

The circuits are compiled in-place, which makes it easy to plug in the cfg object into a QiskitRunner:

# Create a runner object to simulate the circuit
runner = QiskitRunner(
    cfg,
    lattice,
)

# Simulate the circuits
runner.run(
    10
    2**12,
    "output_dir",
    False
)

Note

Example configuration: simulating CQLBM with Qulacs and Tket.

cfg = SimulationConfig(
    initial_conditions=CollisionlessInitialConditions(lattice, logger),
    algorithm=CQLBM(lattice, logger),
    postprocessing=EmptyPrimitive(lattice, logger),
    measurement=GridMeasurement(lattice, logger),
    target_platform="QULACS",
    compiler_platform="TKET",
    optimization_level=0,
    statevector_sampling=statevector_sampling,
    execution_backend=None,
    sampling_backend=AerSimulator(method="statevector"),
    logger=logger,
)

Once constructed, the cfg will figure out the appropriate compiler calls to convert the circuit to the appropriate format. All the user needs to do is call the prepare_for_simulation() method:

cfg.prepare_for_simulation()

The circuits are compiled in-place, which makes it easy to plug in the cfg object into a QulacsRunner:

# Create a runner object to simulate the circuit
runner = QulacsRunner(
    cfg,
    lattice,
)

# Simulate the circuits
runner.run(
    10
    2**12,
    "output_dir",
    True
)
Parameters:
  • initial_conditions (Statevector | QuantumCircuit | QuantumState | QuantumCircuit | QuantumComponent)

  • algorithm (QuantumCircuit | QuantumCircuit | QuantumComponent)

  • postprocessing (QuantumCircuit | QuantumCircuit | QuantumComponent)

  • measurement (QuantumCircuit | QuantumComponent)

  • target_platform (str)

  • compiler_platform (str)

  • optimization_level (int)

  • statevector_sampling (bool)

  • execution_backend (AerBackend | None)

  • sampling_backend (AerBackend)

  • logger (Logger)

validate()[source]#

Validates the configuration.

This includes the following checks:

  1. The algorithmic attributes are of compatible types.

  2. The target platform is available.

  3. The execution backend (if enabled) is compatible with the target platform.

  4. The sampling backend (if enabled) is compatible with the target platform.

This function simply checks that the provided attributes are suitable - it does not perform any conversions.

prepare_for_simulation()[source]#

Converts all algorithmic components to the target platform according to the specification, in place.

Return type:

None

get_execution_compiler()[source]#

Get the CircuitCompiler that can converts the algorithmic attributes to the target sampling_backend simulator.

Returns:

A compatible circuit compiler.

Return type:

CircuitCompiler

get_sampling_compiler()[source]#

Get the CircuitCompiler that can converts the algorithmic attributes to the target execution_backend simulator.

Returns:

A compatible circuit compiler.

Return type:

CircuitCompiler