33import numpy as np
44import scipy .linalg as splin
55
6- from . import convert
7-
86
97class AtomicEnsemble :
108 """
119 Represents an atomic ensemble consisting of n atoms.
1210
1311 Each atom is is defined by its phase space vector (x0, y0, z0, vx, vy, vz) at time
1412 t=0. From this phase space vector the position at later times can be calculated.
15- Optionally, weights can be added for each atom in the ensemble. Slicing along the
16- axis of the n atoms is supported.
1713
1814 Parameters
1915 ----------
@@ -30,8 +26,6 @@ class AtomicEnsemble:
3026 time : float, optional
3127 the initial time (default 0) when the phase space and state vectors are
3228 initialized
33- weights : 1darray , optional
34- Optional weights for each of the n atoms in the ensemble
3529
3630 Attributes
3731 ----------
@@ -47,7 +41,7 @@ class AtomicEnsemble:
4741
4842 """
4943
50- def __init__ (self , phase_space_vectors , state_kets = [1 , 0 ], time = 0 , weights = None ):
44+ def __init__ (self , phase_space_vectors , state_kets = [1 , 0 ], time = 0 ):
5145 assert phase_space_vectors .shape [1 ] == 6
5246 self .phase_space_vectors = phase_space_vectors
5347 self .state_kets = state_kets
@@ -56,37 +50,28 @@ def __init__(self, phase_space_vectors, state_kets=[1, 0], time=0, weights=None)
5650 self .initial_position = self .phase_space_vectors [:, 0 :3 ]
5751 self .initial_velocity = self .phase_space_vectors [:, 3 :6 ]
5852
59- if weights is None :
60- weights = np .ones (len (self )) # unity weight for each atom
61- else :
62- assert len (weights ) == self .phase_space_vectors .shape [0 ]
63- self .weights = weights
64-
6553 def __getitem__ (self , key ):
6654 """Select certain atoms "from the ensemble.
6755
68- Parameters
56+ Parameters
6957 ----------
70- key : int or slice or bool map
71- for example 2, 1:15 or a boolean map
58+ key : int or slice or bool map
59+ for example 2, 1:15 or a boolean map
7260
73- Returns
74- -------
75- new_instance : AtomicEnsemble
76- a new instance of the atomic ensemble only containing the selected atoms
61+ Returns
62+ -------
63+ new_instance : AtomicEnsemble
64+ a new instance of the atomic ensemble only containing the selected atoms
7765 """
7866 phase_space_vectors = self .phase_space_vectors [key ][:]
7967 state_kets = self .state_kets [key ][:]
80- weights = self .weights [key ]
8168 if isinstance (key , int ):
82- # ratain correct shape in case of only one atom is selected
69+ # retain correct shape in case of only one atom is selected
8370 phase_space_vectors = phase_space_vectors .reshape (1 , 6 )
8471 state_kets = state_kets .reshape (1 , len (state_kets ))
85- weights = weights .reshape (1 , 1 )
8672 new_instance = AtomicEnsemble (
8773 phase_space_vectors = phase_space_vectors ,
8874 state_kets = state_kets ,
89- weights = weights ,
9075 )
9176 return new_instance
9277
@@ -279,123 +264,6 @@ def create_random_ensemble_from_gaussian_distribution(
279264 return ensemble
280265
281266
282- def create_ensemble_from_grids (pos_params , vel_params , ** kwargs ):
283- """
284- Create an atomic ensemble from evenly spaced position and velocity grids.
285-
286- The resulting position and velocity grids are evenly spaced on polar coordinates.
287-
288- Parameters
289- ----------
290- pos_params, vel_params : dict
291- Dictionary containing the parameters determining the position and velocity
292- distributions of the atomic ensemble. They each have to contain the arguments
293- described in the docstring of `make_grid`, i.e. `std_rho`, `std_z` (required),
294- `n_rho`, `n_theta`, `n_z`, `m_std_rho`, `m_std_z`, `weight`, optional.
295- **kwargs :
296- Optional keyworded arguments passed to `AtomicEnsemble`
297-
298- Returns
299- -------
300- ensemble : AtomicEnsemble
301- Atomic ensemble contains all possible combinations of the position and velocity
302- grid as phase space vectors. They vectors are weighted according to the combined
303- (multiplied) weights of the respective position and velocity distributions
304- according to the `weight` arguments in `pos_params` and `vel_params`
305- """
306- pos_grid , pos_weights = make_grid (** pos_params )
307- vel_grid , vel_weights = make_grid (** vel_params )
308- grid = combine_grids (pos_grid , vel_grid )
309- weights = combine_weights (pos_weights , vel_weights )
310- ensemble = AtomicEnsemble (grid , weights = weights , ** kwargs )
311- return ensemble
312-
313-
314- def make_grid (std_rho , std_z , n_rho = 20 , n_theta = 36 , n_z = 1 , m_std_rho = 3 , m_std_z = 0 ):
315- """
316- Evenly spaced grid of positions (or velocities) and weights.
317-
318- Each of these positions (or velocities) are evenly spaced in polar coordinates and
319- weighted according to a gaussian distribution.
320-
321- Parameters
322- ----------
323- std_rho, std_sigma : float
324- 1/e radius of the position or velocity distribution.
325- n_rho, n_theta, n_z : int
326- number of grid points per standard deviation along rho and z direction and
327- total number of points along theta, respectively
328- m_std_rho, m_std_z : int
329- number of standard deviations for the rho and z distribution
330-
331- Returns
332- -------
333- grid : n × 3 array
334- Grid of n vectors in carthesian coordinates (x, y, z). In polar coordinates,
335- the grid has this form:
336- [[dRho, 0, -m_std_z*sigma_z/2]
337- [dRho, dTheta, ...]
338- [dRho , 2*dTheta, ...]
339- [... , ..., 0]
340- [dRho , <2*pi, ...]
341- [2*dRho , 0, ...]
342- [2*dRho , dTheta, ...]
343- [... , ..., ...
344- [m_std_rho*sigma_rho , <2*pi, +m_std_z*sigma_z/2]]
345- weights : 1 × n array
346- weights for each vector in the grid
347- """
348- rhos = np .linspace (0 , m_std_rho * std_rho , n_rho )
349- thetas = np .linspace (0 , 2 * np .pi , n_theta )
350- zs = np .linspace (- m_std_z * std_z / 2 , m_std_z * std_z / 2 , max (n_z * m_std_z , 1 ))
351- grid = np .array (np .meshgrid (rhos , thetas , zs )).T .reshape (- 1 , 3 )
352- # get weights before converting to carthesian coordinates
353- weights = np .exp (- grid [:, 0 ] ** 2 / (2 * std_rho ** 2 ))
354- if std_z != 0 :
355- # check if distribution is 2d to avoid divide by 0
356- weights = weights * np .exp (- grid [:, 2 ] ** 2 / (2 * std_z ** 2 ))
357- grid = convert .pol2cart (grid )
358- return grid , weights
359-
360-
361- def combine_grids (pos , vel ):
362- """
363- Combine a position and velocity grid into an array of phase space vectors.
364-
365- The resulting array contains (x, y, z, vx, vy, vz).
366-
367- Parameters
368- ----------
369- pos, vel : n, m × 3 array
370- position and velocity grids as generated by `make_grid`
371-
372- Returns
373- -------
374- phase_space_vectors : n * m × 1 array
375- """
376- # FIXME: replace with faster version, for example based on meshgrid
377- phase_space_vectors = np .array (
378- [np .array ((p , v )).flatten () for p in pos for v in vel ]
379- )
380- return phase_space_vectors
381-
382-
383- def combine_weights (pos_weights , vel_weights ):
384- """
385- Combine the weights of a position and velocity grid.
386-
387- Complements `_combine_grids`.
388-
389- Parameters
390- ----------
391- pos_weights, vel_weights : n × 1 array
392- weights of a position and velocity grids.
393-
394- """
395- # FIXME: replace with faster version, for example based on meshgrid
396- return np .array ([p * v for p in pos_weights for v in vel_weights ])
397-
398-
399267def _fidelity (rho_a , rho_b ):
400268 """
401269 Calculate the fidelity of two density matrices [1, 2].
0 commit comments