wavepacket 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.
@@ -0,0 +1,94 @@
1
+ import numpy as np
2
+
3
+ import wavepacket as wp
4
+ import wavepacket.typing as wpt
5
+ from .operatorbase import OperatorBase
6
+ from ..grid import Grid
7
+
8
+
9
+ class PlaneWaveFbrOperator(OperatorBase):
10
+ """
11
+ Base class for operators that are diagonal in a plane wave basis.
12
+
13
+ The :py:class:`wavepacket.grid.PlaneWaveDof` describes an expansion of
14
+ the wave function in plane waves. In the corresponding FBR, derivatives
15
+ are diagonal and essentially transform into a multiplication with the
16
+ wave vector / FBR grid.
17
+
18
+ A key difference to a generic FBR operator is that this operator
19
+ does not apply the shift after the FFT, which increases performance.
20
+
21
+ Parameters
22
+ ----------
23
+ grid : wp.grid.Grid
24
+ The grid on which the operator is defined.
25
+ dof_index : int
26
+ The degree of freedom along which the operator is defined.
27
+ generator : wpt.Generator
28
+ A callable that gives the operator value for each FBR point.
29
+
30
+ Raises
31
+ ------
32
+ wp.InvalidValueError
33
+ If the supplied degree of freedom is not a plane wave expansion.
34
+ """
35
+
36
+ def __init__(self, grid: Grid, dof_index: int, generator: wpt.Generator):
37
+ if not isinstance(grid.dofs[dof_index], wp.grid.PlaneWaveDof):
38
+ raise wp.InvalidValueError(
39
+ f"PlaneWaveFbrOperator requires a PlaneWaveDof, but got {grid.dofs[dof_index].__class__}")
40
+
41
+ self._wf_index = dof_index
42
+ self._ket_index = grid.normalize_index(dof_index)
43
+ self._bra_index = self._ket_index + len(grid.dofs)
44
+
45
+ # shifting the data here allows us to skip the fftshift() on the input data in apply*()
46
+ data = generator(grid.dofs[dof_index].fbr_points)
47
+ shifted_data = np.fft.ifftshift(data)
48
+ self._wf_data = grid.broadcast(shifted_data, dof_index)
49
+ self._ket_data = grid.operator_broadcast(shifted_data, dof_index)
50
+ self._bra_data = grid.operator_broadcast(shifted_data, dof_index, is_ket=False)
51
+
52
+ super().__init__(grid)
53
+
54
+ def apply_to_wave_function(self, psi: wpt.ComplexData, t: float) -> wpt.ComplexData:
55
+ psi_fft = np.fft.fft(psi, axis=self._wf_index)
56
+ return np.fft.ifft(psi_fft * self._wf_data, axis=self._wf_index)
57
+
58
+ def apply_from_left(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
59
+ rho_fft = np.fft.fft(rho, axis=self._ket_index)
60
+ return np.fft.ifft(rho_fft * self._ket_data, axis=self._ket_index)
61
+
62
+ def apply_from_right(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
63
+ rho_fft = np.fft.ifft(rho, axis=self._bra_index)
64
+ return np.fft.fft(rho_fft * self._bra_data, axis=self._bra_index)
65
+
66
+
67
+ class CartesianKineticEnergy(PlaneWaveFbrOperator):
68
+ """
69
+ Convenience class that implements the common Cartesian kinetic energy operator.
70
+
71
+ The form of the operator is :math:`-\\frac{1}{2m} \ \\frac{\partial^2}{\partial x^2}`.
72
+ It requires the degree of freedom to be a :py:class:`wavepacket.grid.PlaneWaveDof`.
73
+
74
+ Parameters
75
+ ----------
76
+ grid : wp.grid.Grid
77
+ The grid on which the operator is defined.
78
+ dof_index : inst
79
+ Degree of freedom along which the operator acts
80
+ mass : float
81
+ The mass of the particle.
82
+
83
+ Raises
84
+ ------
85
+ wp.InvalidValueError
86
+ If the mass is not positive, or if the degree of freedom does not describe a
87
+ plane wave expansion.
88
+ """
89
+
90
+ def __init__(self, grid: Grid, dof_index: int, mass: float):
91
+ if mass <= 0:
92
+ raise wp.InvalidValueError(f"Particle mass must be positive, but is {mass}")
93
+
94
+ super().__init__(grid, dof_index, lambda fbr_points: fbr_points ** 2 / (2 * mass))
@@ -0,0 +1,204 @@
1
+ from abc import ABC, abstractmethod
2
+ from collections.abc import Sequence
3
+ import typing
4
+
5
+ import numpy as np
6
+
7
+ import wavepacket as wp
8
+ import wavepacket.typing as wpt
9
+ from ..grid import Grid, State
10
+
11
+
12
+ class OperatorBase(ABC):
13
+ """
14
+ Base class of an operator.
15
+
16
+ An operator can be applied to a wave function or from the left or right to a
17
+ density operator. It is defined on a grid, and can only operate on states on that
18
+ grid.
19
+
20
+ Parameters
21
+ ----------
22
+ grid : wp.grid.Grid
23
+ The grid on which the operator is defined.
24
+ Particular operators may require additional parameters.
25
+
26
+ Attributes
27
+ ----------
28
+ grid
29
+ """
30
+
31
+ def __init__(self, grid: Grid):
32
+ self._grid = grid
33
+
34
+ @property
35
+ def grid(self):
36
+ """
37
+ Returns the grid on which the operator is defined.
38
+ """
39
+ return self._grid
40
+
41
+ def apply(self, state: State, t: typing.Optional[float] = None) -> State:
42
+ """
43
+ Applies the operator onto a wave function or a density operator from the left.
44
+
45
+ This is a convenience function if you just want to apply the operator without
46
+ detailed knowledge of the state.
47
+
48
+ Parameters
49
+ ----------
50
+ state : wp.grid.State
51
+ The state that the operator is applied on.
52
+ t : float, optional
53
+ The time at which the operator is applied.
54
+ The default value `None` raises an exception for time-dependent operators.
55
+
56
+ Returns
57
+ -------
58
+ wp.grid.State
59
+ The result of applying the operator on the state.
60
+
61
+ Raises
62
+ ------
63
+ wp.BadGridError
64
+ If the state's grid does not match the grid of the operator.
65
+ wp.BadStateError
66
+ If the state is neither a wave function nor a density operator.
67
+
68
+ """
69
+ if state.grid != self._grid:
70
+ raise wp.BadGridError("Grid of state does not match grid of operator.")
71
+
72
+ if state.is_wave_function():
73
+ return State(state.grid, self.apply_to_wave_function(state.data, t))
74
+ elif state.is_density_operator():
75
+ return State(state.grid, self.apply_from_left(state.data, t))
76
+ else:
77
+ raise wp.BadStateError("Cannot apply the operator to an invalid state.")
78
+
79
+ def __add__(self, other: typing.Self) -> typing.Self:
80
+ """
81
+ Adds two operators and returns the result as a :py:class:`wavepacket.operator.OperatorSum`.
82
+ """
83
+ return OperatorSum([self, other])
84
+
85
+ @abstractmethod
86
+ def apply_to_wave_function(self, psi: wpt.ComplexData, t: float) -> wpt.ComplexData:
87
+ """
88
+ Applies the operator on a wave function.
89
+
90
+ This function is mainly for Wavepacket-internal use. It ignores most error
91
+ handling (usually done by the wrapping :py:class:`wavepacket.expression.ExpressionBase`),
92
+ and operates directly on coefficients to avoid the creation of temporary states.
93
+
94
+ Parameters
95
+ ----------
96
+ psi : wpt.ComplexData
97
+ The coefficients describing the wave function on which the operator acts.
98
+ t : float
99
+ The time at which the operator should be evaluated.
100
+
101
+ Returns
102
+ -------
103
+ wpt.ComplexData
104
+ The coefficients of the resulting wave function.
105
+ """
106
+ pass
107
+
108
+ @abstractmethod
109
+ def apply_from_left(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
110
+ """
111
+ Applies the operator on a density operator from the left.
112
+
113
+ This function is mainly for Wavepacket-internal use. It ignores most error
114
+ handling (usually done by the wrapping :py:class:`wavepacket.expression.ExpressionBase`),
115
+ and operates directly on coefficients to avoid the creation of temporary states.
116
+
117
+ Parameters
118
+ ----------
119
+ rho : wpt.ComplexData
120
+ The coefficients describing the density operator on which the operator acts.
121
+ t : float
122
+ The time at which the operator should be evaluated.
123
+
124
+ Returns
125
+ -------
126
+ wpt.ComplexData
127
+ The coefficients of the resulting density operator.
128
+ """
129
+ pass
130
+
131
+ @abstractmethod
132
+ def apply_from_right(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
133
+ """
134
+ Applies the operator on a density operator from the right.
135
+
136
+ This function is mainly for Wavepacket-internal use. It ignores most error
137
+ handling (usually done by the wrapping :py:class:`wavepacket.expression.ExpressionBase`),
138
+ and operates directly on coefficients to avoid the creation of temporary states.
139
+
140
+ Parameters
141
+ ----------
142
+ rho : wpt.ComplexData
143
+ The coefficients describing the density operator on which the operator acts.
144
+ t : float
145
+ The time at which the operator should be evaluated.
146
+
147
+ Returns
148
+ -------
149
+ wpt.ComplexData
150
+ The coefficients of the resulting density operator.
151
+ """
152
+ pass
153
+
154
+
155
+ class OperatorSum(OperatorBase):
156
+ """
157
+ An operator that represents the sum of multiple other operators.
158
+
159
+ You do not normally construct this operator directly. It is the result of
160
+ adding two or more operators together. All functionality is simply forwarded
161
+ to the individual operators.
162
+
163
+ Parameters
164
+ ----------
165
+ ops : Sequence[wp.operator.OperatorBase]
166
+ The operators that should be summed up.
167
+
168
+ Raises
169
+ ------
170
+ wp.BadGridError
171
+ If the operators are defined on different grids.
172
+ """
173
+
174
+ def __init__(self, ops: Sequence[OperatorBase]):
175
+ if not ops:
176
+ raise wp.InvalidValueError("OperatorSum needs at least one operator to sum.")
177
+ for op in ops:
178
+ if op.grid != ops[0].grid:
179
+ raise wp.BadGridError("All grids in a sum operator must be equal.")
180
+
181
+ self._ops = ops
182
+ grid = ops[0].grid
183
+ super().__init__(grid)
184
+
185
+ def apply_to_wave_function(self, psi: wpt.ComplexData, t: float) -> wpt.ComplexData:
186
+ result = np.zeros(self.grid.shape, dtype=np.complex128)
187
+ for op in self._ops:
188
+ result += op.apply_to_wave_function(psi, t)
189
+
190
+ return result
191
+
192
+ def apply_from_left(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
193
+ result = np.zeros(self.grid.operator_shape, dtype=np.complex128)
194
+ for op in self._ops:
195
+ result += op.apply_from_left(rho, t)
196
+
197
+ return result
198
+
199
+ def apply_from_right(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
200
+ result = np.zeros(self.grid.operator_shape, dtype=np.complex128)
201
+ for op in self._ops:
202
+ result += op.apply_from_right(rho, t)
203
+
204
+ return result
@@ -0,0 +1,30 @@
1
+ from typing import Optional
2
+
3
+ import numpy as np
4
+
5
+ import wavepacket as wp
6
+ from .operatorbase import OperatorBase
7
+ from ..grid import State
8
+
9
+
10
+ def expectation_value(op: OperatorBase, state: State, t: Optional[float] = None) -> complex:
11
+ """
12
+ Calculates the expectation value of an operator for a given state.
13
+
14
+ Parameters
15
+ ----------
16
+ op : wp.operator.OperatorBase
17
+ The operator whose expectation value is calculated.
18
+ state : wp.grid.State
19
+ The wave function or density operator that is used for the calculation.
20
+ t : float, optional
21
+ The time at which the operator should be evaluated.
22
+ Only required for time-dependent operators, where the default value `None` raises an exception.
23
+ """
24
+ new = op.apply(state, t)
25
+
26
+ if state.is_wave_function():
27
+ return np.vdot(state.data, new.data)
28
+ else:
29
+ matrix_data = np.reshape(new.data, [new.grid.size, new.grid.size])
30
+ return np.trace(matrix_data)
@@ -0,0 +1,43 @@
1
+ import wavepacket.typing as wpt
2
+ from .operatorbase import OperatorBase
3
+ from ..grid import Grid
4
+
5
+
6
+ class Potential1D(OperatorBase):
7
+ """
8
+ Operator that represents a real-valued one-dimensional potential.
9
+
10
+ Within the DVR approximation [1]_, potential energy operators are
11
+ diagonal in the DVR. That is, they are completely described by a value
12
+ for each grid point.
13
+
14
+ Parameters
15
+ ----------
16
+ grid : wp.grid.Grid
17
+ The grid on which the operator is defined
18
+ dof_index : int
19
+ the index of the degree of freedom along which the potential is defined.
20
+ generator : wpt.RealGenerator
21
+ A callable that generates a potential energy value for each grid point of the respective DOF.
22
+
23
+ References
24
+ ----------
25
+ .. [1] https://sourceforge.net/p/wavepacket/wiki/Numerics.DVR>
26
+ """
27
+
28
+ def __init__(self, grid: Grid, dof_index: int, generator: wpt.RealGenerator):
29
+ data = generator(grid.dofs[dof_index].dvr_points)
30
+ self._wf_data = grid.broadcast(data, dof_index)
31
+ self._ket_data = grid.operator_broadcast(data, dof_index)
32
+ self._bra_data = grid.operator_broadcast(data, dof_index, False)
33
+
34
+ super().__init__(grid)
35
+
36
+ def apply_to_wave_function(self, psi: wpt.ComplexData, t: float) -> wpt.ComplexData:
37
+ return self._wf_data * psi
38
+
39
+ def apply_from_left(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
40
+ return self._ket_data * rho
41
+
42
+ def apply_from_right(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
43
+ return self._bra_data * rho
@@ -0,0 +1,8 @@
1
+ """
2
+ This module contains classes to solve the equations of motion set up by the
3
+ classes in the :py:mod:`wavepacket.expression` module.
4
+ """
5
+ __all__ = ['OdeSolver', 'SolverBase']
6
+
7
+ from .odesolver import OdeSolver
8
+ from .solverbase import SolverBase
@@ -0,0 +1,70 @@
1
+ import numpy as np
2
+ from scipy.integrate import solve_ivp
3
+
4
+ import wavepacket as wp
5
+ import wavepacket.typing as wpt
6
+ from .solverbase import SolverBase
7
+ from ..grid import Grid, State
8
+ from ..expression import ExpressionBase
9
+
10
+
11
+ def _inner_solver(t: float, y: wpt.ComplexData,
12
+ eq: ExpressionBase, grid: Grid) -> wpt.ComplexData:
13
+ # 1. Reconstruct a state from the data array y
14
+ if len(y) == grid.size:
15
+ state = State(grid, np.reshape(y, grid.shape))
16
+ else:
17
+ state = State(grid, np.reshape(y, grid.operator_shape))
18
+
19
+ # 2. Insert into the equation expression
20
+ dot_state = eq.apply(state, t)
21
+
22
+ # 3. Extract the result as a vector again and return
23
+ return dot_state.data.ravel()
24
+
25
+
26
+ class OdeSolver(SolverBase):
27
+ """
28
+ A solver that uses scipy's ODE integrators as backend.
29
+
30
+ This solver merely wraps `scipy.integrate.solve_ivp()` in a Wavepacket
31
+ solver. ODESolvers tend to be workhorses that can be applied to most
32
+ systems without a second thought at the expense of performance.
33
+ See [1]_ for more details on the convergence of solvers.
34
+
35
+ Parameters
36
+ ----------
37
+ expr : wp.expression.ExpressionBase
38
+ The right-hand side of the differential equation that encapsulates the quantum system.
39
+ dt : float
40
+ The elementary time step for the time evolution.
41
+ **kwargs
42
+ Additional keyword parameters can be supplied that are directly forwarded to scipy.
43
+ The most important parameters are "method" (defaults to "RK45") and "rtol", "atol"
44
+ for the relative and absolute error tolerances (booth default to 1e-6).
45
+
46
+ References
47
+ ----------
48
+ .. [1] https://sourceforge.net/p/wavepacket/cpp/blog/2021/04/convergence-2
49
+ """
50
+
51
+ def __init__(self, expr: ExpressionBase, dt: float, **kwargs):
52
+ super().__init__(dt)
53
+
54
+ self._expression = expr
55
+ self._kwargs = kwargs
56
+
57
+ # The default error tolerance is typically too generous for our use cases. Override
58
+ self._kwargs.setdefault('rtol', 1e-6)
59
+
60
+ def step(self, state: State, t: float) -> State:
61
+ t_span = (t, t + self.dt)
62
+ y0 = state.data.ravel()
63
+ args = (self._expression, state.grid)
64
+
65
+ solution = solve_ivp(_inner_solver, t_span, y0, t_eval=[t + self._dt], args=args, **self._kwargs)
66
+ if solution.status != 0:
67
+ raise wp.ExecutionError("Bad return value from integrating"
68
+ f"Status: {solution.status} != 0; Message: {solution.msg}")
69
+
70
+ return State(state.grid, np.reshape(solution.y, state.data.shape))
@@ -0,0 +1,119 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ import wavepacket as wp
4
+ from ..grid import State
5
+
6
+
7
+ class SolverBase(ABC):
8
+ """
9
+ Abstract base class for all differential equation solvers.
10
+
11
+ Each solver must take in the constructor the size of an
12
+ elementary time step, and it must implement a method :py:meth:`step`
13
+ to evolve a state by one elementary time step forward in time.
14
+
15
+ Usually, a solver does not need to know what step it evolves or
16
+ how a differential equation looks in detail. The form of the
17
+ differential equation is encapsulated in a
18
+ :py:class:`wavepacket.expression.ExpressionBase` instance that
19
+ is usually supplied to the specific implementations.
20
+
21
+ A solver may, however, implicitly assume properties of the
22
+ differential equation. It may also take additional arguments,
23
+ these details are documented in the specific implementations.
24
+
25
+ Parameters
26
+ ----------
27
+ dt : float
28
+ The size of an elementary time step.
29
+
30
+
31
+ Attributes
32
+ ----------
33
+ dt
34
+
35
+ Raises
36
+ ------
37
+ wp.InvalidValueError
38
+ If the timestep is not positive.
39
+ """
40
+
41
+ def __init__(self, dt: float):
42
+ if dt <= 0:
43
+ raise wp.InvalidValueError(f"Require positive timestep, got {dt}")
44
+
45
+ self._dt = dt
46
+
47
+ @property
48
+ def dt(self) -> float:
49
+ """
50
+ Returns the size of the elementary time step.
51
+ """
52
+ return self._dt
53
+
54
+ @abstractmethod
55
+ def step(self, state: State, t: float) -> State:
56
+ """
57
+ Evolves the given state for one elementary time step.
58
+
59
+ Given a wave function or density operator at time t,
60
+ this function should return the propagated wave function
61
+ or density operator at the new time t+dt, where dt is
62
+ the elementary time step.
63
+
64
+ Parameters
65
+ ----------
66
+ state : wp.grid.State
67
+ The state to be evolved in time
68
+ t : float
69
+ The time at which the time evolution starts
70
+
71
+ Returns
72
+ -------
73
+ wp.grid.State
74
+ The state at the new time t+dt.
75
+ """
76
+ pass
77
+
78
+ def propagate(self, state0: State, t0: float,
79
+ num_steps: int, include_first: bool = True):
80
+ """
81
+ Generator function that yields the propagated wave function at multiple time steps.
82
+
83
+ This function allows you to propagate in one go with a for loop.
84
+ It repeatedly calls :py:meth:`step` and returns the wave function and current time.
85
+
86
+ Parameters
87
+ ----------
88
+ state0 : wp.grid.State
89
+ The initial state to be propagated in time.
90
+ t0 : float
91
+ The initial time at which the state is given
92
+ num_steps : int
93
+ For how many elementary time steps the state should be propagated.
94
+ include_first : bool
95
+ If true, the function will start by yielding the initial state.
96
+
97
+ Yields
98
+ ------
99
+ float
100
+ The time at which the state is yielded. Starts optionally with t0 and progresses in units of dt.
101
+ wp.grid.State
102
+ The propagated state at the given time.
103
+
104
+ Raises
105
+ ------
106
+ wp.InvalidValueError
107
+ If num_steps is negative.
108
+ """
109
+ if num_steps < 0:
110
+ raise wp.InvalidValueError("Cannot propagate for negative number of steps.")
111
+
112
+ if include_first:
113
+ yield t0, state0
114
+
115
+ state = state0
116
+ for step in range(num_steps):
117
+ t = t0 + step * self._dt
118
+ state = self.step(state, t)
119
+ yield t + self._dt, state
@@ -0,0 +1,11 @@
1
+ """
2
+ Utility classes and functions for writing Wavepacket tests.
3
+ """
4
+
5
+ __all__ = ['DummyDof', 'DummyOperator',
6
+ 'assert_close', 'random_state']
7
+
8
+ from .assertions import assert_close
9
+ from .dummydof import DummyDof
10
+ from .dummyoperator import DummyOperator
11
+ from .random import random_state
@@ -0,0 +1,20 @@
1
+ import numpy.testing
2
+
3
+ import wavepacket as wp
4
+
5
+
6
+ def assert_close(actual: wp.grid.State, expected: wp.grid.State, diff: float = 0) -> None:
7
+ """
8
+ Assertion helper: Verify that two states are on the same grid and similar to each other.
9
+
10
+ Parameters
11
+ ----------
12
+ actual : wp.grid.State
13
+ The expected state
14
+ expected : wp.grid.State
15
+ The state to be tested.
16
+ diff : float
17
+ The maximum absolute difference between any coefficients of the two states.
18
+ """
19
+ assert actual.grid == expected.grid
20
+ numpy.testing.assert_allclose(actual.data, expected.data, rtol=0, atol=diff)
@@ -0,0 +1,22 @@
1
+ import wavepacket.typing as wpt
2
+ from ..grid import DofBase
3
+
4
+
5
+ class DummyDof(DofBase):
6
+ """
7
+ Empty DOF without transformations.
8
+ """
9
+ def __init__(self, dvr_array: wpt.RealData, fbr_array: wpt.RealData):
10
+ super().__init__(dvr_array, fbr_array)
11
+
12
+ def from_fbr(self, data: wpt.ComplexData, index: int, is_ket: bool = True) -> wpt.ComplexData:
13
+ return data
14
+
15
+ def to_dvr(self, data: wpt.ComplexData, index: int) -> wpt.ComplexData:
16
+ return data
17
+
18
+ def from_dvr(self, data: wpt.ComplexData, index: int) -> wpt.ComplexData:
19
+ return data
20
+
21
+ def to_fbr(self, data: wpt.ComplexData, index: int, is_ket: bool = True) -> wpt.ComplexData:
22
+ return data
@@ -0,0 +1,26 @@
1
+ import wavepacket as wp
2
+ import wavepacket.typing as wpt
3
+ from ..grid import Grid
4
+ from ..operator import OperatorBase
5
+
6
+
7
+ class DummyOperator(OperatorBase):
8
+ """
9
+ Empty operator that throws when it is applied.
10
+
11
+ Used for testing where we need an operator, but do not get
12
+ as far as actually doing something.
13
+ """
14
+ def __init__(self, grid: Grid):
15
+ super().__init__(grid)
16
+
17
+ def apply_to_wave_function(self, psi: wpt.ComplexData, t: float) -> wpt.ComplexData:
18
+ raise wp.BadFunctionCall("Should be patched.")
19
+
20
+ def apply_from_left(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
21
+ raise wp.BadFunctionCall("Should be patched.")
22
+
23
+ def apply_from_right(self, rho: wpt.ComplexData, t: float) -> wpt.ComplexData:
24
+ raise wp.BadFunctionCall("Should be patched.")
25
+
26
+
@@ -0,0 +1,16 @@
1
+ import numpy as np
2
+
3
+ import wavepacket as wp
4
+
5
+
6
+ def random_state(grid, seed) -> wp.grid.State:
7
+ """
8
+ Creates a random wave function.
9
+
10
+ Note that this function does not employ high-quality randomization,
11
+ it is only meant to create states without accidental symmetries or lots of code.
12
+ """
13
+ rng = np.random.default_rng(seed)
14
+ data = rng.random(grid.shape) + 1j * rng.random(grid.shape)
15
+
16
+ return wp.grid.State(grid, data)
@@ -0,0 +1,7 @@
1
+ """
2
+ Several aliases and definitions for type checking.
3
+ """
4
+
5
+ __all__ = ['ComplexData', 'RealData', 'Generator', 'RealGenerator']
6
+
7
+ from .data_types import ComplexData, RealData, Generator, RealGenerator