System Initialisation
The core functionality of this library is to provide an easy way to construct arbitrarily complicated systems of spins and electronic levels. Below you find a hands-on introduction on how to initialise an open quantum system in python with SimOS.
System Construction
Arbitrarily complicated systems of spins and electronic levels can be constructed using only two mathematical operations, (i) a tensor product and (ii) a direct sum, to combine the Hilbert spaces of the individual system components. If a tensor product is used, the overall size of the composite Hilbert space is the product of the dimensions of the individual components. If a direct sum is used, the overall Hilbert space size results as the sum of the individual dimensions.

Composite Hilbert spaces of spins or electronic levels can be combined via tensor products (left) and direct sums (right).
The central part of this library is the class System
which is used to define the system structure and which holds a complete set of operators after construction.
The class constructor is called with a systemarray
that is essentially a recipe on how to build the quantum system. It specifies all members of the system and how
their individual Hilbert spaces are combined.
Each member of the spin system is defined as a python dictionary with keys
name: The name of member:
val: Multiplicity of the level, i.e. val = 0.5 for spin 1/2, val = 1 for spin 1 and so on or val = 0.0 for a single electronic level.
These dictionaries are then combined in the systemarray
as a series of nested lists and tuples. Lists indicate that Hilbert spaces are combined using tensor products
while tuples indicate combination with a direct sum. The system class instance (typically in our examples denoted as s
) holds identity operators
for all members of the quantum system (e.g., s.Aid
for a member with name A
) as well as x, y, z, lowering, raising and projection operators for all spins (e.g.,
s.Ax, s.Ay, s.Az, s.Aplus, s.Aminus, s.Ap[0.5]
and s.Ap[-0.5]
for a spin 1/2 with name A
).
Warning
Names of system members must only contain alphanumerical letters. Special characters are reserved and used internally to distinguish native from non-native system members.
Note
Additional keys can be used to specify further parameters of system members, such as the spin type, isotope, relaxation times, spatial positions etc. Some functions of SimOS
require that these parameters are specified.
Keys can be read, added, deleted or modified after system construction with the get_properties
, set_properties
and del_properties
functions of the system.
As a first example, we generate a system of two spins S=1/2 (e.g. two electron spins), whose composite Hilbert space size is 2x2 = 4.

Constructing a system of two coupled spins.
import simos as sos
S = {'name': 'S', 'val': 0.5}
I = {'name': 'I', 'val': 0.5}
system_array = [S,I]
s = sos.System(system_array)
The spin operators in the joint Hilbert space are stored as class attributes,
e.g. the z-operators of the spins are obtained as s.Sz
and s.Iz
.
Another very simple example is a pair of electronic levels,
e.g. an electronic ground and an electronic excited state, with no spin.
The composite Hilbert space size is 1+1=2.

Constructing a system of two electronic levels.
GS= {'name': 'GS', 'val': 0}
ES = {'name': 'ES', 'val': 0}
system_array = (GS,ES)
s = sos.System(system_array)
The identity operators of the levels in the combined Hilbert space are obtained as
s.GSid
and s.ESid
. More complicated examples, e.g. the NV center in diamond and
spin-correlated radical pairs are showcased in our examples section.
Basis Transformations
Upon system construction, the spin operators are initialized
in the Zeeman basis, spanned by the magnetic quantum numbers of the individual spin
members. To include alternative basis sets and transform operators between various bases,
the System
class provides users with two specific methods.
Coupled Product States of Spins
Coupled product states of pairs or groups of spins are useful representations for systems
in which spin-spin interactions dominate the system Hamiltonian.
In SimOS, they can be constructed with the add_ghostspin
method of the System
.
The simplest example, the coupled representation of two coupled spin 1/2 particles as a singlet ( \(S_{tot} = \frac{1}{2}-\frac{1}{2} = 0\) ) and
triplet ( \(S_{tot} = \frac{1}{2} + \frac{1}{2} = 1\) ) is illustrated
below. Here, spins S
and I
are coupled to obtain the singlet and triplet
ghostspins C_1
and C_3
.

S = {"name": "S", "val": 1/2}
I = {"name": "I", "val": 1/2}
s = sos.System([S,I])
s.add_ghostspin("C", ["S", "I"])
The operators of the ghostspins are generated in full analogy to the native spins
of the system. For example, the projector onto the the \(m_S=0\) level of the triplet
is obtained as C_3p[0]
. The matrix representations of these operators are
still formulated in the Zeeman basis. However, the add_ghostspin
method also
constructs the transformation matrices to transform operators to or from the coupled basis and
stores them as attributes of the System
. In our simple example, the transformation matrices
can be assessed as s.toC
and s.fromC
and allow to transform any system operator to or from the Zeeman
to the coupled Singlet-Triplet basis.
# Transform z Operators of Singlet and Triplet from
# Zeeman to Singlet-Triplet Basis
C1z_st = s.C_1z.transform(s.toC)
C3z_st = s.C_3z.transform(s.toC)
The spin operators of the ghost-spins are named according to the following conventions:
The name that the user has specified, e.g. in our case C.
An underscore.
An integer or a series of integers specifying the spin multiplicity. If more than two spins are coupled, the same multiplicity occurs multiple times for the coupled spin. To distinguish them, the multiplicities of their coupling history are included in their name.
The actual name of the operator, e.g. z for a z operator, id for the identity.
If add_ghostspin
is called with the option return_names = True
the names of the ghost spins (i.e. parts 1-3 of the above list) are returned.
This can be helpful if larger groups of spins are coupled.
Generic Basis Transformations
A completely user-defined basis may be defined using a second method, add_basis
,
by providing (i) a transformation matrix from the Zeeman basis to the new basis and (ii) a name
for the basis and a list of names for all basis states. Here, we illustrate this method for the example
of a pair of electronic levels. The method stores identity operators of all new
basis states as well as transformation matrices for back and forth conversion between the Zeeman and the alternate basis.

GS= {'name': 'GS', 'val': 0}
ES = {'name': 'ES', 'val': 0}
system_array = (GS,ES)
s = sos.System(system_array)
T= _np.array([[0.5, 0.5], [0.5, -0.5]])
s.add_basis(T, "A", ["g", "e"])
# Transform one of these states to the new basis.
gid = s.A_gid.transform(s.toA)
The nomenclature of the alternate basis levels has the following convention:
The name that the user has specified for the alternate basis.
An underscore.
The name that the user has specified for the new basis state.
Subspaces
The extraction of sub-parts of a multipartite quantum system is an essential task during simulation routines and is supported in SimOS. For composite systems whose combined Hilbert space was spanned with a tensor product, the subsystem is extracted with a partial trace. If a composite system was obtained with a direct sum, subsystems are directly obtained as subsets of the full system operator by the means of projection. For more complicated multipartite systems, our algorithm extracts the desired subsystem with up to three steps in a top-down approach. This routine does not remove members unless they are fully separable from the desired subsystem.
Let us first consider the simple system of two spins S=1/2 that we have already encountered before. The combined Hilbert space is separable into the individual Hilbert spaces since it was constructed with a tensor product.

op = s.Sz
op_subS, op_subI, reverse = sos.subsystem(s, op, "S")
Next, we consider our system of two electronic levels. Here the extraction is performed with a projection operation since the two members were combined with a direct sum.

op = s.GSid
op_subGS, op_subES, reverse = sos.subsystem(s, op, "A")
In addition to the operator in the desired subspace (e.g. op_subS
and op_subGS
),
the subsystem
routine also returns the subspace for the remaining system (e.g. op_subI
and op_subES
).
If the extraction was performed in multiple steps, a list of operators is returned for the remaining system. Each entry of the list
is the subspace which was traced out in a single step of the exctraction procedure.
Further, the routine returns a third argument which is essentially an instruction that can be used
to again construct the full system from density matrices of the individual components. Users may
utilize the reverse_subsystem
for this purpose.
Syntax Reference
- class System(system_array: list | tuple, method='qutip')[source]
A class for representing composite quantum systems of spins and electronic levels. Stores all operators of the system.
- method
Backend that was used for system construction.
- Type:
str
- system
A list of dictionaries for all members of the system.
- Type:
list
- structure
A list/tuple of lists/tuples holding the members’ names which indicates how the multipartite quantum system was constructed.
- Type:
list/tuple
- dim
Hilbert space size of the system.
- Type:
int
- dims
Substructure of the Hilbert space (i.e. list of Hilbert space sizes of “separable” subsystems that were combined with a tensor product, in analogy to qutips dims attribute)
- Type:
list
- id
Identity operator of the system.
- add_basis(T: ndarray, basis_name: str, state_names: list | tuple | ndarray)[source]
Constructs a complete alternate basis for the system using a user-defined transformation matrix. The identity operators of the new basis states and transformation matrices for conversion between the original and the new basis are stored as attributes of the system.
- Parameters:
T (numpy.ndarray) – Transformation matrix, describing the transformation from the native system basis (Zeeman basis) to the new basis.
basis_name (str) – Name of the basis.
state_names (list, tuple, numpy.ndarray) – Series of names for the states of the new basis. A name must be provided for each individual state.
- add_ghostspin(newname: str, spinnames: list, returnnames=False)[source]
Couples N spins of the systems with spin values j1, j2 … to N spins with values |j1-j2-...|, |j1-j2-...+1|, …, |j1+j2+...| and stores the spin operators of the new, coupled spins as attributes of the system class.
- Parameters:
newname (str) – Will be used as a prefix for the spin operators of the new spins.
spinnames (list) – List of the names of all spins that will be coupled.
- Keyword Arguments:
returnnames (
bool
) – If True, a list with the names of all new spins is returned.
- del_property(level_name: str, property_name: str)[source]
Deletes a property of a spin or an electronic level of the system.
- Parameters:
level_name (str) – Name of the spin or electronic level.
property_name (str) – Name of the property.
- get_index(level_name: str)[source]
Returns the position of a spin or an electronic level in the system-list.
- Parameters:
level_name (str) – Name of the spin or electronic level.
- Returns:
Position index.
- reverse_subsystem(op, rop, reverse)[source]
Projects an operator formulated in the Hilbert space of a subsystem back into the Hilbert space of the full system. This function is intended as a counterpart to the subsystem method and difficult to use as a standalone method.
- Parameters:
system (System) – Instance of the System-class.
op – Operator of the subsystem.
reverse_instruction (dict) – A dictionary with instructions on how to project operators of this subsystem back into the full system. Such a dictionary is automatically generated by the subsystem method.
- Returns:
The operator in the Hilbert space of the full system.
- spinops(spindef, method='qutip', prefix='')[source]
Returns single-spin operators.
- Parameters:
spindef (dict) – Definition of the spin for which operators should be build. Must contain a key ‘val’ that specifies the spin value. Can further contain a key ‘type’ to specify the spin type and a key ‘name’ for the spin name. If spin type is ‘NV’, ‘NV-‘or ‘NV+’ a series of additional operators are constructed.
- Keyword Arguments:
method (
str
) – Backend. Default is qutip.prefix (
str
) – A prefix that is added to the keys of the return dictionary.
- Returns:
Dictionary that holds the single spin operators, keys are ‘id’,’x’,’y’,’z’,’+’,’-‘,’p’ for ‘default’ spin type with spin > 0 and ‘id’ if spin = 0.
- subsystem(system, op, selection, keep=True)[source]
Extracts a subsystem from a system. Whenever possible, the extraction is done with a partial trace. Otherwise, the subsystem is extracted by simply dropping all non-member dimensions.
- Parameters:
system (System) – Instance of the System-class.
op – Operator of the system.
selection (dict, list, str) – Specifies the subsystem. Members can be specified with their names or their entire definition, mutiple members can be provided in a list.
- Keyword Arguments:
keep (
bool
) – If True, selection specifies the system remaining after partial trace. If False, selection specifies the subsystem that is removed.
- Returns:
The operator of the selected subsytem.
The operator(s) of the remaining subsystem(s).
list
Instructions on how to project operators of this subsystem back into the full system.