SpinChain

class qugradlab.systems.semiconducting.esr.SpinChain(spins: int, zeeman_splittings: ndarray[float], max_drive_strength: float, J_max: float, J_min: float = 0, feromagnetic: bool = True, use_graph: bool = True)[source]

Bases: Controls, QubitSystem

A qugrad.QuantumSystem for a spin chain with electron spin resonance (ESR) controls. The Hamiltonian is given by

\[ H(t) = \sum_{i=0}^{\texttt{spins}-1} \frac{1}{2} B_i Z_i + g(t) \sum_{i=0}^{\texttt{spins}-1} \frac{1}{2} X_i + \frac{1}{4} \sum_{i=0}^{\texttt{spins}-2} J_i(t) \vec\sigma_i \cdot \vec\sigma_{i+1}, \]
where \(\vec\sigma_i\equiv\begin{pmatrix}X_i & Y_i & Z_i\end{pmatrix}^\intercal\) is the vector of the Pauli-x, -y, and -z operators acting on the \(i\)th spin, \(B_i\) are the Zeeman splittings, \(J_i(t)\) is the exchange coupling, and
\[ g(t) = \sum_{j=0}^{\texttt{spins}-1}\real\left(a_j(t)e^{i\omega_j t}\right), \]
is the Rabi drive with frequency components \(\omega_j\) and amplitudes \(a_j(t)\).

  • Attributes

    H0

    The systems drift Hamiltonian as a dim x dim matrix.

    Hs

    An array of the system's control Hamiltonians with shape (n_ctrl, dim, dim).

    J_max

    The maximum value of the exchange coupling \(J\)

    J_min

    The minimum value of the exchange coupling \(J\)

    dim

    The dimension of states in the quantum system.

    evolver

    The integrator used for time evolutions of the system.

    ferromagnetic

    Whether the exchange coupling is ferromagnetic or antiferromagnetic

    hilbert_space

    The Hilbert space of the system

    max_drive_strength

    The maximum drive strength that can be applied to the system

    n_ctrl

    The number of control Hamiltonians.

    state_shape

    The shape of the states in the system.

    using_graph

    Whether to use TensorFlow graphs during computation.

    zeeman_splittings

    The Zeeman splittings of the spins

    Methods

    H

    Computes the system Hamiltonian for the specified control amplitudes.

    __init__

    Initialises a spin chain with ESR controls.

    _envolope_processing

    When calling any evolution method (listed in the See also section section) _pre_processing() is executed on the arguements before the control amplitudes are modulated by the frequencies during _envolope_processing() and then finally the modulated control amplitudes are used by the evolution method.

    _pre_processing

    When calling any evolution method (listed in the See also section) _pre_processing() is executed on the arguments before the control amplitudes are modulated by the frequencies (during _envolope_processing()) and then finally the modulated control amplitudes are used by the evolution method.

    evolved_expectation_value

    Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the final state using evolved_expectation_value() from PySTE.

    evolved_expectation_value_all

    Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the state at each time-step using evolved_expectation_value_all() from PySTE.

    get_driving_pulses

    When calling any evolution method (listed in the See also section`) get_driving_pulses() is executed on the arguements before the evolution method.

    gradient

    Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the final state and then computes the gradient of the final state with respect to the first argument (args[0]) using switching_function() from PySTE.

    initialise_evolver

    Initialises evolver with an evolver from PySTE.

    propagate

    Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes using propagate() from PySTE.

    propagate_all

    Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes using propagate_all() from PySTE and returns the state at each time-step.

    propagate_collection

    Evolves a collection of state vectors under the time-dependent Hamiltonian defined by the control amplitudes using propagate_collection() from PySTE.

    pulse_form

    Initialises a new QuantumSystem in which _pre_processing() corresponds to executing pulse_function() and piping the output into the previous definition of _pre_processing().

H(ctrl_amp: ndarray[float] | ndarray[Callable[[float], ndarray[float]]]) ndarray[complex] | Callable[[float], ndarray[complex]]

Computes the system Hamiltonian for the specified control amplitudes.

Parameters:

ctrl_amp (NDArray[Shape[s := Any_Shape, n_ctrl], float | Callable[[float], np.ndarray[float]]]) – The control amplitudes (stored in the last axis). The prior axes allow for multiple sets of control amplitudes to be passed and the Hamiltonian for each computed. The control amplitudes can be passed as np.ndarray[float] to compute the system Hamiltonian for a specific value of the control ampltiudes. Alternatively, the control amplitudes can be passed as np.ndarray[Callable[[float], np.ndarray[float]]] where each element is a function of time. This will generate a time-dependent Hamiltonian: a function that takes a single parameter (time) and returns the Hamiltonian at this time.

Returns:

Either the systems Hamiltonian stored in the last two axes (if specific control amplitudes were passed) or a collection of time-dependent Hamiltonians (if time-dependent controls were passed).

Return type:

NDArray[Shape[s, dim, dim], complex] | NDArray[Shape[s], Callable[[float], np.ndarray[complex]]]]

__init__(spins: int, zeeman_splittings: ndarray[float], max_drive_strength: float, J_max: float, J_min: float = 0, feromagnetic: bool = True, use_graph: bool = True)[source]

Initialises a spin chain with ESR controls. The Hamiltonian is given by:

\[ H(t) = \sum_{i=0}^{\texttt{spins}-1} \frac{1}{2} B_i Z_i + g(t) \sum_{i=0}^{\texttt{spins}-1} \frac{1}{2} X_i + \frac{1}{4} \sum_{i=0}^{\texttt{spins}-2} J_i(t) \vec\sigma_i \cdot \vec\sigma_{i+1}, \]
where \(\vec\sigma_i\equiv\begin{pmatrix}X_i&Y_i&Z_i\end{pmatrix}^\intercal\) is the vector of the Pauli-x, -y, and -z operators acting on the \(i\)th spin, \(B_i\) corresponds to zeeman_splittings, \(J_i(t)\) is the exchange coupling, and
\[ g(t) = \sum_{j=0}^{\texttt{spins}-1}\real\left(a_j(t)e^{i\omega_j t}\right), \]
is the Rabi drive with frequency components \(\omega_j\) and amplitudes \(a_j(t)\).

Parameters:
  • spins (int) – The number of spins in the chain

  • zeeman_splittings (NDArray[Shape[spins], float]) – The Zeeman splitting of each of the spins

  • max_drive_strength (float) –

    The maximum drive strength that can be applied at a specific frequency and quadrature. That is if their are n_drive_ctrl frequencies and both quadratures are used then the maximum amplitude of the drive that can be applied to the device is:

    np.sqrt(2) * n_drive_ctrl * max_drive_strength
    

  • J_max (float) – The minimum value of the exchange coupling \(J\)

  • J_min (float) – The maximum value of the exchange coupling \(J\), by default 0

  • feromagnetic (bool) – If True, the exchange coupling is ferromagnetic. If False, the exchange coupling is antiferromagnetic. By default, True.

  • use_graph (bool) – Whether to use TensorFlow graphs during computation, by default True

_envolope_processing(ctrl_amp, dt: float, frequencies, number_channels: list[int]) tuple

When calling any evolution method (listed in the See also section section) _pre_processing() is executed on the arguements before the control amplitudes are modulated by the frequencies during _envolope_processing() and then finally the modulated control amplitudes are used by the evolution method.

Parameters:
  • ctrl_amp (tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]) – The envolope control amplitudes

  • dt (float) – The itegration time step

  • frequencies (tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]) – The frequencies to modulate the control amplitudes with

  • number_channels (list[int]) –

    The number of channels associated with each control Hamiltonian

    Warning

    This must be a list and not an NDArray or a TensorFlow tensor.

Returns:

The modulated control amplitudes

Return type:

tf.Tensor[Shape[n_time_steps, n_ctrl], tf.complex128]

_pre_processing(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float) tuple

When calling any evolution method (listed in the See also section) _pre_processing() is executed on the arguments before the control amplitudes are modulated by the frequencies (during _envolope_processing()) and then finally the modulated control amplitudes are used by the evolution method.

_pre_processing() can be overridden to produce desired pulse shapes. You can either override _pre_processing() directly by creating a child class, or you can use pulse_form().

For gradient() to function correctly _pre_processing() should be written in TensorFlow.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

Returns:

tuple[tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], tf.Tensor[Shape[ – A tuple of 1. The control amplitude envolopes 2. The initial state 3. The integrator time step 4. The frequencies to modulate the control amplitude envolopes with 5. A list of the number of channels for each control Hamiltonian

Warning

The number of channels for each control Hamiltonian must be stored as a list and not an NDArray or a TensorFlow tensor.

Return type:

attr:state_shape], tf.complex128], float, tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], list[int]]

evolved_expectation_value(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float, observable: ndarray[complex]) complex

Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the final state using evolved_expectation_value() from PySTE.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

  • observable (NDArray[Shape[dim, dim], complex]) – The observable to take the expectation value of.

Warning

Keyword arguments are not supported.

Returns:

The expectation value.

Return type:

complex

evolved_expectation_value_all(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float, observable: ndarray[complex]) ndarray[complex]

Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the state at each time-step using evolved_expectation_value_all() from PySTE.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

  • observable (NDArray[Shape[dim, dim], complex]) – The observable to take the expectation value of.

Warning

Keyword arguments are not supported.

Returns:

The state at each integrator time step (including the initial state).

Return type:

NDArray[Shape[n_time_steps+1], complex]

get_driving_pulses(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float) tuple[ndarray[complex], ndarray[complex], float]

When calling any evolution method (listed in the See also section`) get_driving_pulses() is executed on the arguements before the evolution method.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

Warning

Keyword arguments are not supported.

Returns:

A tuple of: 1. Control amplitudes 2. Initial state 3. Integrator time step

Return type:

tuple[NDArray[Shape[n_time_steps, n_ctrl], complex], NDArray[Shape[state_shape], complex], float]

gradient(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float, observable: ndarray[complex]) tuple[float, ndarray[float]]

Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes and computes the expectation value of a specified observable with respect to the final state and then computes the gradient of the final state with respect to the first argument (args[0]) using switching_function() from PySTE.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

  • observable (NDArray[Shape[dim, dim], complex]) – The observable to take the expectation value of.

Warning

Keyword arguments are not supported.

Returns:

A tuple of the expectation value and the gradient.

Return type:

tuple[complex, NDArray[Shape[n_parameters], float]]

initialise_evolver(sparse: bool = False, force_dynamic: bool = False)

Initialises evolver with an evolver from PySTE. PySTE is Python wrapper around the C++ header-only library Suzuki-Trotter-Evolver: a fast Schrödinger solver utilising the first-order Suzuki-Trotter expansion.

Warning

This can take a very long time to execute, especially for large Hilbert space dimensions. If you plan to evolve the same quantum system many times we recommended pickling the evolver.

Parameters:
  • sparse (bool) – Whether to use sparse or dense matrices during integration. To make a decision on whether sparse or dense matrices are likely to lead to faster integration you can consult the benchmarks at https://PySTE.readthedocs.io/en/latest/benchmarks.

  • force_dynamic (bool) –

    Whether to force PySTE to use a dynamic evolver.

    Note

    PySTE has precompiled evolvers for specific Hilbert space dimensions and numbers of control Hamiltonians. When these cannot be found PySTE uses less efficient evolvers with the Hilbert space dimension and the number of controls determined dynamically at runtime.

propagate(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float) ndarray[complex]

Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes using propagate() from PySTE.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

Warning

Keyword arguments are not supported.

Returns:

The final state

Return type:

NDArray[Shape[state_shape], complex]

propagate_all(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float) ndarray[complex]

Evolves a state vector under the time-dependent Hamiltonian defined by the control amplitudes using propagate_all() from PySTE and returns the state at each time-step.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) – The itegration time step

Warning

Keyword arguments are not supported.

Returns:

The state at each integrator time step (including the initial state).

Return type:

NDArray[Shape[n_time_steps+1, state_shape], complex]

propagate_collection(drive_ctrl_amp: ndarray[complex], drive_frequencies: ndarray[complex], J_ctrl_amp: ndarray[complex], initial_state: ndarray[complex], dt: float) ndarray[complex]

Evolves a collection of state vectors under the time-dependent Hamiltonian defined by the control amplitudes using propagate_collection() from PySTE.

Parameters:
  • drive_ctrl_amp (NDArray[Shape[n_time_steps, n_drive_ctrl], complex]) – The control amplitudes for the global Rabi-drive Hamiltonian. These values should be in the range [-1, 1] and will be linearly rescaled to the range [− max_drive_strength, max_drive_strength] where -1 corresponds to the − max_drive_strength and 1 to the max_drive_strength.

  • drive_frequencies (NDArray[Shape[n_drive_ctrl], complex]) – The frequencies to modulate the control amplitude of the global Rabi-drive Hamiltonian with

  • J_ctrl_amp (NDArray[Shape[n_time_steps, n_J_ctrl], complex]) – The control amplitudes for the exchange Hamiltonians. These values should be in the range [-1, 1] and will be linearly rescaled to the range [J_min, J_max] where -1 corresponds to the J_min and 1 to the J_max.

  • initial_state (NDArray[Shape[dim], complex]) – The initial state for the integrator

  • dt (float) –

    The itegration time step

    Warning

    This must be a list and not an NDArray or a TensorFlow tensor.

Warning

Keyword arguments are not supported.

Returns:

The final state

Return type:

NDArray[Shape[n_states, state_shape], complex]

pulse_form(pulse_function: Callable, path: str | None = None) PulseForm

Initialises a new QuantumSystem in which _pre_processing() corresponds to executing pulse_function() and piping the output into the previous definition of _pre_processing().

Parameters:

pulse_function (Callable) – The function to compose with _pre_processing().

Returns:

The new QuantumSystem

Return type:

PulseForm

property H0: ndarray[complex]

The systems drift Hamiltonian as a dim x dim matrix.

See also

Hs

property Hs: ndarray[complex]

An array of the system’s control Hamiltonians with shape (n_ctrl, dim, dim).

See also

H0

property J_max: float

The maximum value of the exchange coupling \(J\)

property J_min: float

The minimum value of the exchange coupling \(J\)

property dim: int

The dimension of states in the quantum system.

See also

state_shape

property evolver: UnitaryEvolver

The integrator used for time evolutions of the system.

Note

The evolver can take a while to initialise and so is not initialised until evolver is is first used or when initialise_evolver() is called. Using evolver before calling initialise_evolver() initialises the evolver with the default parameters of initialise_evolver().

property ferromagnetic: bool

Whether the exchange coupling is ferromagnetic or antiferromagnetic

property hilbert_space: HilbertSpace

The Hilbert space of the system

property max_drive_strength: float

The maximum drive strength that can be applied to the system

property n_ctrl: int

The number of control Hamiltonians.

property state_shape: tuple[int]

The shape of the states in the system.

See also

dim

property using_graph: bool

Whether to use TensorFlow graphs during computation. Using a TensorFlow graph will increase the speed of computation. However, you have to be careful that function parameters have not been baked into the graph leading to unexpected behaviour.

property zeeman_splittings: ndarray[complex]

The Zeeman splittings of the spins