Package reference
- class spinsim.Device(value)
Bases:
EnumThe target device that the integrator is being compiled for.
- CPU = 'cpu'
Use the
numba.jit()LLVM compiler to compile the integrator to run on all CPU cores, in parallel.Note
To use this device option, the user defined field function must be
numba.jit()compilable. See Supported Python features for compilable python features, and Supported Numpy features for compilable numpy features.
- CPU_SINGLE = 'cpu_single'
Use the
numba.jit()LLVM compiler to compile the integrator to run on a single CPU core.Note
To use this device option, the user defined field function must be
numba.jit()compilable. See Supported Python features for compilable python features, and Supported Numpy features for compilable numpy features.
- CUDA = 'cuda'
Use the
numba.cuda.jit()LLVM compiler to compile the integrator to run on an Nvidia cuda compatible GPU, in parallel.Note
To use this device option, the user defined field function must be
numba.cuda.jit()compilable. See Supported CUDA Python features for compilable python features.
- PYTHON = 'python'
Use pure python interpreted code for the integrator, ie, don’t compile the integrator.
- ROC = 'roc'
Use the
numba.roc.jit()LLVM compiler to compile the integrator to run on an AMD ROCm compatible GPU, in parallel.Warning
Work in progress, not currently functional!
- class spinsim.ExponentiationMethod(value)
Bases:
EnumThe implementation to use for matrix exponentiation within the integrator.
- Parameters:
- ANALYTIC = 'analytic'
Analytic expression of the matrix exponential. For spin-half
SpinQuantumNumber.HALFsystems only. SeeUtilities.matrix_exponential_analytic()for more information.
- LIE_TROTTER = 'lie_trotter'
Approximation using the Lie Trotter theorem, using the Pauli matrices and a single quadratic operator. See
Utilities.matrix_exponential_lie_trotter()for more information.
- LIE_TROTTER_8 = 'lie_trotter_8'
Approximation using the Lie Trotter theorem, using all basis elements of su(3). For spin-one
SpinQuantumNumber.HALFsystems only. SeeUtilities.matrix_exponential_lie_trotter_8()for more information.
- class spinsim.IntegrationMethod(value)
Bases:
EnumOptions for describing which method is used during the integration.
- Parameters:
value (
str) – A text label that can be used for archiving.
- EULER = 'euler'
Euler integration method.
- HEUN = 'heun'
Integration method from AtomicPy. Makes two Euler integration steps, one sampling the field from the start of the time step, one sampling the field from the end of the time step. The equivalent of the trapezoidal method.
- MAGNUS_CF4 = 'magnus_cf4'
Commutator free, fourth order Magnus based integrator.
- class spinsim.Results(time: ndarray, time_evolution: ndarray, state: ndarray, spin_calculator: callable)
Bases:
objectThe results of an evaluation of the integrator.
- time
The times that state was evaluated at.
- Type:
numpy.ndarrayofnumpy.float64(time_index)
- time_evolution
The evaluated time evolution operator between each time step. See Numba architecture for some information.
- Type:
numpy.ndarrayofnumpy.float128(time_index, y_index, x_index)
- state
The evaluated quantum state of the spin system over time, written in terms of the eigenstates of the spin projection operator in the z direction.
- Type:
numpy.ndarrayofnumpy.complex128(time_index, magnetic_quantum_number)
- spin
The expected spin projection (Bloch vector) over time. This is calculated just in time using the JITed
callablespin_calculator.- Type:
numpy.ndarrayofnumpy.float64(time_index, spatial_direction)
- spin_calculator
Calculates the expected spin projection (Bloch vector) over time for a given time series of a quantum state. Used to calculate spin the first time it is referenced by the user.
Parameters:
state (
numpy.ndarrayofnumpy.complex128(time_index, magnetic_quantum_number)) - The quantum state of the spin system over time, written in terms of the eigenstates of the spin projection operator in the z direction.
Returns:
spin (
numpy.ndarrayofnumpy.float64(time_index, spatial_direction)) - The expected spin projection (Bloch vector) over time.
- Type:
- Parameters:
time (
numpy.ndarrayofnumpy.float64(time_index)) – The times that state was evaluated at.time_evolution (
numpy.ndarrayofnumpy.float128(time_index, y_index, x_index)) – The evaluated time evolution operator between each time step. See Numba architecture for some information.state (
numpy.ndarrayofnumpy.complex128(time_index, magnetic_quantum_number)) – The evaluated quantum state of the spin system over time, written in terms of the eigenstates of the spin projection operator in the z direction.spin_calculator (
callable) –Calculates the expected spin projection (Bloch vector) over time for a given time series of a quantum state. Used to calculate spin the first time it is referenced by the user.
Parameters:
state (
numpy.ndarrayofnumpy.complex128(time_index, magnetic_quantum_number)) - The quantum state of the spin system over time, written in terms of the eigenstates of the spin projection operator in the z direction.
Returns:
spin (
numpy.ndarrayofnumpy.float64(time_index, spatial_direction)) - The expected spin projection (Bloch vector) over time.
- class spinsim.Simulator(get_field: callable, spin_quantum_number: SpinQuantumNumber, device: Optional[Device] = None, exponentiation_method: Optional[ExponentiationMethod] = None, use_rotating_frame: bool = True, integration_method: IntegrationMethod = IntegrationMethod.MAGNUS_CF4, number_of_squares: int = 24, threads_per_block: int = 64, max_registers: Optional[int] = None, number_of_threads: Optional[int] = None)
Bases:
object- spin_quantum_number
The option to select whether the simulator will integrate a spin-half
SpinQuantumNumber.HALF, or spin-oneSpinQuantumNumber.ONEquantum system.- Type:
- threads_per_block
The size of each thread block (workgroup), in terms of the number of threads (workitems) they each contain, when running on the GPU target devices
Device.CUDA(Device.ROC). Defaults to 64. Modifying might be able to increase execution time for different GPU models.- Type:
- device
The option to select which device will be targeted for integration. That is, whether the integrator is compiled for a CPU or GPU. Defaults to
Device.CUDAif the system it is being run on is Nvidia Cuda compatible, and defaults toDevice.CPUotherwise. SeeDevicefor all options and more details.- Type:
- get_time_evolution
The internal function for evaluating the time evolution operator in parallel. Compiled for chosen device on object constrution.
Parameters:
sweep_parameters (
numpy.ndarrayofnumpy.float64) - The input to theget_field()function supplied by the user. Modifies the field function so the integrator can be used for many experiments, without the need for slow recompilation. For example, if the sweep_parameters is used to define the bias field strength inget_field(), then one can run many simulations, sweeping through bias values, by calling this method multiple times, each time varying sweep_parameters.time_coarse (
numpy.ndarrayofnumpy.float64(time_index)) - The times that state was evaluated at.time_end_points (
numpy.ndarrayofnumpy.float64(start/end)) - The time offset that the experiment is to start at, and the time that the experiment is to finish at. Measured in s.time_step_integration (
float) - The integration time step. Measured in s.time_step_output (
float) - The sample resolution of the output timeseries for the state. Must be a whole number multiple of time_step_integration. Measured in s.time_evolution_output (
numpy.ndarrayofnumpy.float128(time_index, y_index, x_index)) - The evaluated time evolution operator between each time step. See Numba architecture for some information.
- Type:
- spin_calculator
Calculates the expected spin projection (Bloch vector) over time for a given time series of a quantum state. This
callableis passed to theResultsobject returned fromSimulator.evaluate(), and is executed there just in time if the spin property is needed. Compiled for chosen device on object constrution.Parameters:
state (
numpy.ndarrayofnumpy.complex128(time_index, magnetic_quantum_number)) - The quantum state of the spin system over time, written in terms of the eigenstates of the spin projection operator in the z direction.
Returns:
spin (
numpy.ndarrayofnumpy.float64(time_index, spatial_direction)) - The expected spin projection (Bloch vector) over time.
- Type:
- Parameters:
get_field (
callable) –A python function that describes the field that the spin system is being put under. It must have three arguments:
time_sample (
float) - the time to sample the field at, in units of s.simulation_index (
int) - a parameter that can be swept over when multiple simulations need to be run. For example, it is used to sweep over dressing frequencies during the simulations that spinsim was designed for.field_sample (
numpy.ndarrayofnumpy.float64(spatial_index)) the returned value of the field. This is a four dimensional vector, with the first three entries being x, y, z spatial directions (to model a magnetic field, for example), and the fourth entry being the amplitude of the quadratic shift (only appearing, and required, in spin-one systems).
Note
This function must be compilable for the device that the integrator is being compiled for. See
Devicefor more information and links.spin_quantum_number (
SpinQuantumNumber) – The option to select whether the simulator will integrate a spin-halfSpinQuantumNumber.HALF, or spin-oneSpinQuantumNumber.ONEquantum system.device (
Device) – The option to select which device will be targeted for integration. That is, whether the integrator is compiled for a CPU or GPU. Defaults toDevice.CUDAif the system it is being run on is Nvidia Cuda compatible, and defaults toDevice.CPUotherwise. SeeDevicefor all options and more details.exponentiation_method (
ExponentiationMethod) – Which method to use for matrix exponentiation in the integration algorithm. Defaults toExponentiationMethod.LIE_TROTTERwhen spin_quantum_number is set toSpinQuantumNumber.ONE, and defaults toExponentiationMethod.ANALYTICwhen spin_quantum_number is set toSpinQuantumNumber.HALF. SeeExponentiationMethodfor more details.use_rotating_frame (
bool) –Whether or not to use the rotating frame optimisation. Defaults to
True. If set toTrue, the integrator moves into a frame rotating in the z axis by an amount defined by the field in the z direction. This removes the (possibly large) z component of the field, which increases the accuracy of the output since the integrator will on average take smaller steps.Note
The use of a rotating frame is commonly associated with the use of a rotating wave approximation, a technique used to get approximate analytic solutions of spin system dynamics. This is not done when this option is set to
True- no such approximations are made, and the output state in given out of the rotating frame. One can, of course, usespinsimto integrate states in the rotating frame, using the rating wave approximation: just defineget_field()with field functions that use the rotating wave approximation in the rotating frame.integration_method (
IntegrationMethod) – Which integration method to use in the integration. Defaults toIntegrationMethod.MAGNUS_CF4. SeeIntegrationMethodfor more details.number_of_squares (
int) – The number of squares made by the matrix exponentiator, ifExponentiationMethod.LIE_TROTTERis chosen.threads_per_block (
int) – The size of each thread block (workgroup), in terms of the number of threads (workitems) they each contain, when running on the GPU target devicesDevice.CUDA(Device.ROC). Defaults to 64. Modifying might be able to increase execution time for different GPU models.max_registers (
int) –The maximum number of registers allocated per thread when using
Device.CUDAas the target device, and can be modified to increase the execution speed for a specific GPU model.Raising this value allocates more registers (fast memory) to each thread, out of a maximum number for the whole GPU, for each specific GPU model. This means that if more registers are allocated than are available for the GPU model, the GPU must run fewer threads concurrently than it has Cuda cores, meaning some cores are inactive, and the GPU is said to have less occupancy. Lowering the value increases GPU occupancy, meaning more threads run concurrently, at the expense of fewer resgiters being avaliable to each thread, meaning slower memory must be used. Thus, there will be an optimal value of max_registers for each model of GPU running
spinsim, balancing more threads vs faster running threads, and changing this value could increase performance for your GPU. See Achieved Occupancy for Nvidia’s official explanation.number_of_threads (
int) – The number of CPU threads to use when running on a CPU device.
- compile_time_evolver(get_field: callable, spin_quantum_number: SpinQuantumNumber, device: Device, use_rotating_frame: bool = True, integration_method: IntegrationMethod = IntegrationMethod.MAGNUS_CF4, exponentiation_method: Optional[ExponentiationMethod] = None, number_of_squares: int = 24, threads_per_block: int = 64, max_registers: Optional[int] = None)
Compiles the integrator and spin calculation functions of the simulator.
- Parameters:
get_field (
callable) –A python function that describes the field that the spin system is being put under. It must have three arguments:
time_sample (
float) - the time to sample the field at, in units of s.sweep_parameters (
numpy.ndarrayofnumpy.float64) - an array of parameters that can be swept over when multiple simulations need to be run. For example, it is used to sweep over dressing frequencies during the magnetometry experiments that spinsim was designed for.field_sample (
numpy.ndarrayofnumpy.float64(spatial_index)) the returned value of the field. This is a four dimensional vector, with the first three entries being x, y, z spatial directions (to model a magnetic field, for example), and the fourth entry being the amplitude of the quadratic shift (only appearing, and required, in spin-one systems).
Note
This function must be compilable for the device that the integrator is being compiled for. See
Devicefor more information and links.spin_quantum_number (
SpinQuantumNumber) – The option to select whether the simulator will integrate a spin-halfSpinQuantumNumber.HALF, or spin-oneSpinQuantumNumber.ONEquantum system.device (
Device) – The option to select which device will be targeted for integration. That is, whether the integrator is compiled for a CPU or GPU. Defaults toDevice.CUDAif the system it is being run on is Nvidia Cuda compatible, and defaults toDevice.CPUotherwise. SeeDevicefor all options and more details.exponentiation_method (
ExponentiationMethod) – Which method to use for matrix exponentiation in the integration algorithm. Defaults toExponentiationMethod.LIE_TROTTERwhen spin_quantum_number is set toSpinQuantumNumber.ONE, and defaults toExponentiationMethod.ANALYTICwhen spin_quantum_number is set toSpinQuantumNumber.HALF. SeeExponentiationMethodfor more details.use_rotating_frame (
bool) –Whether or not to use the rotating frame optimisation. Defaults to
True. If set toTrue, the integrator moves into a frame rotating in the z axis by an amount defined by the field in the z direction. This removes the (possibly large) z component of the field, which increases the accuracy of the output since the integrator will on average take smaller steps.Note
The use of a rotating frame is commonly associated with the use of a rotating wave approximation, a technique used to get approximate analytic solutions of spin system dynamics. This is not done when this option is set to
True- no such approximations are made, and the output state in given out of the rotating frame. One can, of course, usespinsimto integrate states in the rotating frame, using the rating wave approximation: just defineget_field()with field functions that use the rotating wave approximation in the rotating frame.integration_method (
IntegrationMethod) – Which integration method to use in the integration. Defaults toIntegrationMethod.MAGNUS_CF4. SeeIntegrationMethodfor more details.number_of_squares (
int) – The number of squares made by the matrix exponentiator, ifExponentiationMethod.LIE_TROTTERis chosen.threads_per_block (
int) – The size of each thread block (workgroup), in terms of the number of threads (workitems) they each contain, when running on the GPU target devicesDevice.CUDA(Device.ROC). Defaults to 64. Modifying might be able to increase execution time for different GPU models.max_registers (
int) –The maximum number of registers allocated per thread when using
Device.CUDAas the target device, and can be modified to increase the execution speed for a specific GPU model. Defaults to 63 (optimal for GTX1070, the device used for testing. Note that one extra register per thread is always added to the number specified for control, so really this number is 64).Raising this value allocates more registers (fast memory) to each thread, out of a maximum number for the whole GPU, for each specific GPU model. This means that if more registers are allocated than are available for the GPU model, the GPU must run fewer threads concurrently than it has Cuda cores, meaning some cores are inactive, and the GPU is said to have less occupancy. Lowering the value increases GPU occupancy, meaning more threads run concurrently, at the expense of fewer registers being avaliable to each thread, meaning slower memory must be used. Thus, there will be an optimal value of max_registers for each model of GPU running
spinsim, balancing more threads vs faster running threads, and changing this value could increase performance for your GPU. See Achieved Occupancy for Nvidia’s official explanation.
- evaluate(time_start: float64, time_end: float64, time_step_integration: float64, time_step_output: float64, state_init: ndarray, sweep_parameters: ndarray = [0]) Results
Integrates the time dependent Schroedinger equation and returns the quantum state of the spin system over time.
- Parameters:
sweep_parameters (
numpy.ndarrayofnumpy.float64) – The input to theget_field()function supplied by the user. Modifies the field function so the integrator can be used for many experiments, without the need for slow recompilation. For example, if the sweep_parameters is used to define the bias field strength inget_field(), then one can run many simulations, sweeping through bias values, by calling this method multiple times, each time varying sweep_parameters.time_start (
float) – The time offset that the experiment is to start at. Measured in s.time_end (
float) – The time that the experiment is to finish at. Measured in s. The duration of the experiment is time_end - time_start.time_step_integration (
float) – The integration time step. Measured in s.time_step_output (
float) – The sample resolution of the output timeseries for the state. Must be a whole number multiple of time_step_integration. Measured in s.state_init (
numpy.ndarrayofnumpy.complex128(magnetic_quantum_number)) – The initial quantum state of the spin system, written in terms of the eigenstates of the spin projection operator in the z direction.
- Returns:
results – An object containing the results of the simulation.
- Return type:
- static get_state(state_init: ndarray, state: ndarray, time_evolution: ndarray)
Use the stepwise time evolution operators in succession to find the quantum state timeseries of the 3 level atom.
- Parameters:
state_init (
numpy.ndarrayofnumpy.complex128) – The state (spin wavefunction) of the system at the start of the simulation.state (
numpy.ndarrayofnumpy.complex128(time_index, state_index)) – The state (wavefunction) of the spin system in the lab frame, for each time sampled.time_evolution (
numpy.ndarrayofnumpy.complex128(time_index, bra_state_index, ket_state_index)) – The evaluated time evolution operator between each time step. See Numba architecture for some information.
- class spinsim.SpinQuantumNumber(value)
Bases:
EnumOptions for the spin quantum number of a system.
- Parameters:
value (
float) – The numerical value of the spin quantum number.dimension (
int) – Dimension of the hilbert space the states with this spin belong to.label (
str) – A text label that can be used for archiving.plus_x (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.plus_y (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.plus_z (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.zero_x (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.zero_y (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.zero_z (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.minus_x (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.minus_y (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.minus_z (
numpy.ndarrayofnumpy.complex128) – Eigenstates of the spin operators for quick reference.
- HALF = 0.5
For two level systems.
- ONE = 1
For three level systems.
- class spinsim.Utilities(spin_quantum_number: SpinQuantumNumber, device: Device, threads_per_block: int, number_of_squares: int)
Bases:
objectA on object that contains definitions of all of the device functions (functions compiled for use on the target device) used in the integrator. These device functions are compiled for the chosen target device on construction of the object.
- conj(z)
Conjugate of a complex number.
\[\begin{split}\begin{align*} (a + ib)^* &= a - ib\\ a, b &\in \mathbb{R} \end{align*}\end{split}\]Parameters:
z (
numpy.complex128) - The complex number to take the conjugate of.
Returns
cz (
numpy.complex128) - The conjugate of z.
- Type:
- expm1i(b)
For real input \(b\), returns \(\exp(ib) - 1\), while avoiding floating point cancellation errors.
Parameters:
b (
numpy.float64) - The imaginary component to exponentiate.
Returns
em1i (
numpy.complex128) - The evalauted output.
- Type:
- cos_exp_m1(a, b)
For real input \(a\), \(b\), returns \(\cos(a)\exp(ib) - 1\), while avoiding floating point cancellation errors.
Parameters:
a (
numpy.float64) - The real component to take the cosine of.b (
numpy.float64) - The imaginary component to exponentiate.
Returns
cem1 (
numpy.complex128) - The evalauted output.
- Type:
- cos_m1(a, b)
For real input \(a\), returns \(\cos(a) - 1\), while avoiding floating point cancellation errors.
Parameters:
a (
numpy.float64) - The real component to take the cosine of.
Returns
cm1 (
numpy.complex128) - The evalauted output.
- Type:
- set_to(operator, result)
Copy the contents of one matrix into another.
\[(A)_{i, j} = (B)_{i, j}\]Parameters:
operator (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to copy from.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to copy to.
- Type:
- set_to_one(operator)
Make a matrix the multiplicative identity, ie, \(1\).
\[\begin{split}\begin{align*} (A)_{i, j} &= \delta_{i, j}\\ &= \begin{cases} 1,&i = j\\ 0,&i\neq j \end{cases} \end{align*}\end{split}\]Parameters:
operator (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to set to \(1\).
- Type:
- set_to_zero(operator)
Make a matrix the zero matrix.
\[\begin{align*} (A)_{i, j} &= 0 \end{align*}\]Parameters:
operator (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to set to \(0\).
- Type:
- matrix_multiply(left, right, result)
Multiply matrices left and right together, to be returned in result.
\[\begin{align*} (LR)_{i,k} = \sum_j (L)_{i,j} (R)_{j,k} \end{align*}\]Parameters:
left (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to left multiply by.right (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix to right multiply by.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - A matrix to be filled with the result of the product.
- Type:
- matrix_square_m1(operator, result)
For matrix \(A = 1 + a\) \(S = A^2 = 1 + s\). Here the input is the residuals \(a\), and the output is \(s\). This is a way to evaluate \(s\) without floating point cancellation error. Specifically,
\[\begin{split}\begin{align*} s &= S - 1\\ &= A^2 - 1\\ &= (2\cdot 1 + a)a \end{align*}\end{split}\]Parameters:
operator (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The residual of the matrix to square.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - A matrix to be filled with the residual of the result of the product.
- Type:
- matrix_multiply_m1(left, right, result)
For matrices \(L = 1 + l\) and \(R = 1 + r\), evaluates \(O = LR = 1 + o\). Here the inputs are the residuals \(l\) and \(r\), and the output is \(o\). This is a way to evaluate \(o\) without floating point cancellation error. Specifically,
\[\begin{split}\begin{align*} o &= O - 1\\ &= LR - 1\\ &= l + r + lr \end{align*}\end{split}\]Parameters:
left (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The residual of the matrix to left multiply by.right (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The residual of the matrix to right multiply by.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - A matrix to be filled with the residual of the result of the product.
- Type:
- matrix_exponential_analytic(field_sample, result)
Calculates a \(\mathfrak{su}(2)\) matrix exponential based on its analytic form.
Warning
Only available for use with spin-half systems. Will not work with spin-one systems.
Assumes the exponent is an imaginary linear combination of \(\mathfrak{su}(2)\), being,
\[\begin{align*} A &= -i(\omega_x J_x + \omega_y J_y + \omega_z J_z), \end{align*}\]with
\[\begin{split}\begin{align*} J_x &= \frac{1}{2}\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix},& J_y &= \frac{1}{2}\begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix},& J_z &= \frac{1}{2}\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \end{align*}\end{split}\]Then the exponential can be calculated as
\[\begin{split}\begin{align*} \exp(A) &= \exp(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z)\\ &= \begin{pmatrix} \cos(\frac{\omega_r}{2}) - i\frac{\omega_z}{\omega_r}\sin(\frac{\omega_r}{2}) & -\frac{\omega_y + i\omega_x}{\omega_r}\sin(\frac{\omega_r}{2})\\ \frac{\omega_y - i\omega_x}{\omega_r}\sin(\frac{\omega_r}{2}) & \cos(\frac{\omega_r}{2}) + i\frac{\omega_z}{\omega_r}\sin(\frac{\omega_r}{2}) \end{pmatrix} \end{align*}\end{split}\]with \(\omega_r = \sqrt{\omega_x^2 + \omega_y^2 + \omega_z^2}\).
Parameters:
field_sample (
numpy.ndarrayofnumpy.float64, (y_index, x_index)) - The values of \(\omega_x\), \(\omega_y\) and \(\omega_z\) respectively, as described above.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix which the result of the exponentiation is to be written to.
- Type:
- matrix_exponential_lie_trotter(field_sample, result)
Calculates a matrix exponential based on the Lie Product Formula,
\[\exp(A + B) = \lim_{c \to \infty} \left(\exp\left(\frac{1}{c}A\right) \exp\left(\frac{1}{c}B\right)\right)^c.\]For spin-half systems:
Assumes the exponent is an imaginary linear combination of a subspace of \(\mathfrak{su}(2)\), being,
\[\begin{align*} A &= -i(\omega_x J_x + \omega_y J_y + \omega_z J_z), \end{align*}\]with
\[\begin{split}\begin{align*} J_x &= \frac{1}{2}\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix},& J_y &= \frac{1}{2}\begin{pmatrix} 0 & -i \\ i & 0 \end{pmatrix},& J_z &= \frac{1}{2}\begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix} \end{align*}\end{split}\]Then the exponential can be approximated as, for large \(\tau\),
\[\begin{split}\begin{align*} \exp(A) =& \exp\left(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z\right)\\ =& \exp\left(2^{-\tau}\left(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z\right)\right)^{2^\tau}\\ \approx& \biggl(\exp\left(-i\frac12 2^{-\tau} \omega_z J_z\right)\exp\left(-i\left(2^{-\tau} \omega_\phi J_\phi\right)\right)\exp\left(-i\frac12 2^{-\tau} \omega_z J_z\right)\biggr)^{2^\tau}\\ =& \begin{pmatrix} \cos\left(\frac{\Phi}{2}\right)e^{-iz} & -i\sin\left(\frac{\Phi}{2}\right) e^{i\phi}\\ -i\sin\left(\frac{\Phi}{2}\right) e^{-i\phi} & \cos\left(\frac{\Phi}{2}\right)e^{iz} \end{pmatrix}^{2^\tau}\\ =& T^{2^\tau}. \end{align*}\end{split}\]Here \(z = 2^{-\tau}\frac{\omega_z}{2}\), \(\Phi = 2^{-\tau}\sqrt{\omega_x^2 + \omega_y^2}\), and \(\phi = \mathrm{atan}2(\omega_y, \omega_x)\).
For spin-one systems
Assumes the exponent is an imaginary linear combination of a subspace of \(\mathfrak{su}(3)\), being,
\[\begin{align*} A &= -i(\omega_x J_x + \omega_y J_y + \omega_z J_z + \omega_q Q), \end{align*}\]with
\[\begin{split}\begin{align*} J_x &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & 1 & 0 \\ 1 & 0 & 1 \\ 0 & 1 & 0 \end{pmatrix},& J_y &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & -i & 0 \\ i & 0 & -i \\ 0 & i & 0 \end{pmatrix},\\ J_z &= \begin{pmatrix} 1 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & -1 \end{pmatrix},& Q &= \frac{1}{3}\begin{pmatrix} 1 & 0 & 0 \\ 0 & -2 & 0 \\ 0 & 0 & 1 \end{pmatrix} \end{align*}\end{split}\]Then the exponential can be approximated as, for large \(\tau\),
\[\begin{split}\begin{align*} \exp(A) =& \exp\left(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z - i\omega_q Q\right)\\ =& \exp\left(2^{-\tau}\left(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z - i\omega_q Q\right)\right)^{2^\tau}\\ \approx& \biggl(\exp\left(-i\frac12\left(2^{-\tau} \omega_z J_z + 2^{-\tau}\omega_q Q\right)\right)\nonumber\\ &\cdot\exp\left(-i\left(2^{-\tau} \omega_\phi J_\phi\right)\right)\nonumber\\ &\cdot\exp\left(-i\frac12\left(2^{-\tau} \omega_z J_z + 2^{-\tau} \omega_q Q\right)\right)\biggr)^{2^\tau}\\ =& \begin{pmatrix} \left(\cos\left(\frac{\Phi}{2}\right) e^{-iz}e^{-iq}\right)^2 & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{iq}e^{-iz}e^{-i\phi} & -\left(\sin\left(\frac{\Phi}{2}\right)e^{iq}e^{-i\phi}\right)^2\\ \frac{-i}{\sqrt{2}} \sin(\Phi)e^{iq}e^{-iz}e^{i\phi} & \cos(\Phi)e^{i4q} & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{iq}e^{iz}e^{-i\phi}\\ -\left(\sin\left(\frac{\Phi}{2}\right)e^{-iq}e^{i\phi}\right)^2 & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{iq}e^{iz}e^{i\phi} & \left(\cos\left(\frac{\Phi}{2}\right) e^{iz}e^{-iq}\right)^2 \end{pmatrix}^{2^\tau}.\\ \end{align*}\end{split}\]Here \(z = 2^{-\tau}\frac{\omega_z}{2}\), \(q = 2^{-\tau}\frac{\omega_q}{6}\), \(\Phi = 2^{-\tau}\sqrt{\omega_x^2 + \omega_y^2}\), and \(\phi = \mathrm{atan}2(\omega_y, \omega_x)\). Once \(T\) is calculated, it is then recursively squared \(\tau\) times to obtain \(\exp(A)\).
Parameters:
field_sample (
numpy.ndarrayofnumpy.float64, (y_index, x_index)) - The values of x, y and z (and q for spin-one) respectively, as described above.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix which the result of the exponentiation is to be written to.number_of_squares (
int) - The number of squares to make to the approximate matrix (\(\tau\) above).
- Type:
- matrix_exponential_lie_trotter_8(field_sample, result)
Calculates a matrix exponential based on the Lie Product Formula,
\[\exp(A + B) = \lim_{c \to \infty} \left(\exp\left(\frac{1}{c}A\right) \exp\left(\frac{1}{c}B\right)\right)^c.\]Warning
Only available for use with spin-one systems. Will not work with spin-half systems.
Assumes the exponent is an imaginary linear combination elements of \(\mathfrak{su}(3)\), being,
\[\begin{align*} A &= -i(\omega_x J_x + \omega_y J_y + \omega_z J_z + \omega_q Q + \omega_{u1} U_1 + \omega_{u2} U_2 + \omega_{v1} V_1 + \omega_{v2} V_2), \end{align*}\]with
\[\begin{split}\begin{align*} J_x &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & 1 & 0 \\ 1 & 0 & 1 \\ 0 & 1 & 0 \end{pmatrix},& J_y &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & -i & 0 \\ i & 0 & -i \\ 0 & i & 0 \end{pmatrix},\\ J_z &= \begin{pmatrix} 1 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & -1 \end{pmatrix},& Q &= \frac{1}{3}\begin{pmatrix} 1 & 0 & 0 \\ 0 & -2 & 0 \\ 0 & 0 & 1 \end{pmatrix},\\ U_1 &= \begin{pmatrix} 0 & 0 & 1 \\ 0 & 0 & 0 \\ 1 & 0 & 0 \end{pmatrix},& U_2 &= \begin{pmatrix} 0 & 0 & -i \\ 0 & 0 & 0 \\ i & 0 & 0 \end{pmatrix},\\ V_1 &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & 1 & 0 \\ 1 & 0 & -1 \\ 0 & -1 & 0 \end{pmatrix},& V_2 &= \frac{1}{\sqrt{2}}\begin{pmatrix} 0 & -i & 0 \\ i & 0 & i \\ 0 & -i & 0 \end{pmatrix}.\\ \end{align*}\end{split}\]Then the exponential can be approximated as, for large \(\tau\),
\[\begin{split}\begin{align*} \exp(A) =& \exp\biggl(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z - i\omega_q Q\\ &- i\omega_{u1} U_1 - i\omega_{u2} U_2 - i\omega_{v1} V_1 - i\omega_{v2} V_2\biggr)\\ & \exp\biggl(2^{-\tau}\biggl(-i\omega_x J_x - i\omega_y J_y - i\omega_z J_z - i\omega_q Q\\ &- i\omega_{u1} U_1 - i\omega_{u2} U_2 - i\omega_{v1} V_1 - i\omega_{v2} V_2\biggr)\biggr)^{2^\tau}\\ \approx& \biggl(\exp\left(-i2^{-\tau} \omega_\phi J_\phi\right)\exp\left(-i2^{-\tau} \omega_{u\phi} U_{u\phi}\right)\\ &\cdot\exp\left(-i2^{-\tau} \omega_{v\phi} V_{v\phi}\right)\exp\left(-i2^{-\tau} \omega_z J_z -i2^{-\tau} \omega_q Q \right)\biggr)^{2^\tau}\\ =& \biggl(\begin{pmatrix} \cos^2\left(\frac{\Phi}{2}\right) & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{-i\phi} & -\left(\sin\left(\frac{\Phi}{2}\right)e^{-i\phi}\right)^2\\ \frac{-i}{\sqrt{2}} \sin(\Phi)e^{i\phi} & \cos\left(\Phi\right) & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{-i\phi}\\ -\left(\sin\left(\frac{\Phi}{2}\right)e^{i\phi}\right)^2 & \frac{-i}{\sqrt{2}} \sin(\Phi)e^{i\phi} & \cos^2\left(\frac{\Phi}{2}\right) \end{pmatrix}\\ &\cdot \begin{pmatrix} \cos\left(\Phi_u\right) & 0 & -i \sin\left(\Phi_u\right)e^{-i\phi_u}\\ 0 & 1 & 0\\ -i \sin\left(\Phi_u\right)e^{i\phi_u} & 0 & \cos\left(\Phi_u\right) \end{pmatrix}\\ &\cdot \begin{pmatrix} \cos^2\left(\frac{\Phi_v}{2}\right) & \frac{-i}{\sqrt{2}} \sin(\Phi_v)e^{-i\phi_v} & \left(\sin\left(\frac{\Phi_v}{2}\right)e^{-i\phi_v}\right)^2\\ \frac{-i}{\sqrt{2}} \sin(\Phi_v)e^{i\phi_v} & \cos\left(\Phi_v\right) & \frac{i}{\sqrt{2}} \sin(\Phi_v)e^{-i\phi_v}\\ \left(\sin\left(\frac{\Phi_v}{2}\right)e^{i\phi_v}\right)^2 & \frac{i}{\sqrt{2}} \sin(\Phi_v)e^{i\phi_v} & \cos^2\left(\frac{\Phi_v}{2}\right) \end{pmatrix}\\ &\cdot \begin{pmatrix} e^{-iz - iq} & 0 & 0\\ 0 & e^{i2q} & 0\\ 0 & 0 & e^{iz - iq} \end{pmatrix}\biggr)^{2^\tau}\\ =& T^{2^\tau}. \end{align*}\end{split}\]Here \(z = 2^{-\tau}\frac{\omega_z}{2}\), \(q = 2^{-\tau}\frac{\omega_q}{6}\), \(\Phi = 2^{-\tau}\sqrt{\omega_x^2 + \omega_y^2}\), \(\phi = \mathrm{atan}2(\omega_y, \omega_x)\), \(\Phi_u = 2^{-\tau}\sqrt{\omega_{u1}^2 + \omega_{u2}^2}\), \(\phi_u = \mathrm{atan}2(\omega_{u1}, \omega_{u2})\), \(\Phi_v = 2^{-\tau}\sqrt{\omega_{v1}^2 + \omega_{v2}^2}\), and \(\phi_v = \mathrm{atan}2(\omega_{v1}, \omega_{v2})\). Once \(T\) is calculated, it is then recursively squared \(\tau\) times to obtain \(\exp(A)\).
Parameters:
field_sample (
numpy.ndarrayofnumpy.float64, (y_index, x_index)) - The values of x, y, z, q, u1, u2, v1 and v2 respectively, as described above.result (
numpy.ndarrayofnumpy.complex128, (y_index, x_index)) - The matrix which the result of the exponentiation is to be written to.number_of_squares (
int) - The number of squares to make to the approximate matrix (\(\tau\) above).
- Type:
- Parameters:
spin_quantum_number (
SpinQuantumNumber) – The option to select whether the simulator will integrate a spin-halfSpinQuantumNumber.HALF, or spin-oneSpinQuantumNumber.ONEquantum system.device (
Device) – The option to select which device will be targeted for integration. That is, whether the integrator is compiled for a CPU or GPU. Defaults toDevice.CUDAif the system it is being run on is Nvidia Cuda compatible, and defaults toDevice.CPUotherwise. SeeDevicefor all options and more details.threads_per_block (
int) – The size of each thread block (workgroup), in terms of the number of threads (workitems) they each contain, when running on the GPU target devicesDevice.CUDA(Device.ROC). Defaults to 64. Modifying might be able to increase execution time for different GPU models.
- spinsim.generate_interpolation_sampler(time: ndarray, amplitude: ndarray, order=3, zero_boundary=True, device: Optional[Device] = None)
Uses a user provided time series to create a field sampling function that is compatible with the
Simulatorobject. Samples in the time series do not have to be equally spaced, and as such the user much specify anumpy.ndarrayfor bothtimeandamplitude.Note
These interpolators can be set up in a variety of ways, such pulse trains where the output modulates a sinusoid. See the examples to learn more.
The sampler can be defined to use one of three kinds of interpolation, defined by the parameter
order. Below we use \(t_j\) and \(b_j\) for the time and amplitude samples respectively, \(t\) for the sampling time, and \(\omega(t)\) for the field value at the sampling time.Use
order= 0 to use the Zero Order Hold (ZOH) protocol:\[\omega_a(t) += b_j \quad \text{for } t_j < t < t_{j + 1}\]This method can be used for simulating hard pulse trains.
Use
order= 1 to use the linear interpolation (LERP) protocol:\[\begin{split}\tau =& \frac{t - t_j}{t_{j + 1} - t_j},\\ \omega_a(t) +=& (1 - \tau) b_j + \tau b_{j + 1} \quad \text{for } t_j < t < t_{j + 1}\end{split}\]This method can be used for simulating linear ramps.
Use
order= 3 to use the cubic Hermite spline using the finite difference gradient:\[\begin{split}\tau =& \frac{t - t_j}{t_{j + 1} - t_j},\\ m_j =& \frac12 \left( \frac{b_{j + 1} - b_j}{t_{j + 1} - t_j} + \frac{b_j - b_{j - 1}}{t_j - t_{j - 1}} \right) \\ \omega_a(t) +=& (2\tau^3 - 3\tau^2 + 1) b_j + (-2\tau^3 + 3\tau^2) b_{j + 1} + (\tau^3 - 2\tau^2 + \tau) (t_{j + 1} - t_j) m_j \\ &+ (\tau^3 - \tau^2) (t_{j + 1} - t_j) m_{j + 1} \quad \text{for } t_j < t < t_{j + 1}\end{split}\]This method is best for simulating based off of recorded traces or smooth functions that cannot be constructed with basic functions in
mathornumpy.For any value of
order, if \(t < t_0\) or \(t > t_\text{end}\) then there are two options. Ifzero_boundaryis set toTrue(default), then \(\omega_a(t) = 0\). Otherwise, if \(t < t_0\), then \(\omega_a(t) = b_0\), and if \(t > t_\text{end}\), then \(\omega_a(t) = b_\text{end}\).- Parameters:
time (
numpy.ndarrayofnumpy.float64 (time_index)) – The times that correspond to the sampled amplitudes inamplitude. Note that times do not have to be equally spaced.amplitude (
numpy.ndarrayofnumpy.float64 (time_index)) – The sampled amplitudes to be interpolated between.order (
int) – The interpolation method to be used by the interpolator. Defaults to 3 (for cubic interpolation). For more information, see above.zero_boundary (
bool) – Whether or not the boundaries (ie times before the first and after the final sample) should be zero padded. Defaults toTrue(they will be zero padded). For more information, see above.device (
Device) – The device that the interpolator should be compiled to. IfNone(the default), it will compile to CUDA if a CUDA compatible device is found, and multithread CPU otherwise. To be used with aSimulatorinstance, you must select the same device as the one selected by that. If the interpolator used in a context outside ofspinsim(plotting the interpolator output, for example), then you must selectDevice.CPUorDevice.CPU_SINGLE.
- Returns:
sampler – An interpolating function based off the time series that can be used as or within a
get_field()function for aSimulatorinstance.Parameters:
time_sample (
float) - The time at which to sample the timeseries.user_parameters (
numpy.ndarrayofnumpy.float64, (parameter_index)) - Unused here, but a required format for theSimulatorinstance to read.field_sample (
numpy.ndarrayofnumpy.float64, (y_index, x_index)) - The array of field values that this function adds to.
Returns:
field_sample (
float) - The interpolated value of the amplitude.
- Return type: