Skip to content

Commit 4047a38

Browse files
authored
Fix random number generation by removing legacy random number generation (#78)
* fix random number generation by incorporating default_rng Signed-off-by: Marcel Müller <[email protected]> * update CHANGELOG.md Signed-off-by: Marcel Müller <[email protected]> --------- Signed-off-by: Marcel Müller <[email protected]>
1 parent 37913dd commit 4047a38

File tree

4 files changed

+24
-15
lines changed

4 files changed

+24
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- bug leading to `UnicodeDecodeError` when reading `xtb` output files
2121
- bug with all atom lists being initialized with a length of 102 instead of 103
2222
- inconsistent default values for the `mindlessgen.toml` and the `ConfigManager` class
23+
- legacy pseudo random number generation removed and replaced by `np.random.default_rng()` for avoiding interference with other packages
2324

2425
### Added
2526
- support for the novel "g-xTB" method (working title: GP3-xTB)

src/mindlessgen/molecules/generate_molecule.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def generate_atom_list(cfg: GenerateConfig, verbosity: int = 1) -> np.ndarray:
6565
"""
6666
Generate a random molecule with a random number of atoms.
6767
"""
68+
# initialize a default random number generator
69+
rng = np.random.default_rng()
70+
6871
# Define a new set of all elements that can be included
6972
set_all_elem = set(range(0, MAX_ELEM))
7073
if cfg.forbidden_elements:
@@ -166,17 +169,17 @@ def add_random(min_adds: int, max_adds: int, min_nat: int, max_nat: int):
166169
"""
167170
Default random atom generation.
168171
"""
169-
numatoms_all = np.random.randint(
170-
min_adds, max_adds
172+
numatoms_all = rng.integers(
173+
low=min_adds, high=max_adds
171174
) # with range(1, 7) -> mean value: 3.5
172175
for _ in range(numatoms_all):
173176
# Define the atom type to be added via a random choice from the set of valid elements
174-
ati = np.random.choice(list(valid_elems))
177+
ati = rng.choice(list(valid_elems))
175178
if verbosity > 1:
176179
print(f"Adding atom type {ati}...")
177180
# Add a random number of atoms of the defined type
178-
natoms[ati] = natoms[ati] + np.random.randint(
179-
min_nat, max_nat
181+
natoms[ati] = natoms[ati] + rng.integers(
182+
low=min_nat, high=max_nat
180183
) # with range(0, 3) -> mean value: 1
181184
# max value of this section with commented settings: 12
182185

@@ -193,11 +196,11 @@ def add_organic(num_adds: int, min_nat: int, max_nat: int) -> None:
193196
return
194197
for _ in range(num_adds): # with range(5) -> mean value 1.5
195198
# go through the elements B to F (4-9 in 0-based indexing)
196-
ati = np.random.choice(valid_organic)
199+
ati = rng.choice(valid_organic)
197200
if verbosity > 1:
198201
print(f"Adding atom type {ati}...")
199-
natoms[ati] = natoms[ati] + np.random.randint(
200-
min_nat, max_nat
202+
natoms[ati] = natoms[ati] + rng.integers(
203+
low=min_nat, high=max_nat
201204
) # with range(0, 3) -> mean value: 1
202205
# max value of this section with commented settings: 8
203206

@@ -243,7 +246,7 @@ def add_hydrogen():
243246
# If no H is included, add H atoms
244247
if natoms[0] == 0:
245248
nat = np.sum(natoms)
246-
randint = np.random.rand()
249+
randint = rng.random()
247250
j = 1 + round(randint * nat * 1.2)
248251
natoms[0] = natoms[0] + j
249252
# Example: For 5 atoms at this point,
@@ -257,7 +260,7 @@ def check_min_max_atoms():
257260
f"Minimal number of atoms: {cfg.min_num_atoms}; "
258261
+ f"Actual number of atoms: {np.sum(natoms)}.\nAdding atoms..."
259262
)
260-
ati = np.random.choice(list(valid_elems))
263+
ati = rng.choice(list(valid_elems))
261264
max_limit = cfg.element_composition.get(ati, (None, None))[1]
262265
if max_limit is not None and natoms[ati] >= max_limit:
263266
continue
@@ -285,7 +288,7 @@ def check_min_max_atoms():
285288
# randomly select an atom type from the list, thereby weighting the selection
286289
# for reduction by the current occurrence
287290
# generate a random number between 0 and the number of atoms in the list
288-
random_index = np.random.randint(len(atom_list))
291+
random_index = rng.integers(0, len(atom_list))
289292
i = atom_list[int(random_index)]
290293
if natoms[i] > 0:
291294
min_limit = cfg.element_composition.get(i, (None, None))[0]
@@ -361,13 +364,15 @@ def generate_random_coordinates(at: np.ndarray) -> tuple[np.ndarray, np.ndarray]
361364
atilist: list[int] = []
362365
xyz = np.zeros((sum(at), 3))
363366
numatoms = 0
367+
rng = np.random.default_rng()
364368
for elem, count in enumerate(at):
365369
for m in range(count):
366370
# different rules for hydrogen
367371
if elem == 0:
368-
xyz[numatoms + m, :] = np.random.rand(3) * 3 - 1.5
372+
xyz[numatoms + m, :] = rng.random(3) * 3 - 1.5
369373
else:
370-
xyz[numatoms + m, :] = np.random.rand(3) * 2 - 1
374+
xyz[numatoms + m, :] = rng.random(3) * 2 - 1
375+
371376
atilist.append(elem)
372377

373378
numatoms += count

src/mindlessgen/molecules/miscellaneous.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def set_random_charge(ati: np.ndarray, verbosity: int = 1) -> tuple[int, int]:
6363
iseven = True
6464
# if the number of electrons is even, the charge is -2, 0, or 2
6565
# if the number of electrons is odd, the charge is -1, 1
66-
randint = np.random.rand()
66+
rng = np.random.default_rng()
67+
randint = rng.random()
6768
if iseven:
6869
if randint < 1 / 3:
6970
charge = -2

src/mindlessgen/molecules/molecule.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ def __init__(self, name: str = ""):
156156
self._xyz: np.ndarray = np.array([], dtype=float)
157157
self._ati: np.ndarray = np.array([], dtype=int)
158158

159+
self.rng = np.random.default_rng()
160+
159161
def __str__(self) -> str:
160162
"""
161163
Return a user-friendly string representation of the molecule.
@@ -637,5 +639,5 @@ def set_name_from_formula(self) -> None:
637639

638640
molname = self.sum_formula()
639641
# add a random hash to the name
640-
hashname = hashlib.sha256(np.random.bytes(32)).hexdigest()[:6]
642+
hashname = hashlib.sha256(self.rng.bytes(32)).hexdigest()[:6]
641643
self.name = f"{molname}_{hashname}"

0 commit comments

Comments
 (0)