Problems
In QruiseML, "problems" are used to define the dynamics of a quantum system. Under the hood, each Problem represents an ordinary differential equation (ODE) over a specified time interval with well-defined initial conditions. From a user perspective, a Problem packages the time-evolution equation, Hamiltonian, initial state, parameters, and time span into a single object, making simulation and optimisation straightforward and consistent.
Once you've instantiated your Problem, you can define the ordinary differential equation algorithm you want to use to solve it. This makes it easy to experiment with different methods on the same quantum system.
Problem¶
Problem is the generic class for specifying a quantum simulation problem. To instantiate the Problem, we need:
- the time-evolution equation (
eq) - the Hamiltonian (
H) - the initial qubit state (
u0), which can be provided as a state vector (1D array) or a density matrix (2D array) - the parameter space (
ps) - the time interval of the simulation (
t0tot1).
To perform the simulation, you need to start a Session and choose an algorithm.
Defining qubit states¶
Qubit states can be defined either using state vectors or density matrices.
State vectors¶
State vectors represent pure states, such as
where \(|0\rangle\), \(|1\rangle\), and \(|+\rangle\) are the ground, first excited, and plus (superposition) states, respectively.
These can be defined using numpy like so:
import numpy as np
# Basis states
ground_state = np.array([1.0, 0.0]) # |0>
excited_state = np.array([0.0, 1.0]) # |1>
# Superposition state
superpos_state = (ground_state + excited_state) / np.sqrt(2) # |+> = (|0> + |1>)/√2
Alternatively, you can define the basis states using QuTiP:
import qutip as qt
# Basis states
ground_state = qt.basis(2, 0) # |0>
excited_state = qt.basis(2, 1) # |1>
Density matrices¶
Density matrices provide a more general description of quantum states. They can represent both pure states and mixed states, and are essential when dealing with open systems or noise, where the state is no longer perfectly pure. For example:
These can be defined using numpy like so:
# Density matrices
rho_ground = np.outer(ground_state, ground_state.conj()) # |0><0|
rho_plus = np.outer(superpos_state, superpos_state.conj()) # |+><+|
rho_mixed = 0.5 * np.outer(ground_state, ground_state.conj()) \
+ 0.5 * np.outer(excited_state, excited_state.conj()) # 50/50 mixture
Or in QuTip like so:
# Density matrices
rho_ground = ground_state.proj() # |0><0|
rho_plus = superpos_state.proj() # |+><+|
rho_mixed = 0.5 * ground_state.proj() + 0.5 * excited_state.proj() # 50/50 mixture
QOCProblem¶
QOCProblem is used for running quantum optimal control problems. Instantiating QOCProblem is almost identical to Problem; however, it requires two extra inputs:
- the desired (target) state (
ut), which (likeu0) should be provided as either a state vector (1D array) or a density matrix (2D array) - a loss function quantifying how far the final state of the simulation is from the desired target state (
loss)
These extra inputs allow QruiseML to adjust your control parameters to get as close as possible to the desired state.
Loss functions¶
The loss function quantifies how far the final system state is from the desired target state. During optimisation, the simulation defined in QOCProblem is repeatedly solved while the drive parameters are updated to minimise the loss function.
In QruiseML, the loss function is specified using one of the following state-overlap measures:
"svo": the state vector overlap"rsvo": the real part of the state vector overlap
For a target state \(|\psi_t\rangle\) and final simulated state \(|\psi(t=t_\text{final})\rangle\), the state vector overlap is given by
The corresponding "svo" loss function is
The "rsvo" loss function instead uses only the real part of the overlap.
EnsembleProblem¶
As the name suggests, EnsembleProblem defines an ensemble of problems, allowing you to run many simulations in parallel. Instead of specifying a single Hamiltonian, initial state, time span, or parameter space, you can provide a list of values for one or more of these quantities. QruiseML then constructs an ensemble of problems containing all possible combinations of the supplied values. This makes it easy to perform parameter sweeps or compare different configurations efficiently. For example, you might sweep a range of flux values to identify the sweet spot of your device.
EnsembleProblem works in much the same way as Problem:
The only difference is that you can pass lists for the initial state, parameters, or time span, e.g.
ps = {
"a": 1.0,
"sigma": 0.1*t1,
"offset": list(np.linspace(-5, 5, 10)),
"scale": list(np.linspace(0.8, 1.2, 11)),
}
EnsembleQOCProblem¶
To perform optimal control on an ensemble, simply use EnsembleQOCProblem as you would QOCProblem:
The difference is, again, that you can provide a list of initial states, Hamiltonians, parameter spaces, and/or time spans.