rydiqule.sensor_solution.Solution

class rydiqule.sensor_solution.Solution[source]

Bases: object

Results object that contains information from a solve.

Methods implement a number of standard analyses on the result based on the density matrix observable formalism and Maxwell’s equations for a plane wave in an optically-thin polarizable medium.

__init__()

Methods

__init__()

copy()

coupling_coefficient_matrix(coupling)

Matrix of coefficients representing coupling strength for a particular coupling.

coupling_coefficient_observable([coupling])

Get the observable associated with the output of coupling_coefficient_matrix().

coupling_rabi(coupling)

Rabi frequency, in Mrad/s, of a particular coupling or coupling group.

deepcopy()

get_OD()

Calculates the optical depth from the solution.

get_observable(matrix)

Returns the trace of a matrix product of the density matrix with a provided matrix.

get_phase_shift()

Extract the phase shift from a solution.

get_solution_element(idx)

Return a slice of an n_dimensional matrix of solutions of shape (...,n^2-1), where n is the basis size of the quantum system.

get_susceptibility()

Return the atomic susceptibility on the probe transition.

get_transmission_coef()

Extract the transmission term from a solution.

rho_ij(i, j)

Gets the full complex density matrix element corresponding to a given pair of indices.

Attributes

beam_area

Cross-sectional area of the probing beam, in square meters.

cell_length

Optical path length of the medium, in meters.

complex_rho

Density-matrix solutions in complex basis.

eta

Eta constant from the Cell.

kappa

Kappa constant from the Cell.

probe_freq

Probing transition frequency, in rad/s.

probe_rabi

Base laser rabi frequency of the probing transition of the Sensor used in a solve.

probe_tuple

Coupling edge corresponding to probing field.

states

Return a list of all states in the Sensor used to produce solution.

couplings

Copy of the Sensor.couplings graph.

axis_labels

Labels for the axes of scanned parameters.

axis_values

Value corresponding to each axis.

rq_version

Version of rydiqule that created the Solution.

dm_basis

The list of density matrix elements in the order they appear in the solution.

variable_parameters

Output of the Sensor.variable_parameters() method for the Sensor used in a solve.

doppler_classes

Doppler classes used to perform the doppler average.

rho

Solutions returned by the solver.

t

Times the solution is returned at, when using the time solver.

init_cond

Initial conditions, when using the time solver.

_beam_area: float | None

Cross-sectional area of the probing beam, in square meters. Not generally defined when using a Sensor.

_cell_length: float | None

Optical path length of the medium, in meters. Not generally defined when using a Sensor.

_eta: float | None

Eta constant from the Cell. Not generally defined when using a Sensor.

Type:

float, optional

_kappa: float | None

Kappa constant from the Cell. Not generally defined when using a Sensor.

Type:

float, optional

_probe_freq: float | None

Probing transition frequency, in rad/s. Not generally defined when using a Sensor.

_probe_tuple: Tuple[int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...], int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...]] | None

Coupling edge corresponding to probing field. Defined as the first added coupling when using a Sensor.

axis_labels: list[str]

Labels for the axes of scanned parameters. If doppler averaging but not summing, doppler dimensions are prepended.

Type:

list of str

axis_values: list

Value corresponding to each axis. If doppler averaging but not summing, doppler classes in internal units are added.

Type:

list

property beam_area: float

Cross-sectional area of the probing beam, in square meters. Not generally defined when using a Sensor.

property cell_length: float

Optical path length of the medium, in meters. Not generally defined when using a Sensor.

property complex_rho: ndarray

Density-matrix solutions in complex basis.

This function calls sensor_utils.convert_dm_to_complex() on rho.

Returns:

Complex density matrix solutions, with ground state present.

Return type:

numpy.ndarray

copy()[source]
coupling_coefficient_matrix(coupling: Tuple[int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...], int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...]]) ndarray[source]

Matrix of coefficients representing coupling strength for a particular coupling.

Returns:

Adjacency matrix of couplings attributes generated from the "coherent_cc" parameter on the coupling edge.

Return type:

np.ndarray

Examples

>>> g = rq.ground_state(5, splitting="fs")
>>> e = rq.D1_excited(5, splitting="fs")
>>> my_cell = rq.Cell('Rb85', [g, e])
>>> my_cell.add_coupling(states=(g, e), rabi_frequency=1, detuning=1, label="probe")
>>> sol = rq.solve_steady_state(my_cell)
>>> for e in sol.couplings.edges(data="coherent_cc"):
...     print(*e)
(5, 0, 0.5, m_j=-0.5) (5, 0, 0.5, m_j=-0.5) None
(5, 0, 0.5, m_j=-0.5) (5, 0, 0.5, m_j=0.5) None
(5, 0, 0.5, m_j=-0.5) (5, 1, 0.5, m_j=-0.5) -0.816496580927726
(5, 0, 0.5, m_j=0.5) (5, 0, 0.5, m_j=-0.5) None
(5, 0, 0.5, m_j=0.5) (5, 0, 0.5, m_j=0.5) None
(5, 0, 0.5, m_j=0.5) (5, 1, 0.5, m_j=0.5) 0.816496580927726
(5, 1, 0.5, m_j=-0.5) (5, 0, 0.5, m_j=-0.5) None
(5, 1, 0.5, m_j=-0.5) (5, 0, 0.5, m_j=0.5) None
(5, 1, 0.5, m_j=0.5) (5, 0, 0.5, m_j=-0.5) None
(5, 1, 0.5, m_j=0.5) (5, 0, 0.5, m_j=0.5) None
>>> print(sol.coupling_coefficient_matrix(sol.probe_tuple))
[[ 0.        0.        0.        0.      ]
 [ 0.        0.        0.        0.      ]
 [-0.816497  0.        0.        0.      ]
 [ 0.        0.816497  0.        0.      ]]
coupling_coefficient_observable(coupling: Tuple[int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...], int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...]] | None = None)[source]

Get the observable associated with the output of coupling_coefficient_matrix().

Calls the get_observable() function with the output of coupling_coefficient_matrix(), with rows and columns limited those defined by the probe coupling. If coupling is None, uses the tuple defined by probe_tuple.

This function is designed to get observable values associated with the probe laser in an experiment, such as transmission and absorption coefficients.

Parameters:

coupling (tuple of int or string, optional) – The 2-length tuple of state specifications to use in the observable calculation. Each state can be either an int or a regex string corresponding to a group of states. If None, uses the probe_tuple attribute as defined in the Sensor used to produce the solution. Defaults to None.

Returns:

The observable or array of observables corresponding to the coupling coefficients.

Return type:

np.ndarray

coupling_rabi(coupling: Tuple[int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...], int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...]]) complex | ndarray[source]

Rabi frequency, in Mrad/s, of a particular coupling or coupling group.

Serves as a more general way of fetching a rabi frequency from the graph that supports couplings between manifolds. Throws an error if any of the couplings in the group specified by coupling do not have matching rabi_frequency, either in values or shape.

Parameters:

coupling (tuple of states) – Pair of states or state manifolds specifying a coupling or coupling group respectively. If a group, all couplings in the group must have matching values and shapes for the rabi_frequency. This is most easily accomplished by adding the coupling to the group using add_coupling() with manifolds, but zip_parameters() can also be used.

Returns:

The rabi_frequency of the coupling or of all couplings in the group specified by coupling.

Return type:

float or np.ndarray

Raises:

ValueError – If coupling is a group with mismatched rabi frequencies, either in shape or value, or coupling has no defined rabi frequencies.

Examples

The basic functionality for couplings between single states mimics the functionality of just accessing the rabi_frequency attribute from the graph.

>>> my_sensor = rq.Sensor(2)
>>> my_sensor.add_coupling((0,1), rabi_frequency=1, detuning=1)
>>> my_sensor.add_decoherence((1,0), 0.1)
>>> sol = rq.solve_steady_state(my_sensor)
>>> print(sol.couplings.edges[0,1]["rabi_frequency"])
1
>>> print(sol.coupling_rabi((0,1)))
1

It can also get the rabi_frequency for an entire coupling group. The rabi_frequency is treated as a laser property. Coupling coefficients as individual coupling properties, and so are not accounted for.

>>> g = (0,0)
>>> e = (1, [-1,1])
>>> cc = {
...     (g, (1,-1)): 0.5,
...     (g, (1,1)): 0.5
... }
>>> my_sensor = rq.Sensor([g,e])
>>> my_sensor.add_coupling((g,e), rabi_frequency=5, detuning=1, label="probe")
>>> my_sensor.add_decoherence((e,g), 0.1, label="foo")
>>> sol = rq.solve_steady_state(my_sensor)
>>> print(sol.couplings.edges.data("rabi_frequency"))
[((0, 0), (1, -1), 5), ((0, 0), (1, 1), 5), ((1, -1), (0, 0), None), ((1, 1), (0, 0), None)]
>>> print(sol.coupling_rabi((g,e)))
5
couplings: DiGraph

Copy of the Sensor.couplings graph.

Type:

dict

deepcopy()[source]
dm_basis: ndarray

The list of density matrix elements in the order they appear in the solution. See Sensor.basis() for details.

Type:

list of str

doppler_classes: ndarray | None

Doppler classes used to perform the doppler average. Will be None if doppler averaging was not used.

Type:

numpy.ndarray, optional

property eta: float

Eta constant from the Cell. Not generally defined when using a Sensor.

get_OD() float | ndarray[source]

Calculates the optical depth from the solution. This equation comes from Steck’s Quantum Optics Notes Eq. 6.74.

Assumes the optically-thin approximation is valid. If a calculated OD for a solution exceeds 1, this approximation is likely invalid.

Returns:

OD – Optical depth of the sample

Return type:

float or numpy.ndarray

Warns:

UserWarning – If any OD exceeds 1, which indicates the optically-thin approximation is likely invalid.

Examples

>>> [g, e] = rq.D2_states('Rb85')
>>> c = rq.Cell('Rb85', [g, e], cell_length =  0.0001)
>>> c.add_coupling(states=(g, e), rabi_frequency=1, detuning=1)
>>> sols = rq.solve_steady_state(c)
>>> print(sols.rho.shape)
(3,)
>>> OD = sols.get_OD()
>>> print(OD)
0.2964844
get_observable(matrix: ndarray)[source]

Returns the trace of a matrix product of the density matrix with a provided matrix. This is the standard definition of an measurement of observable \(A\) taken on a density matrix \(\rho\) given by \(tr(\rho A)\). Note that this function first converts the density matrix into the standard complex basis rather than rydiqule’s real basis, leading to potential round-off errors for very small density matrix elements.

The provided observable wil respect rydiqule stacking convention, and the labels for each axis can be recovered via the axis_labels attribute.

Parameters:

matrix (np.ndarray) – The matrix representing the observable to be computed.

Returns:

Array of observables using rydiqule’s stacking convention.

Return type:

np.ndarray

get_phase_shift() float | ndarray[source]

Extract the phase shift from a solution.

Assumes the optically-thin approximation is valid.

Returns:

Probe phase in radians.

Return type:

float or numpy.ndarray

Examples

>>> [g, e] = rq.D2_states('Rb85')
>>> c = rq.Cell('Rb85', [g, e], cell_length =  0.00001)
>>> c.add_coupling(states=(g, e), rabi_frequency=1, detuning=1)
>>> sols = rq.solve_steady_state(c)
>>> print(sols.rho.shape)
(3,)
>>> print(sols.get_phase_shift())
80.5294887
get_solution_element(idx: int) float | ndarray[source]

Return a slice of an n_dimensional matrix of solutions of shape (…,n^2-1), where n is the basis size of the quantum system.

Parameters:

idx (int) – Solution index to slice.

Returns:

Slice of solutions corresponding to index idx. For example, if sols has shape (…, n^2-1), sol_slice will have shape (…).

Return type:

float or numpy.ndarray

Raises:

RydiquleError – If idx in not within the shape determined by basis size.

Examples

>>> [g, e] = rq.D2_states("Rb85", expand=True)
>>> c = rq.Cell('Rb85', [g, e])
>>> c.add_coupling(states=(g, e), rabi_frequency=1, detuning=1)
>>> sols = rq.solve_steady_state(c)
>>> print(sols.rho.shape)
(3,)
>>> rho_01_im = sols.get_solution_element(0)
>>> print(rho_01_im)
0.00131399
get_susceptibility() complex | ndarray[source]

Return the atomic susceptibility on the probe transition.

Experimental parameters must be defined manually for a Sensor.

Returns:

Susceptibility of the density matrix solution.

Return type:

complex or numpy.ndarray

Examples

>>> [g, e] = rq.D2_states(5, expand=True)
>>> c = rq.Cell('Rb85', [g, e], cell_length = 0.0001)
>>> c.add_coupling(states=(g,e), rabi_frequency=1, detuning=1)
>>> sols = rq.solve_steady_state(c)
>>> print(sols.rho.shape)
(3,)
>>> sus = sols.get_susceptibility()
>>> print(sus)
(1.891136e-05+0.00036817j)
get_transmission_coef() float | ndarray[source]

Extract the transmission term from a solution.

Assumes the optically-thin approximation is valid.

Returns:

Numerical value of the probe absorption in fractional units (P_out/P_in).

Return type:

float or numpy.ndarray

Examples

>>> [g, e] = rq.D2_states('Rb85')
>>> c = rq.Cell('Rb85', [g, e], cell_length =  0.0001)
>>> c.add_coupling(states=(g, e), rabi_frequency=1, detuning=1)
>>> sols = rq.solve_steady_state(c)
>>> print(sols.rho.shape)
(3,)
>>> t = sols.get_transmission_coef()
>>> print(t)
0.743427
init_cond: ndarray

Initial conditions, when using the time solver. Undefined otherwise.

Type:

numpy.ndarray

property kappa: float

Kappa constant from the Cell. Not generally defined when using a Sensor.

property probe_freq: float

Probing transition frequency, in rad/s. Not generally defined when using a Sensor.

property probe_rabi: complex | ndarray

Base laser rabi frequency of the probing transition of the Sensor used in a solve.

The return of this function will be the appropriate shape to be cast using rydiqule’s stacking convention. An error will be thrown if any of the base rabi frequencies of the probe coupling group do not match. (All rabi frequencies should match if added using add_coupling_group and not overwritten)

Returns:

  • The base rabi frequency of the transition defined as the rabi frequency of any of the

  • couplings in the group divided by the coherent_cc on the corresponding edge (default 1).

property probe_tuple: Tuple[int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...], int | str | Tuple[float, ...] | List[int | str | Tuple[float, ...]] | Tuple[float | List[float], ...]]

Coupling edge corresponding to probing field. Defined as the first added coupling when using a Sensor.

rho: ndarray

Solutions returned by the solver.

Type:

numpy.ndarray

rho_ij(i: int | str | Tuple[float, ...], j: int | str | Tuple[float, ...]) complex | ndarray[source]

Gets the full complex density matrix element corresponding to a given pair of indices.

Returns the entire array of density matrix elements corresponding to every combination of parameters defined by the mesh of parameters in the system. The shape of the output array will match the stack_shape of the solution.

Indices can be provided either as integers which number the states, or using state labels (integers, tuples, or strings). For the case of tuples, only individual states can be used, full state specifications are invalid

In the case of a state, it will be mapped to the corresponding integer, then the element itself is fetched using the get_rho_ij() function.

Parameters:
  • i (int, tuple, or string) – density matrix element row, or state label corresponding to row

  • j (int, tuple or string) – density matrix element column, or state label corresponding to column

Returns:

[i,j] complex element(s) of the density matrix

Return type:

complex or numpy.ndarray

Examples

State labels and integer indices can be used interchangeably.

>>> rabis = np.linspace(1, 6, 5)
>>> my_sensor = rq.Sensor(["g","e"])
>>> my_sensor.add_coupling(("g","e"), rabi_frequency=rabis, detuning=1)
>>> my_sensor.add_decoherence(("e","g"), 0.1)
>>> sol = rq.solve_steady_state(my_sensor)
>>> print(sol.rho_ij("g","e"))
[0.3328-0.0166j 0.3184-0.0159j 0.2455-0.0123j 0.1933-0.0097j
 0.1579-0.0079j]
>>> print(sol.rho_ij(0,1))
[0.3328-0.0166j 0.3184-0.0159j 0.2455-0.0123j 0.1933-0.0097j
 0.1579-0.0079j]
rq_version: str

Version of rydiqule that created the Solution.

Type:

str

property states: List[int | str | Tuple[float, ...]]

Return a list of all states in the Sensor used to produce solution.

Returns:

List of all states in the sensor used to produce solution. Order will match order in Sensor.

Return type:

list of states

t: ndarray

Times the solution is returned at, when using the time solver. Undefined otherwise.

Type:

numpy.ndarray

variable_parameters: list

Output of the Sensor.variable_parameters() method for the Sensor used in a solve.