quadlib 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. quadlib/__init__.py +39 -0
  2. quadlib/basis/__init__.py +60 -0
  3. quadlib/basis/boson.py +61 -0
  4. quadlib/basis/discrete.py +71 -0
  5. quadlib/basis/expdvr.py +80 -0
  6. quadlib/basis/hodvr.py +51 -0
  7. quadlib/basis/jbasis.py +87 -0
  8. quadlib/basis/sindvr.py +97 -0
  9. quadlib/basis/spin.py +64 -0
  10. quadlib/cli/__init__.py +0 -0
  11. quadlib/cli/qlb_offset_oper_dof.py +34 -0
  12. quadlib/experimental/__init__.py +17 -0
  13. quadlib/experimental/cpd_eom.py +95 -0
  14. quadlib/experimental/mctdh.py +32 -0
  15. quadlib/experimental/node.py +259 -0
  16. quadlib/experimental/pdof.py +9 -0
  17. quadlib/experimental/tree.py +138 -0
  18. quadlib/hdmctdh/__init__.py +0 -0
  19. quadlib/hdmctdh/rundirectory.py +74 -0
  20. quadlib/legacy/__init__.py +14 -0
  21. quadlib/legacy/math.py +1337 -0
  22. quadlib/operator/sop.py +250 -0
  23. quadlib/propagation/__init__.py +17 -0
  24. quadlib/propagation/cpd.py +35 -0
  25. quadlib/propagation/exact.py +139 -0
  26. quadlib/propagation/integrators.py +108 -0
  27. quadlib/tools/__init__.py +0 -0
  28. quadlib/tools/bath.py +84 -0
  29. quadlib/tools/constants.py +289 -0
  30. quadlib/tools/iterators.py +276 -0
  31. quadlib/tools/linalg.py +215 -0
  32. quadlib/tools/pulses.py +187 -0
  33. quadlib/tools/spectra.py +179 -0
  34. quadlib/tools/tensor.py +227 -0
  35. quadlib/wavefunction/__init__.py +0 -0
  36. quadlib/wavefunction/wfn_cpd.py +266 -0
  37. quadlib/wavefunction/wfn_exact.py +147 -0
  38. quadlib-0.1.0.dist-info/METADATA +135 -0
  39. quadlib-0.1.0.dist-info/RECORD +43 -0
  40. quadlib-0.1.0.dist-info/WHEEL +5 -0
  41. quadlib-0.1.0.dist-info/entry_points.txt +2 -0
  42. quadlib-0.1.0.dist-info/licenses/LICENSE +19 -0
  43. quadlib-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,95 @@
1
+ # QuaDLib: QUAntum Dynamics LIBrary for research and methods development
2
+ # Copyright (C) 2018-2026, The Theoretical Chemistry Group, Heidelberg University
3
+ #
4
+ # This software is licensed under the MIT License (https://spdx.org/licenses/MIT.html)
5
+
6
+ """
7
+ Coupled equations of motion for the CPD wavefunction (A-vector and SPFs).
8
+
9
+ KNOWN INCORRECT, kept as found for later work. The Dirac-Frenkel
10
+ equations for Psi = sum_j A_j prod_d phi_{d,j}, with unconstrained,
11
+ non-orthogonal SPFs, read
12
+
13
+ S dA/dt = -i H A - T A, T_ij = <Phi_i|dPhi_j/dt>
14
+ rho^(d) dphi_d/dt = -i sum_k MF_k^(d) h_k^(d) phi_d - tau^(d) phi_d
15
+
16
+ where T and tau^(d) are sums of terms with one derivative overlap
17
+ each. Compared with them:
18
+
19
+ - T_matrix and tau_matrix take the product of all derivative overlaps
20
+ instead of a sum of terms with one derivative factor each;
21
+ - rhs_A_coupled and rhs_spfs are consistent with those equations only
22
+ if T and tau carry a factor i, which they do not;
23
+ - no gauge condition is imposed and the equations are implicit
24
+ (T and tau need the derivatives being solved for).
25
+
26
+ The mean-field term (mfield_hole and its use in rhs_spfs) does agree
27
+ with the derivation.
28
+
29
+ All functions take the wavefunction ``wfn`` (a ``quadlib.WfnCPD`` with
30
+ its ``hamiltonian`` set) and, where needed, ``dtpsi``: a list with the
31
+ same layout as ``wfn.psi`` holding the time derivatives (SPF matrices
32
+ first, one SPF per row, A-vector last). ``empty_dtpsi`` builds one.
33
+ """
34
+
35
+ import numpy as np
36
+
37
+ import quadlib.tools.linalg as linalg
38
+ from quadlib.wavefunction.wfn_cpd import cpd_overlap_prod, cpd_overlap_tensor, empty_cpd
39
+
40
+
41
+ def empty_dtpsi(wfn):
42
+ return empty_cpd(wfn.pdim, wfn.rank, datatype=complex)
43
+
44
+
45
+ def T_matrix(wfn, dtpsi):
46
+ """T -> configuration overlap matrix, rhs time derivative (see module notes)"""
47
+ ovl_dt = cpd_overlap_tensor(wfn.psi, dtpsi)
48
+ return cpd_overlap_prod(ovl_dt, hole=-1)
49
+
50
+
51
+ def tau_matrix(wfn, dof, dtpsi):
52
+ """tau -> density matrix, rhs time derivative (see module notes)"""
53
+ ovl_dt = cpd_overlap_tensor(wfn.psi, dtpsi)
54
+ return cpd_overlap_prod(ovl_dt, hole=dof)
55
+
56
+
57
+ def mfield_hole(wfn, dof):
58
+ """
59
+ Compute the meanfield-only (the meanfield hole) of the mean field operator
60
+
61
+ dof -- degree of freedom, counting from 0
62
+
63
+ The coefficient of each operator term is included, so that the
64
+ mean field is complete whichever DOF is left out.
65
+ """
66
+ wfn._update_hterms()
67
+ t = np.ones(wfn._hterms.shape, bool)
68
+ t[:, dof, :] = False
69
+ mf = np.prod(wfn._hterms, axis=1, where=t)
70
+ mf = np.einsum('k,kij->kij', wfn._coeff(), mf)
71
+ mf = np.einsum('ij,kij->kij', wfn.A_density(), mf)
72
+ return mf
73
+
74
+
75
+ def rhs_A_coupled(wfn, dtpsi):
76
+ """A-vector RHS including the T term (see module notes)"""
77
+ S1 = linalg.invert_reg(wfn.S_matrix())[0]
78
+ T = T_matrix(wfn, dtpsi)
79
+ H = wfn.H_matrix()
80
+ dtA = H.sum(axis=0) @ wfn.psi[-1]
81
+ dtA = dtA - T @ wfn.psi[-1]
82
+ return -1.0j * S1 @ dtA
83
+
84
+
85
+ def rhs_spfs(wfn, dof, dtpsi):
86
+ """SPF RHS for degree of freedom dof (see module notes)"""
87
+ rho1 = linalg.invert_reg(wfn.rho_matrix(dof))[0]
88
+ tau = tau_matrix(wfn, dof, dtpsi)
89
+ mfhole = mfield_hole(wfn, dof)
90
+ dtspf = np.zeros_like(wfn.psi[dof])
91
+ for k in range(wfn.hamiltonian.rank): # operator terms loop
92
+ tmp = wfn.hamiltonian.multiply_term(k, dof, wfn.psi[dof])
93
+ dtspf = dtspf + mfhole[k] @ tmp
94
+ dtspf = dtspf - tau @ wfn.psi[dof]
95
+ return -1.0j * (rho1 @ dtspf)
@@ -0,0 +1,32 @@
1
+ # QuaDLib: QUAntum Dynamics LIBrary for research and methods development
2
+ # Copyright (C) 2018-2026, The Theoretical Chemistry Group, Heidelberg University
3
+ #
4
+ # This software is licensed under the MIT License (https://spdx.org/licenses/MIT.html)
5
+
6
+ """
7
+ Placeholders for the (ML-)MCTDH equations of motion on a Tree.
8
+
9
+ Not implemented. They mark where the tree EOMs will go; once they work
10
+ they move out of experimental into an actual WfnMCTDH.
11
+ """
12
+
13
+
14
+ def rhsEOM(wfn, oper, **opts):
15
+ """Right-hand side of the MCTDH equations of motion."""
16
+ raise NotImplementedError('MCTDH equations of motion are not implemented yet')
17
+
18
+
19
+ def stepEOM(wfn, oper, **opts):
20
+ raise NotImplementedError('MCTDH equations of motion are not implemented yet')
21
+
22
+
23
+ def htermsEOM(wfn, oper, **opts):
24
+ raise NotImplementedError('MCTDH equations of motion are not implemented yet')
25
+
26
+
27
+ def mfieldsEOM(wfn, oper, **opts):
28
+ raise NotImplementedError('MCTDH equations of motion are not implemented yet')
29
+
30
+
31
+ def dmatEOM(wfn, oper, **opts):
32
+ raise NotImplementedError('MCTDH equations of motion are not implemented yet')
@@ -0,0 +1,259 @@
1
+ import uuid
2
+
3
+ import numpy as np
4
+
5
+ import quadlib.tools.linalg as linalg
6
+
7
+
8
+ class Node:
9
+ def __init__(self, **opts):
10
+ self.uid = uuid.uuid4().hex # unique ID string for the node
11
+ self.tag = opts.get('tag', self.uid) # human readable tagname for the node
12
+ self.links = []
13
+ self.index_at_link = [] # index of this bond in the corresponding linked node
14
+ #self.shape = np.array([], int)
15
+ self.shape = []
16
+ self.parent_index = -1
17
+ self.tensor = None
18
+ self.is_pdof = False
19
+ self.is_visited = False # needed for some traversing algorithms
20
+
21
+
22
+ @property
23
+ def dim(self):
24
+ """
25
+ Return node dimensionality (number of bonds)
26
+ """
27
+ return len(self.shape)
28
+
29
+
30
+ @property
31
+ def len(self, **opts):
32
+ """
33
+ Return node length, i.e. product over dimensions = number of coefficients
34
+ """
35
+ return np.prod(self.shape)
36
+
37
+
38
+ def allocate(self, **opts):
39
+ dtype = opts.get('dtype', complex)
40
+ order = opts.get('order', 'C')
41
+ self.tensor = np.zeros(self.shape, dtype, order=order)
42
+
43
+
44
+ def init_tensor_orthonormal_rand(self, ortho_index = 'last', **opts):
45
+ """
46
+ Initialize the tensor as a random orthogonal matrix w.r.t. index ortho_index
47
+ """
48
+ if ortho_index == 'last':
49
+ dim1 = self.len//self.shape[-1]
50
+ dim2 = self.shape[-1]
51
+ if dim2 > dim1: raise ValueError('init_tensor_orthonormal_rand: error dim2 > dim1')
52
+ Q = linalg.random_orthogonal_matrix(dim1, dim2)
53
+ self.tensor[:] = Q.reshape(self.tensor.shape)
54
+ elif ortho_index == 'first':
55
+ dim1 = self.len//self.shape[0]
56
+ dim2 = self.shape[0]
57
+ if dim2 > dim1: raise ValueError('init_tensor_orthonormal_rand: error dim2 > dim1')
58
+ Q = linalg.random_orthogonal_matrix(dim1, dim2)
59
+ Q = Q.T
60
+ self.tensor[:] = Q.reshape(self.tensor.shape)
61
+
62
+ def init_tensor_normal_rand(self):
63
+ """
64
+ Initialize the tensor with random numbers and normalize
65
+
66
+ (useful e.g. for top A-vector)
67
+ """
68
+ t = np.random.random_sample(self.shape)
69
+ tf = t.flatten()
70
+ N = np.sqrt(np.conj(tf) @ tf)
71
+ self.tensor = t / N
72
+
73
+
74
+ def reset_initial(self, **opts):
75
+ """
76
+ (Re)set the tensor used for getting the autocorrelation
77
+ """
78
+ self.tensor0 = self.tensor.copy()
79
+
80
+
81
+ def link_node(self, nodeB, dim=0):
82
+ """
83
+ Link two nodes.
84
+ """
85
+ nodeA = self
86
+ if nodeA.uid == nodeB.uid: # avoid 1-node loops
87
+ raise ValueError('Not possible to link node with itself\n')
88
+ if nodeA.is_linked_to(nodeB): # avoid double linking
89
+ raise ValueError('Not possible to link two nodes twice\n')
90
+ nodeA.links.append(nodeB)
91
+ if not nodeB.is_pdof:
92
+ nodeB.links.append(nodeA)
93
+ nodeA.shape.append(dim)
94
+ nodeB.shape.append(dim)
95
+ nodeA.index_at_link.append(nodeB.links.index(nodeA))
96
+ nodeB.index_at_link.append(nodeA.links.index(nodeB))
97
+ else:
98
+ nodeA.shape.append(nodeB.dim)
99
+ nodeA.index_at_link.append(0)
100
+
101
+
102
+ def transpose_index(self, iold: int, inew: int):
103
+ """
104
+ Transpose index at position iold to position inew
105
+
106
+ If the node tensor is present the tensor indices are transposed accordingly
107
+ """
108
+ # Transpose pointers to nodes in the self.links list
109
+ trans_node = self.links.pop(iold)
110
+ self.links.insert(inew, trans_node)
111
+ # Transpose entries with bond dimensions in the self.shape list
112
+ trans_shape = self.shape.pop(iold)
113
+ self.shape.insert(inew, trans_shape)
114
+ # Transpose tensor indices
115
+ if self.tensor is not None:
116
+ # Create list with index positions [0, 1, 2,... ,self.dim()]
117
+ indices = list(range(self.dim))
118
+ trans_indx = indices.pop(iold)
119
+ indices.insert(inew, trans_indx)
120
+ self.tensor = self.tensor.transpose(indices)
121
+ # Update index of this node at linked nodes
122
+ for lb, nodeB in enumerate(self.links):
123
+ if not nodeB.is_pdof:
124
+ la = nodeB.links.index(self)
125
+ nodeB.index_at_link[la] = lb
126
+
127
+
128
+
129
+ def has_loops(self):
130
+ """
131
+ Return True if the graph under this node structure contains loops
132
+ """
133
+ pass
134
+
135
+
136
+ def is_linked_to(self, nodeB):
137
+ for lnode in self.links:
138
+ if lnode.uid == nodeB.uid:
139
+ return True
140
+ return False
141
+
142
+
143
+ def list_links(self, tags = False, exclude_pdof = False):
144
+ """
145
+ Lists all connections linked to nodeA, both nodes and primitives
146
+
147
+ Returns
148
+ list: The list is empty if the node has no links
149
+ """
150
+ lconnections = self.links[:]
151
+ if exclude_pdof:
152
+ lnodes = []
153
+ for l in lconnections:
154
+ if not l.is_pdof:
155
+ lnodes.append(l)
156
+ lconnections = lnodes
157
+ if tags:
158
+ return _list_tags(lconnections)
159
+ else:
160
+ return lconnections
161
+
162
+
163
+ def return_link_index(self, other):
164
+ """
165
+ Return the index of node other @ node self
166
+ """
167
+ return self.links.index(other)
168
+
169
+
170
+ def is_primitive(self):
171
+ """
172
+ Return True if a node is primitive
173
+
174
+ Primitive nodes have ONE link to a non-primitive node
175
+ """
176
+ p = self.count_pdof()
177
+ n = len(self.shape) - p
178
+ if p >= 1 and n == 1:
179
+ return True
180
+ else:
181
+ return False
182
+
183
+
184
+ def is_mixed(self):
185
+ """
186
+ Return True if a node is of mixed type
187
+
188
+ A mixed node has primitive DOFs and more than one link to non-primitive nodes
189
+ """
190
+ p = self.count_pdof()
191
+ n = len(self.shape) - p
192
+ if p >= 1 and n > 1:
193
+ return True
194
+ else:
195
+ return False
196
+
197
+
198
+ def is_pure(self):
199
+ """
200
+ Return True if a node is of pure type
201
+
202
+ A pure node no primitive DOFs
203
+ """
204
+ p = self.count_pdof()
205
+ n = len(self.shape) - p
206
+ if p == 0 and n >= 1:
207
+ return True
208
+ else:
209
+ return False
210
+
211
+
212
+ def count_pdof(self):
213
+ count = 0
214
+ for child in self.list_links():
215
+ if child.is_pdof:
216
+ count += 1
217
+ return count
218
+
219
+
220
+ def sanity_check(self):
221
+ print('------------- Check Node ' + self.tag + ' ------------')
222
+ i = 0
223
+ for nodeB in self.links:
224
+ if nodeB.is_pdof:
225
+ continue
226
+ if nodeB.shape[nodeB.links.index(self)] == self.shape[self.links.index(nodeB)]:
227
+ tagA = self.tag
228
+ tagB = nodeB.tag
229
+ iB_at_A = str(self.links.index(nodeB))
230
+ iA_at_B = str(nodeB.links.index(self))
231
+ iA_at_B_2 = str(self.index_at_link[i])
232
+ m = str(nodeB.shape[nodeB.links.index(self)])
233
+ print(' ... ' + tagA + ' ' + iB_at_A + ' <-> ' + tagB + ' ' + iA_at_B + ' ' + iA_at_B_2 + ' => ' + m)
234
+ else:
235
+ raise ValueError(nodeB.tag + ' <-> ' + self.tag + ' broken')
236
+
237
+
238
+ def apply_matrix_to_index(self, matrix, indx):
239
+ pass
240
+
241
+ def apply_unitary_to_link(self, U, indx):
242
+ """
243
+ [NodeA ------ U] ------ [U^-1 --------- NodeB]
244
+
245
+ The convention is to apply U to node A (self) and U^-1 to node B
246
+ """
247
+ self.apply_matrix_to_index(U, indx)
248
+ UT = np.conj(U.T)
249
+ nodeB = self.links[indx]
250
+ iA_at_B = nodeB.links.index(self)
251
+ nodeB.apply_matrix_to_index(UT, iA_at_B)
252
+
253
+ def _list_tags(nodeList):
254
+ lid = []
255
+ for node in nodeList:
256
+ lid.append(node.tag)
257
+ return lid
258
+
259
+
@@ -0,0 +1,9 @@
1
+ import uuid
2
+
3
+
4
+ class Pdof:
5
+ def __init__(self, **opts):
6
+ self.uid = uuid.uuid4().hex
7
+ self.tag = opts.get('tag', self.uid)
8
+ self.dim = opts.get('dim', 0)
9
+ self.is_pdof = True
@@ -0,0 +1,138 @@
1
+ import uuid
2
+
3
+
4
+ class Tree:
5
+ def __init__(self, root = None, **opts):
6
+ self.uid = uuid.uuid4().hex
7
+ self.root = root
8
+ self.set_root(root)
9
+
10
+
11
+ @property
12
+ def len(self, **opts):
13
+ nCoeff = 0
14
+ lnodes = self.traverse_preorder(self.root)
15
+ for n in lnodes:
16
+ nNodeCoeff = n.len
17
+ nCoeff += nNodeCoeff
18
+ return nCoeff
19
+
20
+
21
+ def set_root(self, nroot, **opts):
22
+ self.root = nroot
23
+ self.root.is_visited = True
24
+ self.root.parent_index = -1
25
+ L = self.traverse_preorder() # pre-order traversal starting from the new root
26
+ for node in L[1:]:
27
+ node.is_visited = True
28
+ for j, lnode in enumerate(node.links):
29
+ if lnode.is_pdof is True:
30
+ continue
31
+ if lnode.is_visited is True:
32
+ node.parent_index = j
33
+ break
34
+ # clean-up
35
+ for node in L:
36
+ node.is_visited = False
37
+ # Bring node links to canonical order (within each node parent is last self.links entry)
38
+ self.transpose_parent_index(**opts)
39
+
40
+
41
+ def transpose_parent_index(self, **opts):
42
+ """
43
+ Transpose parent index to last (first) position
44
+
45
+ For each node, the link to the parent is moved to either the first or the last (default) position.
46
+ The local tensor with the node's data is transposed accordingly.
47
+ """
48
+ index_order = opts.get('index_order', 'plast')
49
+ nodelist = self.traverse_preorder()
50
+ for n in nodelist[1:]:
51
+ iparent = n.parent_index
52
+ ndim = n.dim
53
+ if index_order == 'plast':
54
+ n.transpose_index(iparent, ndim - 1)
55
+ elif index_order == 'pfirst':
56
+ n.transpose_index(iparent, 0)
57
+
58
+
59
+ def traverse_preorder(self,
60
+ next_node = None,
61
+ tags = False,
62
+ lnodes = None,
63
+ discovered = None,
64
+ isFirstCall = True):
65
+ """
66
+ Traverse the tree in depth-first-search, preorder
67
+
68
+ - rootNode: node where the tree traversal is started
69
+ """
70
+ if isFirstCall:
71
+ lnodes = []
72
+ discovered = []
73
+ next_node = self.root
74
+ lnodes.append(next_node)
75
+ discovered.append(next_node)
76
+ for node in next_node.list_links(exclude_pdof = True):
77
+ if discovered.count(node) == 0:
78
+ self.traverse_preorder(node,
79
+ tags = False,
80
+ lnodes = lnodes,
81
+ discovered = discovered,
82
+ isFirstCall = False)
83
+ if tags:
84
+ return _list_tags(lnodes)
85
+ else:
86
+ return lnodes
87
+
88
+
89
+ def traverse_postorder(self,
90
+ next_node = None,
91
+ tags = False,
92
+ lnodes = None,
93
+ discovered = None,
94
+ isFirstCall = True):
95
+ """
96
+ Traverse the tree in depth-first-search, postorder
97
+ """
98
+ if isFirstCall:
99
+ lnodes = []
100
+ discovered = []
101
+ next_node = self.root
102
+ discovered.append(next_node)
103
+ for node in next_node.list_links(exclude_pdof = True):
104
+ if discovered.count(node) == 0:
105
+ self.traverse_postorder(node,
106
+ tags = False,
107
+ lnodes = lnodes,
108
+ discovered = discovered,
109
+ isFirstCall = False)
110
+ lnodes.append(next_node)
111
+ if tags:
112
+ return _list_tags(lnodes)
113
+ else:
114
+ return lnodes
115
+
116
+
117
+ def print_tree_list(self):
118
+ for n in self.traverse_preorder():
119
+ t = n.tag
120
+ l = n.list_links(tags = True)
121
+ if n.parent_index > -1:
122
+ l.pop(n.parent_index)
123
+ s = t + ' -> ' + str(l)
124
+ print(s)
125
+
126
+
127
+ def sanity_check(self):
128
+ lnodes = self.traverse_preorder()
129
+ for node in lnodes:
130
+ node.sanity_check()
131
+
132
+
133
+ def _list_tags(nodeList):
134
+ lid = []
135
+ for node in nodeList:
136
+ lid.append(node.tag)
137
+ return lid
138
+
File without changes
@@ -0,0 +1,74 @@
1
+
2
+ # QuaDLib: QUAntum Dynamics LIBrary for research and methods development
3
+ # Copyright (C) 2018-2026, The Theoretical Chemistry Group, Heidelberg University
4
+ #
5
+ # This software is licensed under the MIT License (https://spdx.org/licenses/MIT.html)
6
+
7
+
8
+ # This module defines classes and functions to open and interact with
9
+ # an MCTDH run directory
10
+
11
+ from pathlib import Path
12
+
13
+ STANDARDFILES = ['input', 'output', 'check', 'log', 'oper', 'dvr']
14
+
15
+ class Rundirectory:
16
+ """
17
+ Open an MCTDH Run directory for analysis etc.
18
+
19
+ path: str, path (absolute or relative) to run directory
20
+ """
21
+
22
+ def __init__(self, path):
23
+ rundir = Path(path)
24
+ self._messages = []
25
+ if not rundir.is_dir():
26
+ raise ValueError(path + ' is not a directory')
27
+ # We look for standard files to determine that this is an MCTDH run
28
+ for f in STANDARDFILES:
29
+ rf = rundir / f
30
+ if not rf.is_file():
31
+ msg = ' * Warning: ' + str(rf) + ' file not found'
32
+ self._messages.append(msg)
33
+ self.rundir = rundir
34
+ try:
35
+ self.auto = self.load_auto()
36
+ except Exception:
37
+ msg = ' * Error: auto file could not be loaded'
38
+ self._messages.append(msg)
39
+
40
+ @property
41
+ def messages(self):
42
+ return self._messages
43
+
44
+ def load_auto(self):
45
+ """
46
+ Load auto file as numpy array and return
47
+
48
+ Returns numpy array:
49
+ auto[0]: time steps
50
+ auto[1]: real part
51
+ auto[2]: imag part
52
+ auto[3]: absolute
53
+ """
54
+ import numpy as np
55
+ path_auto = self.rundir / 'auto'
56
+ if not path_auto.is_file():
57
+ msg = 'auto file not found in ' + str(self.rundir)
58
+ raise ValueError(msg)
59
+ self._messages.append(msg)
60
+ auto = np.loadtxt(path_auto).T
61
+ return auto
62
+
63
+ def plauto(self, mode = 'survival'):
64
+ import matplotlib.pyplot as plt
65
+ a = self.auto
66
+ if mode == 'abs':
67
+ plt.plot(a[0], a[3])
68
+ elif mode == 'real':
69
+ plt.plot(a[0], a[1])
70
+ elif mode == 'imag':
71
+ plt.plot(a[0], a[2])
72
+ elif mode == 'survival':
73
+ plt.plot(a[0], a[3]**2)
74
+ plt.show()
@@ -0,0 +1,14 @@
1
+ # QuaDLib: QUAntum Dynamics LIBrary for research and methods development
2
+ # Copyright (C) 2018-2026, The Theoretical Chemistry Group, Heidelberg University
3
+ #
4
+ # This software is licensed under the MIT License (https://spdx.org/licenses/MIT.html)
5
+
6
+ """
7
+ Older utilities kept for reference and for existing scripts.
8
+
9
+ Not imported by ``quadlib/__init__.py``, not used by the library, and
10
+ not tested; import explicitly, e.g. ``import quadlib.legacy.math``.
11
+ ``quadlib.legacy.math`` is the former ``quadlib.tools.math`` minus the
12
+ functions the library uses, which now live in ``quadlib.tools.linalg``
13
+ and ``quadlib.tools.spectra``.
14
+ """