quop_mpi

class quop_mpi.Ansatz(system_size: int, MPI_communicator: mpi4py.MPI.Intracomm = mpi4py.MPI.COMM_WORLD)

Define and simulate a QVA.

Associated QuOp Functions:

Parameters:
system_sizeint

number of quantum basis states in the simulated system

MPI_communicatorIntracomm, optional

MPI Intracomm, by default MPI.COMM_WORLD

Examples

Minimal definition of an arbitrary QVA, of size system size. Where [UQ, UW] defines the ansatz unitary and observable_function is an Observables Function.

alg = Ansatz(system_size) alg.set_unitaries([UQ, UW])
alg.set_observables(observable_function)
Attributes:
system_sizeint

The size of the simulated quantum system.

local_iint

parallel partition size of system state and observables

local_i_offsetint

global index offset of the local parallel partition

partition_tablendarray[int]

1-D integer array describing the global partitioning scheme such that for a given MPI rank partition_table[rank + 1] - partition_table[rank] = local_i

observablesndarray[float64]

1-D real array of local_i observables

variational_parametersndarray[float64]

1-D real array of variational parameters, updated during optimisation

ansatz_depthint

number of ansatz iterations, by default 1

total_paramsint

number of variational parameters associated with each ansatz iteration

expectationfloat

last computed objective function value, updated during optimisation

ansatz_initial_statendarray[complex128]

1-D complex array of local_i values, the initial system state

final_statendarray[complex128]

1-D array of local_i elements, the system state after computation of the state evolution under the action of an ansatz unitary.

last_evaluatedndarray[float]

1-D real array, the last variational parameters passed to evolve_state()

objective_cntint

number of objective function evaluations during QVA simulation

resultdict

last result returned by the execute() method

seedint

seeds random number generation, incremented before each repeat in the benchmark() method

sample_indexeslist[ndarray[int32]]

if simulating sampling, contains the global indexes for each block of sampled observables, resets to [] when the objective function value is accepted

sampleslist[ndarray[float64]]

if simulating sampling, contains the observable value for each block of sampled observables, resets to [] when the objective function value is accepted

sample_minimum_indexeslist[int]

if simulating sampling, contains the index of the minimum observable sampled for each computation of the objective function

benchmark(ansatz_depths: Iterable[int], repeats: int, param_persist: bool = False, verbose: bool = True, filename: str | None = None, label: str = 'test', save_action: str = 'a', time_limit: int | None = None, suspend_path: str | None = None)

A method by which to study how a QVA performs as the number of ansatz iterations increases.

Parameters:
ansatz_depthsiterable[int]

integers specifying a sequence of ansatz depths

repeatsint

number of repeats at each ansatz depth

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results by default True

filenamestr or None, optional

name of *.h5 file in which to save() the optimised system state and observables

labelstr, optional

if filename is not None, *.h5 data will be saved as “filename/label_depth_repeat”, by default "test"

save_action{‘a’, ‘w’}, optional

action taken during first file write: ‘a’ to append, ‘w’ to overwrite, by default ‘a’

time_limitint or None, optional

total allocated in-program time in seconds, if the time of the previous simulation exceeds the time remaining, the benchmark is suspended

suspend_pathstr or None, optional

path to the suspend file if time_limit is not None

evaluate(variational_parameters: list[float] | np.ndarray[float]) float

Lazily computes the objective function value.

The Ansatz instance stores the last variational parameters passed to evaluate and the corresponding objective function value. If the input variational parameters match, re-computation of the final state is skipped and the previously computed objective function value is returned.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

Returns:
float

objective function value

evolve_state(variational_parameters: list[float] | np.ndarray[float])

Compute the system state under the action of the ansatz unitary.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters.

See also

set_unitaries()
execute(variational_parameters: list[float] | np.ndarray[float] = None)

Simulate a QVA.

If variational_parameters is None, initial parameter values are generated using the Parameter Function of the corresponding unitary instances.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

gen_initial_params(ansatz_depth: int = None) np.ndarray[np.float64]

Generate initial variational parameters.

Values are generated using the Parameter Function associated with each unitary passed to the set_unitaries() method.

Note

If ansatz_depth is None the ansatz depth defaults to 1 or the depth specified by the set_depth() method.

Parameters:
ansatz_depthint, optional

number of ansatz iterations

Returns:
ndarray[float64]

1-D (ansatz_depth * total_params,) real array of variational parameters

get_expectation_value() float

Compute the objective function at the current value of variational_parameters().

Returns:
float

objective function value

get_final_state() np.ndarray[np.complex128] | None

Gather the final state to rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[complex128] or None

the final state at rank 0 of the Ansatz subcommunicator, None otherwise

get_probabilities() np.ndarray[np.float64] | None

Gather probabilities computed from the final state at rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[float64] or None

1-D real array of state probabilities at rank 0 of the Ansatz subcommunicator, None otherwise

objective(variational_parameters: list[float] | np.ndarray[float]) float

Compute the objective function at variational parameters variational_parameters.

Parameters:
variational_parameterslist or ndarray[float]
Returns:
float

objective function value

print_optimiser_result()

Print the result returned from the optimiser for the last QVA simulation.

print_result()

Print a summary of the results of the last QVA simulation.

save(file_name: str, config_name: str, action: str = 'a')

Write the final state, observables and results summary to a HDf5 file.

Parameters:
file_namestr

file path to saved data

config_namestr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ to overwrite, by default ‘a’

Notes

Data is saved into a *.h5 file with the following structure.

├── config_name
    ├── final_state 
    ├── observables

The minimization result is saved in the ‘minimize_result’ attribute of ‘config_name’ as a formatted string.

Multiple configurations with a unique config_name can be stored in the same .h5 file. HDF5 files are supported in python by the h5py package. With it, a saved configuration can be accessed as follows:

import h5py

config_name = "my_simulation"

f = h5py.File(file_name + ".h5", "r") final_state =
np.array(f[config_name]['final_state']).view(np.complex128)
eigenvalues =
np.array(f[config_name]['eigenvalues']).view(np.complex128)
observables =
np.array(f[config_name]['observables']).view(np.float64)

print(f["my_simulation"].attrs["minimize_result"])

Warning

The "final_state" and "observables" datasets are saved using Fortran subroutines which make use of parallel HDF5.

The complex values of the final_state array are saved as a compound datatype consisting of contiguous double precision reals. This is equivalent to the np.complex128 NumPy datatype. To access this data without a loss of precision in python, the user must set the view of the NumPy array to np.complex128, rather than casting it to np.complex128 using the dtype keyword.

Similarly, the observables array, which is saved as an array of double-precision reals, should have its view set to np.float64.

set_depth(depth: int)

Set the simulated ansatz depth.

Parameters:
depthint

number of ansatz iterations

set_free_params(function: Callable, free_params_dict: dict | None = None)

Optimise over a subset of the free variational parameters.

Parameters:
functioncallable

Free Parameters Function

free_params_dictdict, optional

FunctionDict for the Free Parameters Function

Examples

A Free Params Function that restricts optimisation to the last ansatz iteration:

def last_ansatz_iteration(total_params, ansatz_depth):
   return list(
       range((ansatz_depth - 1) * total_params, ansatz_depth * total_params)
   )
set_initial_state(function: Callable, initial_state_dict: dict | None = None)

Define the initial state.

Parameters:
functioncallable

Initial State Function

initial_state_dictFunctionDict, optional

FunctionDict for the Initial State Function

set_log(filename: str, label: str, action: str = 'a')

Creates a CSV in which to save simulation results after a call to execute().

Parameters:
filenamestr

path to the log file

labelstr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ overwrite, by default ‘a’

set_observables(function: Callable | int, observable_dict: dict | None = None)

Specify the observables.

Parameters:
functioncallable or int

an Observables Function or an integer specifying the index of a phase-shift unitary in the list passed to the set_observables() whose exponent contains the observable vector.

observables_dict: FunctionDict, optional

FunctionDict for the Observables Function

set_optimiser(optimiser: str, optimiser_args: dict | None = None, optimiser_log: list[str] | None = None)

Define the classical optimiser for QVA simulation.

Optionally allows for specification of arguments passed to the optimiser and fields in the optimiser dictionary to write to the log file (see set_log()). QuOp_MPI supports optimisers provided by SciPy through its minimize method minimize and optimisers provided by the NLopt package with respect to minimisation with scalar constraints through a SciPy-like interface.

Parameters:
optimiser: {‘scipy’, ‘nlopt’}

‘scipy’ to use the SciPy, ‘nlopt’ to use NLopt, or a callable QuOp_MPI-compatible optimisation function.

optimiser_args: dict

arguments to pass to the optimiser

optimiser_log: list[str]

results of the optimisation process are stored in a dictionary. These values may be logged by passing a list of the corresponding keys

Examples

The default optimiser is the BFGS algorithm, which is set internally as follows:

Ansatz.set_optimiser( 'scipy',
        {'method':'BFGS','options':{'gtol':1e-3}},
                    ['fun','nfev','success'])
set_parallel_jacobian(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, method: str | Callable = 'forward', h: float | None = None)

Specify optimisation of the variational parameters using parallel computation of the jacobian.

This creates MPI subcommunicators containing duplicates of the Ansatz instance which return partial derivative information to the root MPI process during optimisation.

Parameters:
nodes_per_subcommint

MPI nodes per subcommunicator

processes_per_nodeint

MPI processors associated with each node

maxcommint

maximum number of created MPI subcommunicators (and Ansatz instance duplicates) if nodes_per_subcomm > 1, or the maximum number of MPI subcommunicators per node if nodes_per_subcomm = 1

method :{‘forward’, ‘central’} or callable, optional

‘forward’ or ‘central’ to used the forward difference or central difference method for numerical approximation of the partial derivatives, or a QuOp Jacobian Function, by default ‘forward’

hfloat, optional

step-size used by the forward or central difference methods, by default np.sqrt(np.finfo(float).eps)

set_sampling(sample_block_size: int, function: Callable | None = None, max_sample_iterations: int = 100, sampling_dict: dict | None = None)

Compute the objective function using simulated sampling.

Samples are taken in blocks of sample_block_size. These are passed as a list of lists to function (a Sampling Function), which returns a value for expectation value/objective function and a boolean that indicates wether the sampled result should be passed to the classical optimiser.

If function is None, the objective function is computed as the mean of sample_block_size shots.

Parameters:
sample_block_sizeint

number of shots taken between successive computation of the expectation value/objective function

functioncallable, optional

Sampling Function

max_sample_iterationsint, optional

maximum number of sample blocks per computation of the expectation value/objective function, overrides the boolean returned by function, by default 100

sampling_dictFunctionDict, optional

FunctionDict for the Sampling Function

set_seed(seed: int)

Integer for seeding of random number generation.

Parameters:
seedint

seeds the generation of random parameters

set_unitaries(unitaries: list[Unitary])

Define the ansatz unitary.

Unitaries are passed as a python list in order of application from left to right.

Parameters:
unitaries: list[unitary]

list of unitaries specifying the action of one ansatz iteration

unset_sampling()

Revert to simulation using exact computation of the objective function.

class quop_mpi.Unitary(operator_function: Callable, operator_n_params: int = 0, operator_dict: dict | None = None, parameter_function: Callable | None = None, param_dict: dict | None = None, unitary_n_params: int = 1)

Base class for a unitary.

A unitary is derived from the Unitary class and implements simulation of a specfic unitary through definition of the following methods:

A list of unitary instances passed to quop_mpi.Ansatz.set_unitaries() defines the ansatz unitary of a QVA. After initialisation, unitary instances are managed by the quop_mpi.Ansatz class and calls to unitary methods are not made explicitly.

See quop_mpi.propagator for predefined unitary subclasses.

Associated QuOp Functions:

The following attributes are common to all unitary instances.

Attributes:
final_state

The system state after the action of the unitary.

initial_parameters

Initial variational parameters returned from the user-defined Parameter Function.

initial_state

The initial state of the quantum system.

n_params

The total number of unitary and operator parameterising the unitary.

operator_function

The user-defined Operator Function.

operator_dict

A FunctionDict of additional position and keyword arguments for the Operator Function.

operator

The operator object returned by the Operator Function.

operator_n_params

Number of variational operator parameters.

parameter_function

The user-defined Parameter Function.

param_dict

A FunctionDict of additional position and keyword arguments for the Parameter Function.

planner

If True, the parallel partitioning scheme returned by plan() takes precedence over non-planner unitaries and unitaries that appear later in the ansatz unitary list supplied to quop_mpi.Ansatz.set_unitaries().

seed

Integer for seeding random number generation, shared with quop_mpi.Ansatz.

system_size

The size of the simulated quantum system, shared with quop_mpi.Ansatz.

unitary_n_params

The number of unitary parameters.

unitary_type

A string labeling the unitary type (e.g. “diagonal” or “sparse”).

variational_parameters

Operator variational parameters. If present as an argument of the Operator Function, a real array of size operator_n_params is passed to the Operator Function.

MPI_COMM

MPI Intracommunicator, shared with quop_mpi.Ansatz.

alloc_local

The size of the array storing the :term`operator` if the operator is an array (equal to local_i otherwise). The second return value of plan().

lb

The lower global index of the local system state partition.

ub

The upper global index of the local system state partition.

local_i

The size of the local system state partition. The first return value of plan()

local_i_offset

The global index offset of the local system state partition.

partition_table

1-D integer array describing the global partitioning scheme such that for a given MPI rank partition_table[rank + 1] - partition_table[rank] = local_i

copy_plan(ex_unitary: Unitary)

Perform any setup required by the propagation method called in propagate().

When implemented, copy_plan performs the same internal operations as plan() using the local_i and alloc_local attributes of ex_unitary. Does not return [local_i, alloc_local].

Warning

Not implemented by the base Unitary class.

Parameters:
ex_unitaryunitary

a unitary instance with computed local_i and alloc_local attributes

destroy()

Free memory allocated by Python extension modules in plan() or copy_plan(). collector.

Memory allocated by compiled Python extension modules is typically not managed by the Python garbage collector. These allocations must be freed via relevant methods in the extension module to prevent the occurrence of memory leaks.

Warning

Not implemented by the base Unitary class.

plan(system_size: int, MPI_COMM: mpi4py.MPI.Intracomm)

Plan the partitioning scheme used by an quop_mpi.Ansatz instance and performs any other tasks required by propagate().

An implemented plan returns (local_i, alloc_local). Data structures and allocation required by the propagation method called in propagate() are assigned to attributes of the Unitary instance.

The alloc_local return value specifies the size of the local system state arrays initial_state and final_state. In most cases alloc_local == local_i, however alloc_local > local_i may be required by particular external propagation methods (e.g. the parallel FFTW).

Warning

Not implemented by the base Unitary class.

Parameters:
system_sizeint

size of the simulated quantum system.

MPI_COMMIntracomm

MPI communicator over which the Ansatz observables, initial state and final state are partitioned.

Returns:
(int, int)

number of elements in a row-wise partitioning of the system state and size to allocate for the initial_state and final_state arrays

Examples

def plan(self, system_size, MPI_COMM):

    local_i = system_size  // MPI_COMM.size

    local_i = (
        system_size - local_i * MPI_COMM.rank if MPI_COMM.rank == 0
        else local_i
    )

    alloc_local = local_i

    return local_i, alloc_local
propagate(x: np.ndarray[np.float64])

Simulation of the action of a :term`unitary`.

When implemented, propagate contains a call to a method (typically a contained in a complied Python extension module) that takes the class attributes initial_state, final_state and MPI_COMM, together with attributes describing the parallel partitioning scheme and variational parameters x, as input. The action of the unitary is computed in MPI parallel, with the computed result written to final_state.

Warning

Not implemented by the base Unitary class.

Parameters:
xndarray[float64]

a 1-D real array of n_params variational parameters

Examples

def propagate(self, x):

    external_propagator(
        x, self.partition_table, self.initial_state,
        self.final_state, self.MPI_COMM )

algorithm

combinatorial

Predefined QVAs for combinatorial optimisation problems.

Note

The following compatible Operator Functions may be imported from the combinatorial:

class quop_mpi.algorithm.combinatorial.qaoa(system_size: int, MPI_communicator: mpi4py.MPI.Intracomm = mpi4py.MPI.COMM_WORLD)

Simulate the QAOA.

See quop_mpi.Ansatz.

Parameters:
system_sizeint

system size of the simulated QVA

MPI_COMMIntracomm, optional

MPI communicator, default mpi4py.MPI.COMM_WORLD

benchmark(ansatz_depths: Iterable[int], repeats: int, param_persist: bool = False, verbose: bool = True, filename: str | None = None, label: str = 'test', save_action: str = 'a', time_limit: int | None = None, suspend_path: str | None = None)

A method by which to study how a QVA performs as the number of ansatz iterations increases.

Parameters:
ansatz_depthsiterable[int]

integers specifying a sequence of ansatz depths

repeatsint

number of repeats at each ansatz depth

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results by default True

filenamestr or None, optional

name of *.h5 file in which to save() the optimised system state and observables

labelstr, optional

if filename is not None, *.h5 data will be saved as “filename/label_depth_repeat”, by default "test"

save_action{‘a’, ‘w’}, optional

action taken during first file write: ‘a’ to append, ‘w’ to overwrite, by default ‘a’

time_limitint or None, optional

total allocated in-program time in seconds, if the time of the previous simulation exceeds the time remaining, the benchmark is suspended

suspend_pathstr or None, optional

path to the suspend file if time_limit is not None

evaluate(variational_parameters: list[float] | np.ndarray[float]) float

Lazily computes the objective function value.

The Ansatz instance stores the last variational parameters passed to evaluate and the corresponding objective function value. If the input variational parameters match, re-computation of the final state is skipped and the previously computed objective function value is returned.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

Returns:
float

objective function value

evolve_state(variational_parameters: list[float] | np.ndarray[float])

Compute the system state under the action of the ansatz unitary.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters.

See also

set_unitaries()
execute(variational_parameters: list[float] | np.ndarray[float] = None)

Simulate a QVA.

If variational_parameters is None, initial parameter values are generated using the Parameter Function of the corresponding unitary instances.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

gen_initial_params(ansatz_depth: int = None) np.ndarray[np.float64]

Generate initial variational parameters.

Values are generated using the Parameter Function associated with each unitary passed to the set_unitaries() method.

Note

If ansatz_depth is None the ansatz depth defaults to 1 or the depth specified by the set_depth() method.

Parameters:
ansatz_depthint, optional

number of ansatz iterations

Returns:
ndarray[float64]

1-D (ansatz_depth * total_params,) real array of variational parameters

get_expectation_value() float

Compute the objective function at the current value of variational_parameters().

Returns:
float

objective function value

get_final_state() np.ndarray[np.complex128] | None

Gather the final state to rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[complex128] or None

the final state at rank 0 of the Ansatz subcommunicator, None otherwise

get_probabilities() np.ndarray[np.float64] | None

Gather probabilities computed from the final state at rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[float64] or None

1-D real array of state probabilities at rank 0 of the Ansatz subcommunicator, None otherwise

objective(variational_parameters: list[float] | np.ndarray[float]) float

Compute the objective function at variational parameters variational_parameters.

Parameters:
variational_parameterslist or ndarray[float]
Returns:
float

objective function value

print_optimiser_result()

Print the result returned from the optimiser for the last QVA simulation.

print_result()

Print a summary of the results of the last QVA simulation.

save(file_name: str, config_name: str, action: str = 'a')

Write the final state, observables and results summary to a HDf5 file.

Parameters:
file_namestr

file path to saved data

config_namestr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ to overwrite, by default ‘a’

Notes

Data is saved into a *.h5 file with the following structure.

├── config_name
    ├── final_state 
    ├── observables

The minimization result is saved in the ‘minimize_result’ attribute of ‘config_name’ as a formatted string.

Multiple configurations with a unique config_name can be stored in the same .h5 file. HDF5 files are supported in python by the h5py package. With it, a saved configuration can be accessed as follows:

import h5py

config_name = "my_simulation"

f = h5py.File(file_name + ".h5", "r") final_state =
np.array(f[config_name]['final_state']).view(np.complex128)
eigenvalues =
np.array(f[config_name]['eigenvalues']).view(np.complex128)
observables =
np.array(f[config_name]['observables']).view(np.float64)

print(f["my_simulation"].attrs["minimize_result"])

Warning

The "final_state" and "observables" datasets are saved using Fortran subroutines which make use of parallel HDF5.

The complex values of the final_state array are saved as a compound datatype consisting of contiguous double precision reals. This is equivalent to the np.complex128 NumPy datatype. To access this data without a loss of precision in python, the user must set the view of the NumPy array to np.complex128, rather than casting it to np.complex128 using the dtype keyword.

Similarly, the observables array, which is saved as an array of double-precision reals, should have its view set to np.float64.

set_depth(depth: int)

Set the simulated ansatz depth.

Parameters:
depthint

number of ansatz iterations

set_free_params(function: Callable, free_params_dict: dict | None = None)

Optimise over a subset of the free variational parameters.

Parameters:
functioncallable

Free Parameters Function

free_params_dictdict, optional

FunctionDict for the Free Parameters Function

Examples

A Free Params Function that restricts optimisation to the last ansatz iteration:

def last_ansatz_iteration(total_params, ansatz_depth):
   return list(
       range((ansatz_depth - 1) * total_params, ansatz_depth * total_params)
   )
set_initial_state(function: Callable, initial_state_dict: dict | None = None)

Define the initial state.

Parameters:
functioncallable

Initial State Function

initial_state_dictFunctionDict, optional

FunctionDict for the Initial State Function

set_log(filename: str, label: str, action: str = 'a')

Creates a CSV in which to save simulation results after a call to execute().

Parameters:
filenamestr

path to the log file

labelstr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ overwrite, by default ‘a’

set_observables(function: Callable | int, observable_dict: dict | None = None)

Specify the observables.

Parameters:
functioncallable or int

an Observables Function or an integer specifying the index of a phase-shift unitary in the list passed to the set_observables() whose exponent contains the observable vector.

observables_dict: FunctionDict, optional

FunctionDict for the Observables Function

set_optimiser(optimiser: str, optimiser_args: dict | None = None, optimiser_log: list[str] | None = None)

Define the classical optimiser for QVA simulation.

Optionally allows for specification of arguments passed to the optimiser and fields in the optimiser dictionary to write to the log file (see set_log()). QuOp_MPI supports optimisers provided by SciPy through its minimize method minimize and optimisers provided by the NLopt package with respect to minimisation with scalar constraints through a SciPy-like interface.

Parameters:
optimiser: {‘scipy’, ‘nlopt’}

‘scipy’ to use the SciPy, ‘nlopt’ to use NLopt, or a callable QuOp_MPI-compatible optimisation function.

optimiser_args: dict

arguments to pass to the optimiser

optimiser_log: list[str]

results of the optimisation process are stored in a dictionary. These values may be logged by passing a list of the corresponding keys

Examples

The default optimiser is the BFGS algorithm, which is set internally as follows:

Ansatz.set_optimiser( 'scipy',
        {'method':'BFGS','options':{'gtol':1e-3}},
                    ['fun','nfev','success'])
set_parallel_jacobian(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, method: str | Callable = 'forward', h: float | None = None)

Specify optimisation of the variational parameters using parallel computation of the jacobian.

This creates MPI subcommunicators containing duplicates of the Ansatz instance which return partial derivative information to the root MPI process during optimisation.

Parameters:
nodes_per_subcommint

MPI nodes per subcommunicator

processes_per_nodeint

MPI processors associated with each node

maxcommint

maximum number of created MPI subcommunicators (and Ansatz instance duplicates) if nodes_per_subcomm > 1, or the maximum number of MPI subcommunicators per node if nodes_per_subcomm = 1

method :{‘forward’, ‘central’} or callable, optional

‘forward’ or ‘central’ to used the forward difference or central difference method for numerical approximation of the partial derivatives, or a QuOp Jacobian Function, by default ‘forward’

hfloat, optional

step-size used by the forward or central difference methods, by default np.sqrt(np.finfo(float).eps)

set_params(param_function: Callable, param_dict: dict | None = None)

Define the Parameter Function for the phase-shift and mixing unitaries.

Parameters:
param_functionCallable

a Parameter Function

param_dictFunctionDict

FunctionDict for param_function

set_qualities(function: Callable, observables_dict: dict | None = None)

Define the observables and phase-shift unitary operator

Parameters:
functionCallable

an Operator Function

observables_dictFunctionDict, optional

FunctionDict for function

set_sampling(sample_block_size: int, function: Callable | None = None, max_sample_iterations: int = 100, sampling_dict: dict | None = None)

Compute the objective function using simulated sampling.

Samples are taken in blocks of sample_block_size. These are passed as a list of lists to function (a Sampling Function), which returns a value for expectation value/objective function and a boolean that indicates wether the sampled result should be passed to the classical optimiser.

If function is None, the objective function is computed as the mean of sample_block_size shots.

Parameters:
sample_block_sizeint

number of shots taken between successive computation of the expectation value/objective function

functioncallable, optional

Sampling Function

max_sample_iterationsint, optional

maximum number of sample blocks per computation of the expectation value/objective function, overrides the boolean returned by function, by default 100

sampling_dictFunctionDict, optional

FunctionDict for the Sampling Function

set_seed(seed: int)

Integer for seeding of random number generation.

Parameters:
seedint

seeds the generation of random parameters

set_unitaries(unitaries: list[Unitary])

Define the ansatz unitary.

Unitaries are passed as a python list in order of application from left to right.

Parameters:
unitaries: list[unitary]

list of unitaries specifying the action of one ansatz iteration

unset_sampling()

Revert to simulation using exact computation of the objective function.

class quop_mpi.algorithm.combinatorial.qwoa(system_size: int, MPI_communicator: mpi4py.MPI.Intracomm = mpi4py.MPI.COMM_WORLD)

Simulate the QWOA.

See quop_mpi.Ansatz.

Parameters:
system_sizeint

system size of the simulated QVA

MPI_COMMIntracomm, optional

MPI communicator, default mpi4py.MPI.COMM_WORLD

benchmark(ansatz_depths: Iterable[int], repeats: int, param_persist: bool = False, verbose: bool = True, filename: str | None = None, label: str = 'test', save_action: str = 'a', time_limit: int | None = None, suspend_path: str | None = None)

A method by which to study how a QVA performs as the number of ansatz iterations increases.

Parameters:
ansatz_depthsiterable[int]

integers specifying a sequence of ansatz depths

repeatsint

number of repeats at each ansatz depth

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results by default True

filenamestr or None, optional

name of *.h5 file in which to save() the optimised system state and observables

labelstr, optional

if filename is not None, *.h5 data will be saved as “filename/label_depth_repeat”, by default "test"

save_action{‘a’, ‘w’}, optional

action taken during first file write: ‘a’ to append, ‘w’ to overwrite, by default ‘a’

time_limitint or None, optional

total allocated in-program time in seconds, if the time of the previous simulation exceeds the time remaining, the benchmark is suspended

suspend_pathstr or None, optional

path to the suspend file if time_limit is not None

evaluate(variational_parameters: list[float] | np.ndarray[float]) float

Lazily computes the objective function value.

The Ansatz instance stores the last variational parameters passed to evaluate and the corresponding objective function value. If the input variational parameters match, re-computation of the final state is skipped and the previously computed objective function value is returned.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

Returns:
float

objective function value

evolve_state(variational_parameters: list[float] | np.ndarray[float])

Compute the system state under the action of the ansatz unitary.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters.

See also

set_unitaries()
execute(variational_parameters: list[float] | np.ndarray[float] = None)

Simulate a QVA.

If variational_parameters is None, initial parameter values are generated using the Parameter Function of the corresponding unitary instances.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

gen_initial_params(ansatz_depth: int = None) np.ndarray[np.float64]

Generate initial variational parameters.

Values are generated using the Parameter Function associated with each unitary passed to the set_unitaries() method.

Note

If ansatz_depth is None the ansatz depth defaults to 1 or the depth specified by the set_depth() method.

Parameters:
ansatz_depthint, optional

number of ansatz iterations

Returns:
ndarray[float64]

1-D (ansatz_depth * total_params,) real array of variational parameters

get_expectation_value() float

Compute the objective function at the current value of variational_parameters().

Returns:
float

objective function value

get_final_state() np.ndarray[np.complex128] | None

Gather the final state to rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[complex128] or None

the final state at rank 0 of the Ansatz subcommunicator, None otherwise

get_probabilities() np.ndarray[np.float64] | None

Gather probabilities computed from the final state at rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[float64] or None

1-D real array of state probabilities at rank 0 of the Ansatz subcommunicator, None otherwise

objective(variational_parameters: list[float] | np.ndarray[float]) float

Compute the objective function at variational parameters variational_parameters.

Parameters:
variational_parameterslist or ndarray[float]
Returns:
float

objective function value

print_optimiser_result()

Print the result returned from the optimiser for the last QVA simulation.

print_result()

Print a summary of the results of the last QVA simulation.

save(file_name: str, config_name: str, action: str = 'a')

Write the final state, observables and results summary to a HDf5 file.

Parameters:
file_namestr

file path to saved data

config_namestr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ to overwrite, by default ‘a’

Notes

Data is saved into a *.h5 file with the following structure.

├── config_name
    ├── final_state 
    ├── observables

The minimization result is saved in the ‘minimize_result’ attribute of ‘config_name’ as a formatted string.

Multiple configurations with a unique config_name can be stored in the same .h5 file. HDF5 files are supported in python by the h5py package. With it, a saved configuration can be accessed as follows:

import h5py

config_name = "my_simulation"

f = h5py.File(file_name + ".h5", "r") final_state =
np.array(f[config_name]['final_state']).view(np.complex128)
eigenvalues =
np.array(f[config_name]['eigenvalues']).view(np.complex128)
observables =
np.array(f[config_name]['observables']).view(np.float64)

print(f["my_simulation"].attrs["minimize_result"])

Warning

The "final_state" and "observables" datasets are saved using Fortran subroutines which make use of parallel HDF5.

The complex values of the final_state array are saved as a compound datatype consisting of contiguous double precision reals. This is equivalent to the np.complex128 NumPy datatype. To access this data without a loss of precision in python, the user must set the view of the NumPy array to np.complex128, rather than casting it to np.complex128 using the dtype keyword.

Similarly, the observables array, which is saved as an array of double-precision reals, should have its view set to np.float64.

set_depth(depth: int)

Set the simulated ansatz depth.

Parameters:
depthint

number of ansatz iterations

set_free_params(function: Callable, free_params_dict: dict | None = None)

Optimise over a subset of the free variational parameters.

Parameters:
functioncallable

Free Parameters Function

free_params_dictdict, optional

FunctionDict for the Free Parameters Function

Examples

A Free Params Function that restricts optimisation to the last ansatz iteration:

def last_ansatz_iteration(total_params, ansatz_depth):
   return list(
       range((ansatz_depth - 1) * total_params, ansatz_depth * total_params)
   )
set_initial_state(function: Callable, initial_state_dict: dict | None = None)

Define the initial state.

Parameters:
functioncallable

Initial State Function

initial_state_dictFunctionDict, optional

FunctionDict for the Initial State Function

set_log(filename: str, label: str, action: str = 'a')

Creates a CSV in which to save simulation results after a call to execute().

Parameters:
filenamestr

path to the log file

labelstr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ overwrite, by default ‘a’

set_observables(function: Callable | int, observable_dict: dict | None = None)

Specify the observables.

Parameters:
functioncallable or int

an Observables Function or an integer specifying the index of a phase-shift unitary in the list passed to the set_observables() whose exponent contains the observable vector.

observables_dict: FunctionDict, optional

FunctionDict for the Observables Function

set_optimiser(optimiser: str, optimiser_args: dict | None = None, optimiser_log: list[str] | None = None)

Define the classical optimiser for QVA simulation.

Optionally allows for specification of arguments passed to the optimiser and fields in the optimiser dictionary to write to the log file (see set_log()). QuOp_MPI supports optimisers provided by SciPy through its minimize method minimize and optimisers provided by the NLopt package with respect to minimisation with scalar constraints through a SciPy-like interface.

Parameters:
optimiser: {‘scipy’, ‘nlopt’}

‘scipy’ to use the SciPy, ‘nlopt’ to use NLopt, or a callable QuOp_MPI-compatible optimisation function.

optimiser_args: dict

arguments to pass to the optimiser

optimiser_log: list[str]

results of the optimisation process are stored in a dictionary. These values may be logged by passing a list of the corresponding keys

Examples

The default optimiser is the BFGS algorithm, which is set internally as follows:

Ansatz.set_optimiser( 'scipy',
        {'method':'BFGS','options':{'gtol':1e-3}},
                    ['fun','nfev','success'])
set_parallel_jacobian(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, method: str | Callable = 'forward', h: float | None = None)

Specify optimisation of the variational parameters using parallel computation of the jacobian.

This creates MPI subcommunicators containing duplicates of the Ansatz instance which return partial derivative information to the root MPI process during optimisation.

Parameters:
nodes_per_subcommint

MPI nodes per subcommunicator

processes_per_nodeint

MPI processors associated with each node

maxcommint

maximum number of created MPI subcommunicators (and Ansatz instance duplicates) if nodes_per_subcomm > 1, or the maximum number of MPI subcommunicators per node if nodes_per_subcomm = 1

method :{‘forward’, ‘central’} or callable, optional

‘forward’ or ‘central’ to used the forward difference or central difference method for numerical approximation of the partial derivatives, or a QuOp Jacobian Function, by default ‘forward’

hfloat, optional

step-size used by the forward or central difference methods, by default np.sqrt(np.finfo(float).eps)

set_params(param_function, param_dict=None)

Define the Parameter Function for the phase-shift and mixing unitaries.

Parameters:
param_functionCallable

a Parameter Function

param_dictFunctionDict

FunctionDict for param_function

set_qualities(function, observable_dict=None)

Define the observables and phase-shift unitary operator

Parameters:
functionCallable

an Operator Function

observable_dictFunctionDict, optional

FunctionDict for function

set_sampling(sample_block_size: int, function: Callable | None = None, max_sample_iterations: int = 100, sampling_dict: dict | None = None)

Compute the objective function using simulated sampling.

Samples are taken in blocks of sample_block_size. These are passed as a list of lists to function (a Sampling Function), which returns a value for expectation value/objective function and a boolean that indicates wether the sampled result should be passed to the classical optimiser.

If function is None, the objective function is computed as the mean of sample_block_size shots.

Parameters:
sample_block_sizeint

number of shots taken between successive computation of the expectation value/objective function

functioncallable, optional

Sampling Function

max_sample_iterationsint, optional

maximum number of sample blocks per computation of the expectation value/objective function, overrides the boolean returned by function, by default 100

sampling_dictFunctionDict, optional

FunctionDict for the Sampling Function

set_seed(seed: int)

Integer for seeding of random number generation.

Parameters:
seedint

seeds the generation of random parameters

set_unitaries(unitaries: list[Unitary])

Define the ansatz unitary.

Unitaries are passed as a python list in order of application from left to right.

Parameters:
unitaries: list[unitary]

list of unitaries specifying the action of one ansatz iteration

unset_sampling()

Revert to simulation using exact computation of the objective function.

multivariable

Predefined QVAs for the optimisation of continuous multivariable functions.

Note

The following compatible Operator Functions may be imported from the multivariable :

  • setup_cartesian()

  • cartesian()

  • cartesian_scaled()

class quop_mpi.algorithm.multivariable.qmoa(Ns: list[int], MPI_COMM: mpi4py.MPI.Intracomm = mpi4py.MPI.COMM_WORLD)

Simulate the QMOA.

A QVA for the optimisation of continuous multivariable functions.

Parameters:
Nslist[int]

the number of grid points in each each coordinate dimension

MPI_communicatorIntracomm, optional

MPI Intracomm, by default MPI.COMM_WORLD

benchmark(ansatz_depths: Iterable[int], repeats: int, param_persist: bool = False, verbose: bool = True, filename: str | None = None, label: str = 'test', save_action: str = 'a', time_limit: int | None = None, suspend_path: str | None = None)

A method by which to study how a QVA performs as the number of ansatz iterations increases.

Parameters:
ansatz_depthsiterable[int]

integers specifying a sequence of ansatz depths

repeatsint

number of repeats at each ansatz depth

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results by default True

filenamestr or None, optional

name of *.h5 file in which to save() the optimised system state and observables

labelstr, optional

if filename is not None, *.h5 data will be saved as “filename/label_depth_repeat”, by default "test"

save_action{‘a’, ‘w’}, optional

action taken during first file write: ‘a’ to append, ‘w’ to overwrite, by default ‘a’

time_limitint or None, optional

total allocated in-program time in seconds, if the time of the previous simulation exceeds the time remaining, the benchmark is suspended

suspend_pathstr or None, optional

path to the suspend file if time_limit is not None

evaluate(variational_parameters: list[float] | np.ndarray[float]) float

Lazily computes the objective function value.

The Ansatz instance stores the last variational parameters passed to evaluate and the corresponding objective function value. If the input variational parameters match, re-computation of the final state is skipped and the previously computed objective function value is returned.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

Returns:
float

objective function value

evolve_state(variational_parameters: list[float] | np.ndarray[float])

Compute the system state under the action of the ansatz unitary.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters.

See also

set_unitaries()
execute(variational_parameters: list[float] | np.ndarray[float] = None)

Simulate a QVA.

If variational_parameters is None, initial parameter values are generated using the Parameter Function of the corresponding unitary instances.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

gen_initial_params(ansatz_depth: int = None) np.ndarray[np.float64]

Generate initial variational parameters.

Values are generated using the Parameter Function associated with each unitary passed to the set_unitaries() method.

Note

If ansatz_depth is None the ansatz depth defaults to 1 or the depth specified by the set_depth() method.

Parameters:
ansatz_depthint, optional

number of ansatz iterations

Returns:
ndarray[float64]

1-D (ansatz_depth * total_params,) real array of variational parameters

get_expectation_value() float

Compute the objective function at the current value of variational_parameters().

Returns:
float

objective function value

get_final_state() np.ndarray[np.complex128] | None

Gather the final state to rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[complex128] or None

the final state at rank 0 of the Ansatz subcommunicator, None otherwise

get_probabilities() np.ndarray[np.float64] | None

Gather probabilities computed from the final state at rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[float64] or None

1-D real array of state probabilities at rank 0 of the Ansatz subcommunicator, None otherwise

grid_point_from_index(index: int) np.ndarray[np.float64]

Retrieve the corresponding coordinate point from a global index of the system state.

Parameters:
indexint

global index of the system state

Returns:
ndarray[float64]

a 1-D real array containing a grid point in Cartesian coordinates

objective(variational_parameters: list[float] | np.ndarray[float]) float

Compute the objective function at variational parameters variational_parameters.

Parameters:
variational_parameterslist or ndarray[float]
Returns:
float

objective function value

print_optimiser_result()

Print the result returned from the optimiser for the last QVA simulation.

print_result()

Print a summary of the results of the last QVA simulation.

save(file_name: str, config_name: str, action: str = 'a')

Write the final state, observables and results summary to a HDf5 file.

Parameters:
file_namestr

file path to saved data

config_namestr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ to overwrite, by default ‘a’

Notes

Data is saved into a *.h5 file with the following structure.

├── config_name
    ├── final_state 
    ├── observables

The minimization result is saved in the ‘minimize_result’ attribute of ‘config_name’ as a formatted string.

Multiple configurations with a unique config_name can be stored in the same .h5 file. HDF5 files are supported in python by the h5py package. With it, a saved configuration can be accessed as follows:

import h5py

config_name = "my_simulation"

f = h5py.File(file_name + ".h5", "r") final_state =
np.array(f[config_name]['final_state']).view(np.complex128)
eigenvalues =
np.array(f[config_name]['eigenvalues']).view(np.complex128)
observables =
np.array(f[config_name]['observables']).view(np.float64)

print(f["my_simulation"].attrs["minimize_result"])

Warning

The "final_state" and "observables" datasets are saved using Fortran subroutines which make use of parallel HDF5.

The complex values of the final_state array are saved as a compound datatype consisting of contiguous double precision reals. This is equivalent to the np.complex128 NumPy datatype. To access this data without a loss of precision in python, the user must set the view of the NumPy array to np.complex128, rather than casting it to np.complex128 using the dtype keyword.

Similarly, the observables array, which is saved as an array of double-precision reals, should have its view set to np.float64.

set_depth(depth: int)

Set the simulated ansatz depth.

Parameters:
depthint

number of ansatz iterations

set_free_params(function: Callable, free_params_dict: dict | None = None)

Optimise over a subset of the free variational parameters.

Parameters:
functioncallable

Free Parameters Function

free_params_dictdict, optional

FunctionDict for the Free Parameters Function

Examples

A Free Params Function that restricts optimisation to the last ansatz iteration:

def last_ansatz_iteration(total_params, ansatz_depth):
   return list(
       range((ansatz_depth - 1) * total_params, ansatz_depth * total_params)
   )
set_independent_t(independent: bool)

Specify simulation with or without independent unitary parameters (walk times) over each coordinate dimension.

Parameters:
independentbool

simulate a unique walk time in each coordinate dimension if True, all walk times share the same value if False

set_initial_state(function: Callable, initial_state_dict: dict | None = None)

Define the initial state.

Parameters:
functioncallable

Initial State Function

initial_state_dictFunctionDict, optional

FunctionDict for the Initial State Function

set_log(filename: str, label: str, action: str = 'a')

Creates a CSV in which to save simulation results after a call to execute().

Parameters:
filenamestr

path to the log file

labelstr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ overwrite, by default ‘a’

set_mixer(Cs: list[int])

Set the circulant mixing unitary operator in each coordinate dimension.

Parameters:
Cslist[int]

specifies the “i-th” symmetric circulant matrix with edges weights 1, Cs[j] == 1 cycle graph, Cs[j] > system_size // 2 complete graph

See also

quop_mpi.propagator.composite.ith()
set_observables(function: Callable | int, observable_dict: dict | None = None)

Specify the observables.

Parameters:
functioncallable or int

an Observables Function or an integer specifying the index of a phase-shift unitary in the list passed to the set_observables() whose exponent contains the observable vector.

observables_dict: FunctionDict, optional

FunctionDict for the Observables Function

set_optimiser(optimiser: str, optimiser_args: dict | None = None, optimiser_log: list[str] | None = None)

Define the classical optimiser for QVA simulation.

Optionally allows for specification of arguments passed to the optimiser and fields in the optimiser dictionary to write to the log file (see set_log()). QuOp_MPI supports optimisers provided by SciPy through its minimize method minimize and optimisers provided by the NLopt package with respect to minimisation with scalar constraints through a SciPy-like interface.

Parameters:
optimiser: {‘scipy’, ‘nlopt’}

‘scipy’ to use the SciPy, ‘nlopt’ to use NLopt, or a callable QuOp_MPI-compatible optimisation function.

optimiser_args: dict

arguments to pass to the optimiser

optimiser_log: list[str]

results of the optimisation process are stored in a dictionary. These values may be logged by passing a list of the corresponding keys

Examples

The default optimiser is the BFGS algorithm, which is set internally as follows:

Ansatz.set_optimiser( 'scipy',
        {'method':'BFGS','options':{'gtol':1e-3}},
                    ['fun','nfev','success'])
set_parallel_jacobian(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, method: str | Callable = 'forward', h: float | None = None)

Specify optimisation of the variational parameters using parallel computation of the jacobian.

This creates MPI subcommunicators containing duplicates of the Ansatz instance which return partial derivative information to the root MPI process during optimisation.

Parameters:
nodes_per_subcommint

MPI nodes per subcommunicator

processes_per_nodeint

MPI processors associated with each node

maxcommint

maximum number of created MPI subcommunicators (and Ansatz instance duplicates) if nodes_per_subcomm > 1, or the maximum number of MPI subcommunicators per node if nodes_per_subcomm = 1

method :{‘forward’, ‘central’} or callable, optional

‘forward’ or ‘central’ to used the forward difference or central difference method for numerical approximation of the partial derivatives, or a QuOp Jacobian Function, by default ‘forward’

hfloat, optional

step-size used by the forward or central difference methods, by default np.sqrt(np.finfo(float).eps)

set_params(param_function: Callable, param_dict: dict | None = None)

Define the Parameter Function for the phase-shift and mixing unitaries.

Parameters:
param_functionCallable

a Parameter Function

param_dictFunctionDict

FunctionDict for param_function

set_qualities(function: Callable, operator_dict: dict | None = None)

Define the observables and phase-shift unitary operator

Parameters:
functionCallable

an Operator Function

operator_dictFunctionDict, optional

FunctionDict for function

set_sampling(sample_block_size: int, function: Callable | None = None, max_sample_iterations: int = 100, sampling_dict: dict | None = None)

Compute the objective function using simulated sampling.

Samples are taken in blocks of sample_block_size. These are passed as a list of lists to function (a Sampling Function), which returns a value for expectation value/objective function and a boolean that indicates wether the sampled result should be passed to the classical optimiser.

If function is None, the objective function is computed as the mean of sample_block_size shots.

Parameters:
sample_block_sizeint

number of shots taken between successive computation of the expectation value/objective function

functioncallable, optional

Sampling Function

max_sample_iterationsint, optional

maximum number of sample blocks per computation of the expectation value/objective function, overrides the boolean returned by function, by default 100

sampling_dictFunctionDict, optional

FunctionDict for the Sampling Function

set_seed(seed: int)

Integer for seeding of random number generation.

Parameters:
seedint

seeds the generation of random parameters

set_unitaries(unitaries: list[Unitary])

Define the ansatz unitary.

Unitaries are passed as a python list in order of application from left to right.

Parameters:
unitaries: list[unitary]

list of unitaries specifying the action of one ansatz iteration

unset_sampling()

Revert to simulation using exact computation of the objective function.

class quop_mpi.algorithm.multivariable.qowe(Ns: list[int], deltas: list[float], mins: list[float], MPI_COMM: mpi4py.MPI.Intracomm = mpi4py.MPI.COMM_WORLD)
benchmark(ansatz_depths: Iterable[int], repeats: int, param_persist: bool = False, verbose: bool = True, filename: str | None = None, label: str = 'test', save_action: str = 'a', time_limit: int | None = None, suspend_path: str | None = None)

A method by which to study how a QVA performs as the number of ansatz iterations increases.

Parameters:
ansatz_depthsiterable[int]

integers specifying a sequence of ansatz depths

repeatsint

number of repeats at each ansatz depth

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results by default True

filenamestr or None, optional

name of *.h5 file in which to save() the optimised system state and observables

labelstr, optional

if filename is not None, *.h5 data will be saved as “filename/label_depth_repeat”, by default "test"

save_action{‘a’, ‘w’}, optional

action taken during first file write: ‘a’ to append, ‘w’ to overwrite, by default ‘a’

time_limitint or None, optional

total allocated in-program time in seconds, if the time of the previous simulation exceeds the time remaining, the benchmark is suspended

suspend_pathstr or None, optional

path to the suspend file if time_limit is not None

evaluate(variational_parameters: list[float] | np.ndarray[float]) float

Lazily computes the objective function value.

The Ansatz instance stores the last variational parameters passed to evaluate and the corresponding objective function value. If the input variational parameters match, re-computation of the final state is skipped and the previously computed objective function value is returned.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

Returns:
float

objective function value

evolve_state(variational_parameters: list[float] | np.ndarray[float])

Compute the system state under the action of the ansatz unitary.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters.

See also

set_unitaries()
execute(variational_parameters: list[float] | np.ndarray[float] = None)

Simulate a QVA.

If variational_parameters is None, initial parameter values are generated using the Parameter Function of the corresponding unitary instances.

Parameters:
variational_parameterslist[float] or ndarray[float]

1-D (ansatz_depth * total_params,) real array of variational parameters

gen_initial_params(ansatz_depth: int = None) np.ndarray[np.float64]

Generate initial variational parameters.

Values are generated using the Parameter Function associated with each unitary passed to the set_unitaries() method.

Note

If ansatz_depth is None the ansatz depth defaults to 1 or the depth specified by the set_depth() method.

Parameters:
ansatz_depthint, optional

number of ansatz iterations

Returns:
ndarray[float64]

1-D (ansatz_depth * total_params,) real array of variational parameters

get_expectation_value() float

Compute the objective function at the current value of variational_parameters().

Returns:
float

objective function value

get_final_state() np.ndarray[np.complex128] | None

Gather the final state to rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[complex128] or None

the final state at rank 0 of the Ansatz subcommunicator, None otherwise

get_probabilities() np.ndarray[np.float64] | None

Gather probabilities computed from the final state at rank 0 of the Ansatz MPI subcommunicator.

Requires a previous call to execute(), evolve_state() or benchmark(). If called after benchmark() the gathered state will correspond to the last performed simulation.

Returns:
ndarray[float64] or None

1-D real array of state probabilities at rank 0 of the Ansatz subcommunicator, None otherwise

grid_point_from_index(index: int) np.ndarray[np.float64]

Retrieve the corresponding coordinate point from a global index of the system state.

Parameters:
indexint

global index of the system state

Returns:
ndarray[float64]

a 1-D real array containing a grid point in Cartesian coordinates

objective(variational_parameters: list[float] | np.ndarray[float]) float

Compute the objective function at variational parameters variational_parameters.

Parameters:
variational_parameterslist or ndarray[float]
Returns:
float

objective function value

print_optimiser_result()

Print the result returned from the optimiser for the last QVA simulation.

print_result()

Print a summary of the results of the last QVA simulation.

save(file_name: str, config_name: str, action: str = 'a')

Write the final state, observables and results summary to a HDf5 file.

Parameters:
file_namestr

file path to saved data

config_namestr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ to overwrite, by default ‘a’

Notes

Data is saved into a *.h5 file with the following structure.

├── config_name
    ├── final_state 
    ├── observables

The minimization result is saved in the ‘minimize_result’ attribute of ‘config_name’ as a formatted string.

Multiple configurations with a unique config_name can be stored in the same .h5 file. HDF5 files are supported in python by the h5py package. With it, a saved configuration can be accessed as follows:

import h5py

config_name = "my_simulation"

f = h5py.File(file_name + ".h5", "r") final_state =
np.array(f[config_name]['final_state']).view(np.complex128)
eigenvalues =
np.array(f[config_name]['eigenvalues']).view(np.complex128)
observables =
np.array(f[config_name]['observables']).view(np.float64)

print(f["my_simulation"].attrs["minimize_result"])

Warning

The "final_state" and "observables" datasets are saved using Fortran subroutines which make use of parallel HDF5.

The complex values of the final_state array are saved as a compound datatype consisting of contiguous double precision reals. This is equivalent to the np.complex128 NumPy datatype. To access this data without a loss of precision in python, the user must set the view of the NumPy array to np.complex128, rather than casting it to np.complex128 using the dtype keyword.

Similarly, the observables array, which is saved as an array of double-precision reals, should have its view set to np.float64.

set_depth(depth: int)

Set the simulated ansatz depth.

Parameters:
depthint

number of ansatz iterations

set_free_params(function: Callable, free_params_dict: dict | None = None)

Optimise over a subset of the free variational parameters.

Parameters:
functioncallable

Free Parameters Function

free_params_dictdict, optional

FunctionDict for the Free Parameters Function

Examples

A Free Params Function that restricts optimisation to the last ansatz iteration:

def last_ansatz_iteration(total_params, ansatz_depth):
   return list(
       range((ansatz_depth - 1) * total_params, ansatz_depth * total_params)
   )
set_independent_t(independent: bool)

Specify simulation with or without independent unitary parameters (walk times) over each coordinate dimension.

Parameters:
independentbool

simulate a unique walk time in each coordinate dimension if True, all walk times share the same value if False

set_initial_state(function: Callable, initial_state_dict: dict | None = None)

Define the initial state.

Parameters:
functioncallable

Initial State Function

initial_state_dictFunctionDict, optional

FunctionDict for the Initial State Function

set_log(filename: str, label: str, action: str = 'a')

Creates a CSV in which to save simulation results after a call to execute().

Parameters:
filenamestr

path to the log file

labelstr

simulation identifier

action{‘a’, ‘w’}, optional

‘a’ to append or ‘w’ overwrite, by default ‘a’

set_observables(function: Callable | int, observable_dict: dict | None = None)

Specify the observables.

Parameters:
functioncallable or int

an Observables Function or an integer specifying the index of a phase-shift unitary in the list passed to the set_observables() whose exponent contains the observable vector.

observables_dict: FunctionDict, optional

FunctionDict for the Observables Function

set_optimiser(optimiser: str, optimiser_args: dict | None = None, optimiser_log: list[str] | None = None)

Define the classical optimiser for QVA simulation.

Optionally allows for specification of arguments passed to the optimiser and fields in the optimiser dictionary to write to the log file (see set_log()). QuOp_MPI supports optimisers provided by SciPy through its minimize method minimize and optimisers provided by the NLopt package with respect to minimisation with scalar constraints through a SciPy-like interface.

Parameters:
optimiser: {‘scipy’, ‘nlopt’}

‘scipy’ to use the SciPy, ‘nlopt’ to use NLopt, or a callable QuOp_MPI-compatible optimisation function.

optimiser_args: dict

arguments to pass to the optimiser

optimiser_log: list[str]

results of the optimisation process are stored in a dictionary. These values may be logged by passing a list of the corresponding keys

Examples

The default optimiser is the BFGS algorithm, which is set internally as follows:

Ansatz.set_optimiser( 'scipy',
        {'method':'BFGS','options':{'gtol':1e-3}},
                    ['fun','nfev','success'])
set_parallel_jacobian(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, method: str | Callable = 'forward', h: float | None = None)

Specify optimisation of the variational parameters using parallel computation of the jacobian.

This creates MPI subcommunicators containing duplicates of the Ansatz instance which return partial derivative information to the root MPI process during optimisation.

Parameters:
nodes_per_subcommint

MPI nodes per subcommunicator

processes_per_nodeint

MPI processors associated with each node

maxcommint

maximum number of created MPI subcommunicators (and Ansatz instance duplicates) if nodes_per_subcomm > 1, or the maximum number of MPI subcommunicators per node if nodes_per_subcomm = 1

method :{‘forward’, ‘central’} or callable, optional

‘forward’ or ‘central’ to used the forward difference or central difference method for numerical approximation of the partial derivatives, or a QuOp Jacobian Function, by default ‘forward’

hfloat, optional

step-size used by the forward or central difference methods, by default np.sqrt(np.finfo(float).eps)

set_params(param_function: Callable, param_dict: dict | None = None)

Define the Parameter Function for the phase-shift and mixing unitaries.

Parameters:
param_functionCallable

a Parameter Function

param_dictFunctionDict

FunctionDict for param_function

set_qualities(function: Callable, operator_dict: dict | None = None)

Define the observables and phase-shift unitary operator

Parameters:
functionCallable

an Operator Function

operator_dictFunctionDict, optional

FunctionDict for function

set_sampling(sample_block_size: int, function: Callable | None = None, max_sample_iterations: int = 100, sampling_dict: dict | None = None)

Compute the objective function using simulated sampling.

Samples are taken in blocks of sample_block_size. These are passed as a list of lists to function (a Sampling Function), which returns a value for expectation value/objective function and a boolean that indicates wether the sampled result should be passed to the classical optimiser.

If function is None, the objective function is computed as the mean of sample_block_size shots.

Parameters:
sample_block_sizeint

number of shots taken between successive computation of the expectation value/objective function

functioncallable, optional

Sampling Function

max_sample_iterationsint, optional

maximum number of sample blocks per computation of the expectation value/objective function, overrides the boolean returned by function, by default 100

sampling_dictFunctionDict, optional

FunctionDict for the Sampling Function

set_seed(seed: int)

Integer for seeding of random number generation.

Parameters:
seedint

seeds the generation of random parameters

set_unitaries(unitaries: list[Unitary])

Define the ansatz unitary.

Unitaries are passed as a python list in order of application from left to right.

Parameters:
unitaries: list[unitary]

list of unitaries specifying the action of one ansatz iteration

unset_sampling()

Revert to simulation using exact computation of the objective function.

observable

Predefined Observable Functions.

See also: quop_mpi.Ansatz.set_observables().

quop_mpi.observable.array(partition_table: list[int], MPI_COMM: Intracomm, array: list[float] | np.ndarray[float]) np.ndarray[np.float64]

Define observables with a NumPy ndarray.

An Observables Function. The array argument must be passed to quop_mpi.Ansatz.set_observables() in a FunctionDict .

Parameters:
partition_tablelist[int]

1-D array describing the global partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

arrayUnion[list[float], ndarray[float]]

a 1-D real array containing system size observable values

Returns:
ndarray[float64]

local_i observable values with global index offset local_i_offset (see quop_mpi.Ansatz())

quop_mpi.observable.csv(partition_table: list[int], MPI_COMM: Intracomm, *args, **kwargs) np.ndarray[np.float64]

Load observables from a *.csv using pandas.

An Observables Function. The filename argument must be passed to quop_mpi.Ansatz.set_observables() in a FunctionDict. Additional keyword arguments in the FunctionDict are passed to the pandas.read_csv method.

Parameters:
partition_tablelist[int]

1-D array describing the global partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

filenamestr

path to a *csv file

Returns:
ndarray[float64]

local_i observable values with global index offset local_i_offset (see quop_mpi.Ansatz())

quop_mpi.observable.hdf5(partition_table: list[int], MPI_COMM: Intracomm, filename: str, dataset_name: str, **kwargs) np.ndarray[np.float64]

Load observables from a *.h5 file using HDF5 for Python.

An Observables Function. The filename and dataset_name arguments must be passed to quop_mpi.Ansatz.set_observables() in a FunctionDict. Additional positional and keyword arguments in the FunctionDict are passed to the h5py.File method.

Parameters:
partition_tablelist[int]

1-D array describing the global partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

filenamestr

path to a *.h5 file

dataset_namestr

path to the dataset in filename containing an ndarray[float64] of system size observables.

Returns:
ndarray[float64]

local_i observable values with global index offset local_i_offset (see quop_mpi.Ansatz())

quop_mpi.observable.serial(partition_table: list[int], MPI_COMM: Intracomm, function: Callable, *args, **kwargs) np.ndarray[np.float64]

Generate observables using a serial python function.

An Observables Function. The function argument must be passed to

quop_mpi.Ansatz.set_observables() in a FunctionDict. Additional

positional and keyword arguments in the FunctionDict are passed to

function.

Parameters:
partition_tablelist[int]
1-D array describing the global partitioning scheme,

quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

functionCallable
Python function returning a 1-D real array of system size

observable values

Returns:
ndarray[float64]

local_i observable values with global index offset local_i_offset (see quop_mpi.Ansatz())

quop_mpi.observable.rand.uniform(system_size: int, partition_table: list[int], seed: int, MPI_COMM: Intracomm, low: float = 0, high: float = 1) np.ndarray[np.float64]

Generate random observables from a uniform distribution.

An Observables Function. The low and high arguments can be passed to quop_mpi.Ansatz.set_observables() in a FunctionDict.

Parameters:
system_sizeint

the size of the simulated system, class:quop_mpi.Ansatz attribute

partition_tablelist[int]

1-D array describing the global partitioning scheme, quop_mpi.Ansatz attribute

seedint

sets the seed of the random number generator, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI intracommunicator, quop_mpi.Ansatz attribute

lowfloat, optional

lower bound of the generated observable values (inclusive), by default 0

highfloat, optional

upper bound of the genereated observable values (exclusive), by default 1

Returns:
np.ndarray[float64]

local_i observable values with global index offset local_i_offset (see quop_mpi.Ansatz())

param

Predefined Parameter Functions.

See quop_mpi.Unitary().

quop_mpi.param.rand.uniform(n_params: int, seed: int, low: float = 0, high: float = 6.283185307179586) np.ndarray[np.float64]

Generate initial variational parameters from a uniform distribution.

The default Parameter Function of the quop_mpi.Unitary class. User specified low and high values can be specified by passing a corresponding:term:FunctionDict to on initialisation of a unitary instance (see quop_mpi.Unitary()).

Parameters:
n_paramsint

total number of unitary and operator variational parameters, quop_mpi.Unitary attribute

seedint

seeds random number generation, quop_mpi.Unitary attribute

lowfloat, optional

lower bound of the generated variational parameters (inclusive), by default 0

highfloat, optional

upper bound of the generated variational parameters (exclusive), by default 2*pi

Returns:
ndarray[float64]

a 1-D array of n_params variational parameters

state

Predefined Initial State Functions .

See set_initial_state().

quop_mpi.state.array(local_i: int, local_i_offset: int, MPI_COMM: Intracomm, state: np.ndarray[np.complex128], normalize: bool = True) np.ndarray[np.complex128]

Define the initial state using a Numpy array.

An Initial State Function. The normalize argument can be specified by passing a FunctionDict to set_initial_state() .

Parameters:
local_iint

size of the local system state partitions, quop_mpi.Ansatz attribute

local_i_offsetint

global index offset of the local system state partitions, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator of the QVA simulation, quop_mpi.Ansatz attribute

statendarray[complex128]

A 1-D array of system size initial state values

normalizebool, optional

wether to normalize state, by default True

Returns:
ndarray[complex128]

1-D complex array of local_i initial state values with global index offset local_i_offset (see quop_mpi.Ansatz)

quop_mpi.state.basis(local_i: int, local_i_offset: int, basis_states: list[int] = None) np.ndarray[np.complex128]

Generate an equal superposition over a subset of basis states.

An Initial State Function. The basis_states argument can be specified by passing a FunctionDict to quop_mpi.Ansatz.set_initial_state().

Parameters:
local_iint

size of the local system state partitions, quop_mpi.Ansatz attribute

local_i_offsetint

global index offset of the local system state partitions, quop_mpi.Ansatz attribute

basis_stateslist[int], optional

global indexes specifying an equal superposition over a subset of states, by default [0]

Returns:
ndarray[complex128]

1-D complex array of local_i initial state values with global index offset local_i_offset (see quop_mpi.Ansatz )

quop_mpi.state.equal(system_size: int, local_i: int) np.ndarray[np.complex128]

Generate an equal superposition over all system states.

The default Initial State Function of the quop_mpi.Ansatz class.

Parameters:
system_sizeint

size of the simulated QVA, quop_mpi.Ansatz attribute

local_iint

size of the local system state partitions, quop_mpi.Ansatz attribute

Returns:
ndarray[complex128]

1-D complex array of local_i initial state values with global index offset local_i_offset (see quop_mpi.Ansatz)

quop_mpi.state.position_grid(alloc_local: int, local_i: int, local_i_offset: int, MPI_COMM: Intracomm, Ns: list[int], deltas: list[float], mins: list[float], function: Callable) np.ndarray[np.complex128]

Generate an initial state discrete Cartesian coordinates.

An Observables Function. Arguments Ns, deltas, mins and function must be passed to quop_mpi.Ansatz.set_observables() in a FunctionDict.

The function argument must take an len(Ns) -dimensional coordinate and return the complex amplitude of the initial state at that coordinate.

If function is None, position_grid generates a squeezed Gaussian state with its mean situated at a randomly generated coordinate that has a distance of at least length * 0.125 from the boundaries of the grid (where length is the length in each coordinate).

Parameters:
alloc_localint

size of the array containing the local partition of the system, quop_mpi.Ansatz attribute

local_iint

number of initial state values in local partition of the system state, quop_mpi.Ansatz attribute

local_i_offsetint

global index offset of the local system state partition, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

Nslist[int]

number of grid points in each dimension of the Cartesian grid

deltaslist[float]

step size in each coordinate

minslist[float]

minimum value in each coordinate

functionCallable

a Python function returning the value of the initial state at each grid point

Returns:
ndarray[complex128]

1-D complex array of local_i initial state values with global index offset local_i_offset (see quop_mpi.Ansatz)

quop_mpi.state.serial(partition_table: list[int], MPI_COMM: Intracomm, function: Callable, *args, **kwargs) np.ndarray[np.complex128]

Generate the initial state using a serial Python function.

An Initial State Function. The function argument must be specified by passing a FunctionDict to set_initial_state(). Additional positional and keyword arguments in the FunctionDict are passed to function.

Parameters:
partition_tablelist[int]

1-D array describing the global partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator of the QVA simulation, quop_mpi.Ansatz attribute

functionCallable

a Python function returning a 1-D complex array of system size initial state values

Returns:
ndarray[complex128]

1-D complex array of local_i initial state values with global index offset local_i_offset (see quop_mpi.Ansatz)

meta

class quop_mpi.meta.swarm(nodes_per_subcomm: int, processes_per_node: int, maxcomm: int, MPI_COMM: COMM_WORLD, alg: type, *args, **kwargs)

Create and operate on a swarm of identical Ansatz instances.

Each Ansatz instance is associated with an MPI subcommunicator such that they can carry out QVA simulation independently.

The Ansatz instance is initialised by the swarm instance as,

Ansatz(*args, MPI_COMM, **kwargs)
Parameters:
nodes_per_subcommint

number of compute nodes associated with each Ansatz subcommunicator, if nodes_per_subcomm == 1 create maxcomm subcommunicators per available compute node.

processes_per_nodeint

number of MPI processes per compute node, must be the same for all nodes

maxcommint

target number of Ansatz subcommunicators

MPI_COMMIntracomm

MPI communicator from which to create the Ansatz subcommunicators

algAnsatz

an Ansatz, quop_mpi.Ansatz or a predefined algorithm (see quop_mpi.algorithm)

benchmark(*args, **kwargs)

Test QVA performance as a function of Ansatz Depth.

Parameters:
args: list[Ans] or list[list[Any]]

positional arguments for quop_mpi.ansatz.benchmark(), or a list of positional arguments specifying unique input for quop_mpi.ansatz.benchmark() for each Ansatz instance.

kwargs: dict

keyword arguments for quop_mpi.ansatz.benchmark(), or keywords pointing to a list of positional arguments specifying unique input for quop_mpi.ansatz.benchmark() for each Ansatz instance.

See also

benchmark()
benchmark_swarm(ansatz_depths: Iterable[int], repeats: int, basename: str, param_persist: bool = True, verbose: bool = True, save_action: str = 'a', time_limit: float | None = None, logging: bool = True, suspend_path: str | None = None)

Test QVA performance with increasing ansatz depth with repeats at each depth computed in parallel over the swarm.

Parameters:
ansatz_depthsIterable[int]

simulated ansatz depths

repeatsint

number of repeats at each ansatz depth

basenamestr

path to directory in which to write simulation results and logs, by default None

param_persistbool, optional

if True the optimised variational parameter values which achieved the lowest objective function value for all repeats at ansatz_depth will be used as starting parameters for the first ansatz_depth * total_params at ansatz_depth += 1, by default True

verbosebool, optional

if True, print current the ansatz depth, repeat number and optimisation results, by default True

save_action{‘a’, ‘w’}

‘a’ to append, ‘w’ to (over)write, by default “a”

time_limitfloat, optional

time limit in seconds, program will suspend if the time remaining is less than the time taken by the last simulation, by default None

loggingbool, optional

write simulation results to log files, by default True

suspend_pathstr, optional

path to directory in which to write suspend data, by default None

Returns:
list[dict]

optimisation results ordered by ansatz depth

execute_swarm(param_lists: list[np.ndarray[np.float64]], basename: str, log_path: str = None, h5_path: str = None, labels: str | list[str] = None, save_action: str = 'a', time_limit: float = None, verbose: bool = True, suspend_path: str = None)

Parallel simulation of QVAs over a set of initial variational parameters.

Parameters:
param_listslist[np.ndarray[np.float64]]

list of 1-D real arrays containing initial variational parameters

basenamestr

folder in which to store simulation results and suspend data unless otherwise specified, by default None

log_pathstr, optional

folder in which to write simulation log files, by default None

h5_pathstr, optional

folder in which to write simulation results, by default None

labelsstr or list[str], optional

labels(s) for each simulation, by default None

save_action{“a”, “w”}

“a” to append “w” to (over)write, by default "a"

time_limitfloat, optional

suspend if the time remaining is less than the time taken by the last QVA simulation, by default None

verbosebool, optional

print the simulation results and simulation progress, by default True

suspend_pathstr, optional

folder in which to store suspend data, by default None

Returns:
dict

a dictionary of optimisation results with keys str(params_list[i])

get_optimal_result() dict

Retrieve the result with the lowest objective function value out of the last set of simulations executed by the swarm.

Returns:
dict

simulation result

save(*args, **kwargs)

Save simulation results.

Parameters:
args:

positional arguments for quop_mpi.ansatz.save(), or a list of positional arguments specifying unique input for quop_mpi.ansatz.save() for each Ansatz instance.

kwargs:

keyword arguments for quop_mpi.ansatz.save(), or keywords pointing to a list of positional arguments specifying unique input for quop_mpi.ansatz.save() for each Ansatz instance.

See also

save()
set_log(*args, **kwargs)

Log simulation information.

Parameters:
args: list[Any] or list[list[Any]]

positional arguments for quop_mpi.ansatz.set_log(), or a list of positional arguments specifying unique input for quop_mpi.ansatz.set_log() for each Ansatz instance.

kwargs: dict

keyword arguments for quop_mpi.ansatz.set_log(), or keywords pointing to a list of positional arguments specifying unique input for quop_mpi.ansatz.set_log() for each Ansatz instance.

See also

set_log()
set_unitaries(unitaries: list[Unitary] | list[list[Unitary]])

Set the unitaries of an Ansatz swarm.

Parameters:
unitaries :list[Unitary] or list[list[Unitary]]

a list of unitary instances (broadcast to all swarm instances) or a list of unitary instances for each Ansatz instance in the swarm

Raises:
RuntimeError

if unitaries`is a list of lists that is not equal to the :literal:`swarm size

propagator

Predefined unitary classes for simulation of the action of QVA phase-shift and mixing unitaries with compatible Operator Functions.

circulant

class quop_mpi.propagator.circulant.unitary(*args, **kwargs)

operators

Predefined Operator Functions for quop_mpi.propagator.circulant.unitary.

An Operator Function for 'circulant' unitary instances return a local_i sized partition of the operator eigenvalues with global index offset local_i_offset.

quop_mpi.propagator.circulant.operator.complete(system_size: int) np.ndarray[np.float64]

Generate a parallel partition of the eigenvalues of a complete circulant graph with edge weightings 1.

An Operator Function associated with quop_mpi.propagator.circulant.unitary.

Parameters:
system_sizeint

the size of the simulated QVA

local_iint

size of the local system state partitions, quop_mpi.Ansatz attribute

local_i_offsetint

global index offset of the local system state partitions, quop_mpi.Ansatz attribute

Returns:
ndarray[complex128]

1-D complex array of local_i eigenvalues with global index offset local_i_offset

quop_mpi.propagator.circulant.operator.graph(system_size: int, i: int = 1) np.ndarray[np.float64]

Generate the eigenvalues of the i-th symmetric circulant graph with edge weightings 1.

An Operator Function associated with quop_mpi.propagator.circulant.unitary.

Parameters:
system_sizeint

the size of the simulated QVA

local_iint

size of the local system state partitions, quop_mpi.Ansatz attribute

local_i_offsetint

global index offset of the local system state partitions, quop_mpi.Ansatz attribute

iint, optional

index of the graph (ordered by vertex degree), 1 corresponds to a cycle graph and system_size // 2 + 1 to a complete graph, by default 1

Returns:
ndarray[complex128]

1-D complex array of local_i eigenvalues with global index offset local_i_offset

diagonal

class quop_mpi.propagator.diagonal.unitary(*args, **kwargs)

Compute the action of a mixing unitary with a phase_shift operator or a sequence of mixing-unitaries with phase_shift operators (see the unitary_n_params attribute below).

Inheritance Diagram:

digraph "sphinx-ext-graphviz" {
    rankdir="LR";
    node [fontsize="10"];
    Unitary[label="quop_mpi.Unitary", shape="rectangle"];
    unitary[label="quop_mpi.propagator.phase_shift.unitary", shape="rectangle"];

    Unitary -> unitary;
}

See quop_mpi.Unitary.

Attributes:
unitary_type

'phase_shift'

planner

false

unitary_n_params

Set on initialisation to 1 or more. If unitary_n_parameters > 1, the Operator Function must return a list[csr_matrix] of length unitary_n_parameters containing csr_matrix partitions of of local_i rows.

propagate(gammas)

Simulation of the action of a :term`unitary`.

When implemented, propagate contains a call to a method (typically a contained in a complied Python extension module) that takes the class attributes initial_state, final_state and MPI_COMM, together with attributes describing the parallel partitioning scheme and variational parameters x, as input. The action of the unitary is computed in MPI parallel, with the computed result written to final_state.

Warning

Not implemented by the base Unitary class.

Parameters:
xndarray[float64]

a 1-D real array of n_params variational parameters

Examples

def propagate(self, x):

    external_propagator(
        x, self.partition_table, self.initial_state,
        self.final_state, self.MPI_COMM )

operators

Predefined Operator Functions and related utility for quop_mpi.propagator.diagonal.unitary.

An Operator Function for 'diagonal' unitary instances returns an ndarray[float64] of size local_i, or a list[ndarray[float64]] with local_i sized elements, which correspond to partition(s) of diagonal operator(s) with global index offset local_i_offset.

If the Operator function returns list[ndarray[float64]], the unitary instance must be initialised with unitary_n_parameters equal to the length of returned list. The resulting unitary is then equivalent to a sequence of phase-shift unitaries with independently parameterised unitary parameters.

quop_mpi.propagator.diagonal.operator.array(partition_table: list[int], MPI_COMM: Intracomm, array: np.ndarray[np.float64]) np.ndarray[np.float64]

Define the diagonal of the phase-shift unitary using a Numpy array.

An Operator Function for the quop_mpi.propagate.diagonal.unitary class.

Note

For memory efficiency, array can be present as an ndarray[float64] at MPI_COMM.rank == 0 only and None at all other ranks in MPI_COMM.

Parameters:
partition_tablelist[int]

describes the parallel partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

arraynp.ndarray[np.float64]

a 1-D real array of size system size

Returns:
ndarray[float64]

a 1-D real array containing local_i elements of the operator diagonal with global index offset local_i_offset

quop_mpi.propagator.diagonal.operator.cartesian(system_size: int, local_i: int, local_i_offset: int, Ns: list[int], deltas: list[float], mins: list[float], function: Callable, *args, **kwargs) np.ndarray[np.float64]

TODO:UPDATE Generate the diagonal of a phase-shift unitary operator using a Python function defined in discrete Cartesian coordinates.

An Observables Function. Depending on wether QVA simulation is defined using the Ansatz class directly or with a predefined Ansatz subclass from the algorithm submodule, the following arguments must be defined in a corresponding FunctionDict on initialisation of the unitary instance:

Additional positional and keyword arguments in the FunctionDict are passed to function.

The function argument must conform to the signature,

def function(x: ndarray[float64], *args, *kwargs) -> float

where x is a 1-D array containing a len(Ns) -dimensional grid point.

Parameters:
system_sizeint

size of the simulated QVA

local_iint

size of the local system state partition, quop_mpi.Unitary attribute

local_i_offsetint

global index offset of the local system state partition, quop_mpi.Unitary attribute

Nslist[int]

the number of qubits assigned to each dimension of the cartesian grid such that there is 2 ** Ns[d] grid points per dimension d

deltaslist[float]

step size in each Cartesian coordinate

minslist[float]

lower bound of each Cartesian coordinate

functionCallable

a Python function that takes a list of len(Ns) real coordinate values and returns a float

Returns:
ndarray[float64]

a 1-D real array containing local_i elements of the operator diagonal with global index offset local_i_offset

See also

setup_cartesian

compute deltas and mins

cartesian_scaled

alternative to cartesian, scales function between 0 and an upper bound.

quop_mpi.propagator.diagonal.operator.cartesian_scaled(system_size: int, local_i: int, local_i_offset: int, MPI_COMM: Intracomm, Ns: list[int], deltas: list[float], mins: list[float], function: Callable, coeff: float, *args, **kwargs) np.ndarray[np.float64]

Generate the diagonal of a phase-shift unitary operator using a Python function defined in discrete Cartesian coordinates with the function scaled between 0 and coeff.

An Observables Function. Depending on wether QVA simulation is defined using the Ansatz class directly or with a predefined Ansatz subclass from the algorithm submodule, the following arguments must be defined in a corresponding FunctionDict on initialisation of the unitary instance:

Additional positional and keyword arguments in the FunctionDict are passed to function.

The function argument must conform to the signature,

def function(x: ndarray[float64], *args, *kwargs) -> float

where x is a 1-D array containing a len(Ns) -dimensional grid point.

Parameters:
system_sizeint

size of the simulated QVA

local_iint

size of the local system state partition, quop_mpi.Unitary attribute

local_i_offsetint

global index offset of the local system state partition, quop_mpi.Unitary attribute

Nslist[int]

the number of qubits assigned to each dimension of the cartesian grid such that there is 2 ** Ns[d] grid points per dimension d

deltaslist[float]

step size in each Cartesian coordinate

minslist[float]

lower bound of each Cartesian coordinate

functionCallable

a Python function that takes a list of len(Ns) real coordinate values and returns a float

coefffloat

a positive real number, the upper bound of the scaling range

Returns:
ndarray[float64]

a 1-D real array containing local_i elements of the operator diagonal with global index offset local_i_offset

See also

setup_cartesian

compute deltas and mins

cartesian_scaled

alternative to cartesian_scaled, does not scale function

quop_mpi.propagator.diagonal.operator.csv(partition_table: list[int], MPI_COMM: Intracomm, filename: Callable, *args, **kwargs) np.ndarray[np.float64]

Load the diagonal of a phase-shift unitary using pandas.

An Operator Function for the quop_mpi.propagate.diagonal.unitary class. The filename argument must be defined in a corresponding FunctionDict on initialisation of the unitary instance. Additional keyword arguments in the FunctionDict are passed to the pandas.read_csv method.

Parameters:
partition_tablelist[int]

describes the parallel partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

filenameCallable

path to a *.csv file

Returns:
ndarray[float64]

a 1-D real array containing local_i elements of the operator diagonal with global index offset local_i_offset

quop_mpi.propagator.diagonal.operator.hdf5(partition_table: int, MPI_COMM: Intracomm, filename: str, dataset_name: str) np.ndarray[np.float64]

Load the diagonal of a phase-shift unitary using HDF5 for Python.

An Operator Function for the quop_mpi.propagate.diagonal.unitary class. The filename and dataset_name arguments must be defined in a corresponding FunctionDict on initialisation of the unitary instance. Additional positional and keyword arguments in the FunctionDict are passed to the h5py.File method.

Parameters:
partition_tableint

describes the parallel partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

filenamestr

path to a *.h5 file

dataset_namestr

path to the dataset containing a ndarray[float64] of size system size

Returns:
np.ndarray[np.float64]

a 1-D real array containing local_i elements of the operator diagonal with global index offset local_i_offset

quop_mpi.propagator.diagonal.operator.serial(partition_table: list[int], MPI_COMM: Intracomm, variational_parameters: np.ndarray[np.float64], function: Callable, *args, **kwargs) np.ndarray[np.float64] | list[np.ndarray[np.float64]]

Generate the diagonal of the operator for one or more sequential phase-shift unitaries using a serial Python function.

An Operator Function for the quop_mpi.propagator.diagonal.unitary class. The function argument must be defined in a corresponding FunctionDict on initialisation of the unitary instance. Additional positional and keyword arguments contained in the FunctionDict are passed to function.

The function argument must conform to the signature,

def function(*args, *kwargs) -> (ndarray[float64] | list[ndarray[float64]])

where the output is a 1-D real array of type ndarray[float64] and length system size, or list containing one or more 1-D real arrays of type ndarray[float64] and length system_size.

Parameters:
partition_tablelist[int]

describes the parallel partitioning scheme, quop_mpi.Ansatz attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Ansatz attribute

variational_parametersndarray[float64]

operator parameters, passed to function if unitary.operator_n_params > 0, quop_mpi.Unitary attribute

functionCallable

returns one or more ndarray[float64] of size system size corresponding to the diagonal of the operator of one or more phase-shift unitaries

Returns:
ndarray[float64] or list[ndarray[float64]]

a 1-d real array or list of 1-D real arrays containing a local_i elements of the operator diagonal with global index offset local_i_offset

quop_mpi.propagator.diagonal.operator.setup_cartesian(Ns: list[int], bounds: list[list[float]]) list[list[float]]

Compute the step-size and minimum coordinate values in each dimension of a Cartesian grid.

Parameters:
Nslist[int]

the number of qubits assigned to each dimension of the Cartesian grid such that there is 2 ** Ns[d] grid points per dimension d

boundslist[list[float]]

the lower and upper bounds of each dimension where len(Ns) == len(bounds)

Returns:
list[list[float]]

the step-size, deltas, and the minimum, mins, in each Cartesian coordinate

sparse

class quop_mpi.propagator.sparse.unitary(*args, **kwargs)

Compute the action of a mixing unitary with a sparse operator or a sequence of mixing-unitaries with sparse operators (see the unitary_n_params attribute below).

Inheritance Diagram:

digraph "sphinx-ext-graphviz" {
    rankdir="LR";
    node [fontsize="10"];
    Unitary[label="quop_mpi.Unitary", shape="rectangle"];
    unitary[label="quop_mpi.propagator.sparse.unitary", shape="rectangle"];

    Unitary -> unitary;
}

See quop_mpi.Unitary.

Attributes:
unitary_type

'sparse'

planner

false

unitary_n_params

Set on initialisation to 1 or more. If unitary_n_parameters > 1, the Operator Function must return a list[csr_matrix] of length unitary_n_parameters containing csr_matrix partitions of of local_i rows.

propagate(ts)

Simulation of the action of a :term`unitary`.

When implemented, propagate contains a call to a method (typically a contained in a complied Python extension module) that takes the class attributes initial_state, final_state and MPI_COMM, together with attributes describing the parallel partitioning scheme and variational parameters x, as input. The action of the unitary is computed in MPI parallel, with the computed result written to final_state.

Warning

Not implemented by the base Unitary class.

Parameters:
xndarray[float64]

a 1-D real array of n_params variational parameters

Examples

def propagate(self, x):

    external_propagator(
        x, self.partition_table, self.initial_state,
        self.final_state, self.MPI_COMM )

operators

Predefined Operator Functions for quop_mpi.propagator.sparse.unitary.

Operator Functions for unitary instances of type 'sparse' return CSR partitions of one or more matrices. More than one CSR partition defines a sequence of mixing unitaries with independent unitary parameters.

Partitioned CSR Matrix Format

lb

lower index of the system state and observables partition, quop_mpi.Unitary attribute

ub

upper index of the system state and observables partition, quop_mpi.Unitary attribute

W_col_index

a 1-D integer array containing non-zero column indexes for rows lb to ub , grouped by ascending row index

W_values

a 1-D real array containing non-zero values for rows lb to ub , grouped by ascending row index in the same order as W_col_index

W_row_start

an 1-D integer array of length ub - lb + 1 , a cumulative sum of the number of non-zero elements in each row such that W_row_start[row_index + 1] - W_row_start[row_index] is equal to the number of non-zero elements in the row with index row_index and W_rows_start[row] - local_i_offset gives the local starting index for the non-zero column indexes and values in W_col_index and W_values for the row with index row_index

These are returned by the Operator Function as list[list[W_row_start], list[W_col_indexes], list[W_values]].

quop_mpi.propagator.sparse.operator.hypercube(system_size: int, lb: int, ub: int) list[list[np.ndarray[np.int64]], list[np.ndarray[np.int64], list[np.ndarray[np.float64]]]]

Generate a hypercube (QAOA) sparse mixing unitary operator.

An Operator Function for quop_mpi.propagator.sparse.unitary.

Parameters:
system_sizeint

size of the simulated QVA, quop_mpi.Unitary attribute

lbint

lower index of the system state partition, quop_mpi.Unitary attribute

ubint

upper index of the system state partition, quop_mpi.Unitary attribute

Returns:
list[list[np.ndarray[np.int64]], list[np.ndarray[np.int64], list[np.ndarray[np.float64]]]]

a CSR partition of the hypercube (QAOA) mixing operator

Raises:
RuntimeError

if system_size % 2 != 0

quop_mpi.propagator.sparse.operator.serial(partition_table: list[int], MPI_COMM: Intracomm, variational_parameters: np.ndarray[np.float64], function: Callable, *args, **kwargs) list[list[np.ndarray[np.int64]], list[np.ndarray[np.int64], list[np.ndarray[np.float64]]]]

Generate an operator for a sparse mixing unitary using a serial Python function.

An Operator Function for quop_mpi.propagator.sparse.unitary. The function argument must be defined via a corresponding FunctionDict on initialisation of the unitary instance. Additional positional and keyword arguments in the FunctionDict are passed the function. The signature for a function generating an operator with one or more operator parameters is,

def function(variational_parameters, *args, **kwargs) ->
list[csr_matrix]

where variational_parameters may be excluded if unitary.operator_n_params = 0.

Parameters:
partition_tablelist[int]

describes the parallel partitioning of the observables and system state, quop_mpi.Unitary attribute

MPI_COMMIntracomm

MPI communicator, quop_mpi.Unitary attribute

variational_parametersnp.ndarray[np.float64]

a 1-D real array of operator parameters, passed to function if len(variational_parameters) > 0, quop_mpi.Unitary attribute

functionCallable

function returning a list of scipy CSR matrices

Returns:
list[list[np.ndarray[np.int64]], list[np.ndarray[np.int64],
list[np.ndarray[np.float64]]]]

a CSR matrix partition

Raises:
TypeError

if function does not return a list of scipy CSR matrices

composite

class quop_mpi.propagator.composite.unitary(Ns, *args, **kwargs)
propagate(t)

Simulation of the action of a :term`unitary`.

When implemented, propagate contains a call to a method (typically a contained in a complied Python extension module) that takes the class attributes initial_state, final_state and MPI_COMM, together with attributes describing the parallel partitioning scheme and variational parameters x, as input. The action of the unitary is computed in MPI parallel, with the computed result written to final_state.

Warning

Not implemented by the base Unitary class.

Parameters:
xndarray[float64]

a 1-D real array of n_params variational parameters

Examples

def propagate(self, x):

    external_propagator(
        x, self.partition_table, self.initial_state,
        self.final_state, self.MPI_COMM )

operators

quop_mpi.propagator.composite.operator.ith(Ns: list[int], Cs: list[int] = None) np.ndarray[np.float64]

Generate the eigenvalues of a QMOA mixing unitary operator.

An Operator Function for quop_mpi.propagator.composite.unitary. The Cs keyword argument may be defined via a corresponding FunctionDict on initialisation of the receiving unitary instance.

Parameters:
Nslist[int]

the number of grid points in each dimension of the Cartesian grid, quop_mpi.propagator.composite.unitary attribute

Cslist[int], optional

specifies the i-th index of the circulant operators associated with each dimension of the Cartesian grid, complete graphs by default

Returns:
ndarray[complex128]

a 2-D complex array containing local_i eigenvalues of the QMOA mixing unitary with global index offset local_i_offset

See also

quop_mpi.propagate.circulant.operator.graph()

Generate the eigenvalues of the i-th symmetric circulant graph with edge weights 1.

momentum

class quop_mpi.propagator.momentum.unitary(Ns: list[int], minsq: list[float], minsk: list[float], deltasq: list[float], deltask: list[float], *args, **kwargs)

Implements the QOWE mixing unitary.

Warning

unitary instances of type 'momentum require that the size of the MPI communicator assocaited with quop_mpi.Ansatz class be a factor of the first grid dimension (Ns[0] % size == 0).

Inhertance Diagram:

digraph "sphinx-ext-graphviz" {
    rankdir="LR"; node [fontsize="10"];
    Unitary[label="quop_mpi.Unitary", shape="rectangle"];
    unitary[label="quop_mpi.propagator.momentum.unitary",
    shape="rectangle"];

    Unitary -> unitary;
}

See quop_mpi.Unitary.

Parameters:
Nslist[int]

the number of grid points in each dimension of the Cartesian grid in position and momentum space

minsqlist[float]

the minimum of each Cartesian coordinate in position space

minsklist[float]

the minimum of each Cartesian coordinate in momentum space

deltasqlist[float]

the step-size in each Cartesian coordinate in position space

deltasklist[float]

the step-size in each Cartesian coordinate in momentum space

*args and **kwargs:

passed to the initialisation method of quop_mpi.Unitary

Attributes:
unitary_type

'momentum'

planner

True

unitary_n_params

len(Ns)

propagate(x)

Simulation of the action of a :term`unitary`.

When implemented, propagate contains a call to a method (typically a contained in a complied Python extension module) that takes the class attributes initial_state, final_state and MPI_COMM, together with attributes describing the parallel partitioning scheme and variational parameters x, as input. The action of the unitary is computed in MPI parallel, with the computed result written to final_state.

Warning

Not implemented by the base Unitary class.

Parameters:
xndarray[float64]

a 1-D real array of n_params variational parameters

Examples

def propagate(self, x):

    external_propagator(
        x, self.partition_table, self.initial_state,
        self.final_state, self.MPI_COMM )

operators

quop_mpi.propagator.momentum.operator.magnitude_squared(Ns: list[int], minsk: list[float], deltask: list[float]) np.ndarray[np.complex128]

Generate the QMOA mixing unitary operator.

An Operator Function for quop_mpi.propagator.momentum.unitary.

Parameters:
Nslist[int]

the number of grid points in each dimension of the Cartesian grid in position and momentum space, quop_mpi.propagator.momentum.unitary attribute

minsklist[float]

the minimum of each Cartesian coordinate in momentum space, quop_mpi.propagator.momentum.unitary attribute

deltasklist[float]

the step-size in each Cartesian coordinate in momentum space, quop_mpi.propagator.momentum.unitary attribute

Returns:
np.ndarray[np.complex128]

a 1-D complex array of local_i elements of the QOWE diagonal momentum-space operator with global index offset local_i_offset

toolkit

Convieniance functions for use in user-defined Initial State and Observables functions.

quop_mpi.toolkit.I(n_qubits: int) csr_matrix

Generate a sparse identity matrix of size 2 ** n_qubits.

Parameters:
n_qubits: int

generate the identity operator for n_qubits

Returns:
csr_matrix

the identity operator for n_qubits

quop_mpi.toolkit.X(index: int, n_qubits: int) csr_matrix

Generate the Pauli X operator acting on qubit index in a system of n_qubits.

Parameters:
indexint

index of the qubit to which the X operator is applied

n_qubitsint

total number of qubits in the system

Returns:
csr_matrix

the Pauli X operator acting on qubit index in a system of n_qubits

quop_mpi.toolkit.Y(index: int, n_qubits: int) csr_matrix

Generate the Pauli Y operator acting on qubit index in a system of n_qubits.

Parameters:
indexint

index of the qubit to which the Y operator is applied

n_qubitsint

total number of qubits in the system

Returns:
csr_matrix

the Pauli Y operator acting on qubit index in a system of n_qubits

quop_mpi.toolkit.Z(index: int, n_qubits: int)

Generate the Pauli Z operator acting on qubit index in a system of n_qubits.

Parameters:
indexint

index of the qubit to which the Z operator is applied

n_qubitsint

total number of qubits in the system

Returns:
csr_matrix

the Pauli Z operator acting on qubit index in a system of n_qubits

quop_mpi.toolkit.kron(terms: list['sparse']) csr_matrix

Compute the tensor (Kronecker) product of a sequence of sparse matrices.

Parameters:
termslist[sparse]

a list of scipy sparse matrices

Returns:
csr_matrix

the tensor product of terms, computed from left to right

quop_mpi.toolkit.kron_power(term: sparse, n: int) csr_matrix

Compute the tensor (Kronecker) product of n instances of a sparse matrix.

Parameters:
termsparse

a scipy sparse matrix

nint

length of the tensor product sequence

Returns:
csr_matrix

tensor product of n occurences of term

quop_mpi.toolkit.string(state: str) np.ndarray[np.complex128]

Generate an initial state from a bit-string representation.

Parameters:
statestr

a bit string state.

Returns:
ndarray[complex128]

the parsed quantum state