Skip to content

Commit b989730

Browse files
committed
default random.Random() rng, np.random.default_rng() elsewhere
1 parent 55dedda commit b989730

24 files changed

Lines changed: 190 additions & 167 deletions

src/votekit/ballot_generator/ballot_generator.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import math
2+
import random
23
from abc import abstractmethod
34
from typing import Optional, Tuple, Union
45

56
import numpy as np
6-
from numpy.random import Generator
77

88
from votekit.ballot import Ballot
99
from votekit.pref_interval import PreferenceInterval
@@ -85,6 +85,7 @@ def from_params(
8585
bloc_voter_prop: dict,
8686
cohesion_parameters: dict,
8787
alphas: dict,
88+
*,
8889
random_seed: Optional[int] = None,
8990
**data,
9091
):
@@ -126,7 +127,7 @@ def from_params(
126127
interval = PreferenceInterval.from_dirichlet(
127128
candidates=slate_to_candidates[b],
128129
alpha=alphas[current_bloc][b],
129-
rng=rng,
130+
numpy_rng=rng,
130131
)
131132
intervals[b] = interval
132133

@@ -163,7 +164,7 @@ def generate_profile(
163164
pass
164165

165166
@staticmethod
166-
def _round_num(num: float, *, rng: Optional[Generator] = None) -> int:
167+
def _round_num(num: float, *, rng: Optional[random.Random] = None) -> int:
167168
"""
168169
169170
Rounds up or down a float randomly.
@@ -176,7 +177,8 @@ def _round_num(num: float, *, rng: Optional[Generator] = None) -> int:
176177
Returns:
177178
int: A whole number.
178179
"""
179-
rand = np.random.default_rng(rng).random()
180+
rng = random.Random() if rng is None else rng
181+
rand = rng.random()
180182
return math.ceil(num) if rand > 0.5 else math.floor(num)
181183

182184
@staticmethod

src/votekit/ballot_generator/bloc_slate_generator/cambridge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ def _inner_cambridge_sampler(
276276
)
277277

278278
pref_profile_by_bloc[bloc] = _convert_slate_ballots_to_profile(
279-
config, bloc, slate_ballots, rng=rng
279+
config, bloc, slate_ballots, numpy_rng=rng
280280
)
281281

282282
return pref_profile_by_bloc

src/votekit/ballot_generator/bloc_slate_generator/config/core.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import numpy as np
1313
import pandas as pd
14+
from numpy.random import Generator
1415

1516
from votekit.ballot_generator.bloc_slate_generator.config.collections import (
1617
BlocProportions,
@@ -1407,7 +1408,10 @@ def __keycheck_dirichlet_alphas(
14071408
)
14081409

14091410
def set_dirichlet_alphas(
1410-
self, alphas: Union[Mapping[str, Mapping[str, Union[float, int]]], pd.DataFrame]
1411+
self,
1412+
alphas: Union[Mapping[str, Mapping[str, Union[float, int]]], pd.DataFrame],
1413+
*,
1414+
random_seed: Optional[int] = None,
14111415
) -> None:
14121416
"""
14131417
Set the Dirichlet alphas for the configuration and resample the preference intervals.
@@ -1417,6 +1421,8 @@ def set_dirichlet_alphas(
14171421
of bloc names to mappings of slate names to their Dirichlet alpha values. Each bloc
14181422
must have a mapping for every slate defined in slate_to_candidates. All alpha
14191423
values must be positive finite reals.
1424+
random_seed (int | None): seed for RNG, allows for reproducible results given the same
1425+
inputs. Seed set to None by default, different results will be generated each time.
14201426
14211427
Raises:
14221428
ConfigurationWarning: If preference intervals have already been set without
@@ -1438,7 +1444,9 @@ def set_dirichlet_alphas(
14381444
else:
14391445
self.__alphas = alphas.copy().astype(float)
14401446
self.__clear_alpha_bool = False
1441-
self.resample_preference_intervals_from_dirichlet_alphas()
1447+
self.resample_preference_intervals_from_dirichlet_alphas(
1448+
numpy_rng=np.random.default_rng(seed=random_seed)
1449+
)
14421450

14431451
def clear_dirichlet_alphas(self) -> None:
14441452
"""Remove the Dirichlet alphas from the configuration."""
@@ -1457,6 +1465,8 @@ def read_dirichlet_alphas(self) -> Optional[pd.DataFrame]:
14571465

14581466
def resample_preference_intervals_from_dirichlet_alphas(
14591467
self,
1468+
*,
1469+
numpy_rng: Optional[Generator] = None,
14601470
) -> None:
14611471
"""
14621472
Resample the preference intervals for each bloc from the current Dirichlet alphas.
@@ -1475,6 +1485,7 @@ def resample_preference_intervals_from_dirichlet_alphas(
14751485
candidates=list(self.slate_to_candidates[slate]),
14761486
alpha=float(self.__alphas[slate].loc[bloc]),
14771487
allow_zero_support=self.allow_zero_support_candidates,
1488+
numpy_rng=numpy_rng,
14781489
)
14791490
preference_dict[bloc] = slate_intervals
14801491
preference_df = convert_preference_map_to_preference_df(preference_dict)

src/votekit/ballot_generator/bloc_slate_generator/cumulative.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import apportionment.methods as apportion
1515
import numpy as np
16+
from numpy.random import Generator
1617

1718
from votekit.ballot import ScoreBallot
1819
from votekit.ballot_generator.bloc_slate_generator.config import BlocSlateConfig
@@ -24,7 +25,7 @@
2425

2526

2627
def _inner_name_cumulative(
27-
config: BlocSlateConfig, total_points: int, *, random_seed: Optional[int] = None
28+
config: BlocSlateConfig, total_points: int, *, numpy_rng: Optional[Generator] = None
2829
) -> dict[str, ScoreProfile]:
2930
"""
3031
Inner function to generate cumulative profiles by bloc using the name-Cumulative model.
@@ -33,8 +34,9 @@ def _inner_name_cumulative(
3334
config (BlocSlateConfig): Configuration object containing all necessary parameters for
3435
working with a bloc-slate ballot generator.
3536
total_points (int): The total number of points to distribute among candidates.
36-
random_seed (int | None): Seed for RNG, allows for reproducible results given the same
37-
inputs. Seed set to None by default, different results will be generated each time.
37+
numpy_rng (Generator | None): Random Number Generator, allows for reproducible results given
38+
the same inputs. RNG set to None by default, different results will be generated each
39+
time.
3840
3941
Returns:
4042
dict[str, ScoreProfile]: A dictionary whose keys are bloc strings and values are
@@ -51,7 +53,7 @@ def _inner_name_cumulative(
5153
pp_by_bloc: dict[str, ScoreProfile] = {}
5254

5355
pref_by_bloc = config.get_combined_preference_intervals_by_bloc()
54-
rng = np.random.default_rng(seed=random_seed)
56+
rng = np.random.default_rng() if numpy_rng is None else numpy_rng
5557

5658
for bloc in bloc_lst:
5759
num_ballots = int(ballots_per_bloc.get(bloc, 0))
@@ -121,7 +123,8 @@ def name_cumulative_profile_generator(
121123
if total_points <= 0:
122124
raise ValueError("total_points must be a positive integer")
123125

124-
pp_by_bloc = _inner_name_cumulative(config, total_points=total_points, random_seed=random_seed)
126+
rng = np.random.default_rng(seed=random_seed)
127+
pp_by_bloc = _inner_name_cumulative(config, total_points=total_points, numpy_rng=rng)
125128

126129
pp = ScoreProfile()
127130
for profile in pp_by_bloc.values():
@@ -169,7 +172,8 @@ def name_cumulative_ballot_generator_by_bloc(
169172
if total_points <= 0:
170173
raise ValueError("'total_points' must be a positive integer")
171174

172-
pp_by_bloc = _inner_name_cumulative(config, total_points=total_points, random_seed=random_seed)
175+
rng = np.random.default_rng(seed=random_seed)
176+
pp_by_bloc = _inner_name_cumulative(config, total_points=total_points, numpy_rng=rng)
173177

174178
if group_ballots:
175179
for bloc in pp_by_bloc:

src/votekit/ballot_generator/bloc_slate_generator/name_bradley_terry.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import apportionment.methods as apportion
2323
import numpy as np
2424
import pandas as pd
25-
from numpy.random import Generator
2625

2726
from votekit.ballot import RankBallot
2827
from votekit.ballot_generator.bloc_slate_generator.config import BlocSlateConfig
@@ -147,7 +146,7 @@ def _check_name_bt_memory(config: BlocSlateConfig) -> None:
147146

148147

149148
def _inner_name_bradley_terry(
150-
config: BlocSlateConfig, random_seed: Optional[int] = None
149+
config: BlocSlateConfig, *, rng: Optional[random.Random] = None
151150
) -> dict[str, RankProfile]:
152151
"""
153152
Sample from the BT distribution using direct sampling.
@@ -159,8 +158,9 @@ def _inner_name_bradley_terry(
159158
Args:
160159
config (BlocSlateConfig): Configuration object containing all necessary parameters for
161160
working with a bloc-slate ballot generator.
162-
random_seed (int | None): Seed for RNG, allows for reproducible results given the same
163-
inputs. Seed set to None by default, different results will be generated each time.
161+
rng (random.Random | None): Random Number Generator, allows for reproducible results given
162+
the same inputs. RNG set to None by default, different results will be generated each
163+
time.
164164
165165
Returns:
166166
dict[str, RankProfile]: Generated preference profiles by bloc.
@@ -183,7 +183,7 @@ def _inner_name_bradley_terry(
183183
pp_by_bloc = {b: RankProfile() for b in bloc_lst}
184184

185185
pref_interval_by_bloc_dict = config.get_combined_preference_intervals_by_bloc()
186-
rng = np.random.default_rng(seed=random_seed)
186+
rng = random.Random() if rng is None else rng
187187

188188
for bloc in config.bloc_proportions.keys():
189189
n_ballots = ballots_per_bloc[bloc]
@@ -197,10 +197,10 @@ def _inner_name_bradley_terry(
197197

198198
# The return of this will be a numpy array, so we don't need to make it into a list
199199
sampled_indices = np.array(
200-
rng.choice(
201-
a=len(rankings),
202-
size=n_ballots,
203-
p=probs,
200+
rng.choices(
201+
range(len(rankings)),
202+
k=n_ballots,
203+
weights=probs,
204204
),
205205
ndmin=1,
206206
)
@@ -241,7 +241,7 @@ def _bradley_terry_mcmc(
241241
burn_in_time: int = 0,
242242
chain_length: Optional[int] = None,
243243
*,
244-
rng: Optional[Generator] = None,
244+
rng: Optional[random.Random] = None,
245245
):
246246
"""
247247
Sample from BT distribution for a given preference interval using MCMC. Defaults
@@ -258,8 +258,9 @@ def _bradley_terry_mcmc(
258258
chain_length//n_ballots steps from the chain until the desired number of ballots is
259259
reached. Defaults to None which sets the chain_length to the number of ballots in
260260
the config.
261-
rng (Generator | None): Random Number Generator seeded with a known value for reproducible
262-
results. By default, seeded with None to generate different results each time.
261+
rng (random.Random | None): Random Number Generator seeded with a known value for
262+
reproducible results. By default, seeded with None to generate different results each
263+
time.
263264
"""
264265

265266
if chain_length is None:
@@ -280,9 +281,9 @@ def _bradley_terry_mcmc(
280281
burn_in_time = burn_in_time # int(10e5)
281282
if verbose:
282283
print(f"Burn in time: {burn_in_time}")
283-
rng = np.random.default_rng(rng)
284+
rng = random.Random() if rng is None else rng
284285
swap_indices = [
285-
(j1, j1 + 1) for j1 in rng.choice(n_candidates - 1, size=n_ballots + burn_in_time)
286+
(j1, j1 + 1) for j1 in rng.choices(range(n_candidates - 1), k=n_ballots + burn_in_time)
286287
]
287288

288289
for i in range(burn_in_time):
@@ -356,7 +357,7 @@ def _inner_name_bradley_terry_mcmc(
356357
verbose: bool = False,
357358
burn_in_time: int = 0,
358359
chain_length: Optional[int] = None,
359-
random_seed: Optional[int] = None,
360+
rng: Optional[random.Random] = None,
360361
) -> dict[str, RankProfile]:
361362
"""
362363
Sample from the BT distribution using Markov Chain Monte Carlo.
@@ -372,8 +373,9 @@ def _inner_name_bradley_terry_mcmc(
372373
chain_length//n_ballots steps from the chain until the desired number of ballots is
373374
reached. Defaults to None which sets the chain_length to the number of ballots in
374375
the config.
375-
random_seed (int | None): Seed for RNG, allows for reproducible results given the same
376-
inputs. Seed set to None by default, different results will be generated each time.
376+
rng (Generator | None): Seed for Random Number Generator, allows for reproducible results
377+
given the same inputs. RNG set to None by default, different results will be generated
378+
each time.
377379
378380
Returns:
379381
Union[RankProfile, Tuple]
@@ -393,7 +395,7 @@ def _inner_name_bradley_terry_mcmc(
393395

394396
pp_by_bloc = {b: RankProfile() for b in bloc_lst}
395397
pref_interval_by_bloc_dict = config.get_combined_preference_intervals_by_bloc()
396-
rng = np.random.default_rng(seed=random_seed)
398+
rng = random.Random() if rng is None else rng
397399

398400
for bloc in bloc_lst:
399401
n_ballots = ballots_per_bloc[bloc]
@@ -448,7 +450,8 @@ def name_bt_profiles_by_bloc_generator(
448450
"""
449451
_check_name_bt_memory(config)
450452
config.is_valid(raise_errors=True)
451-
pp_by_bloc = _inner_name_bradley_terry(config, random_seed=random_seed)
453+
rng = random.Random(random_seed)
454+
pp_by_bloc = _inner_name_bradley_terry(config, rng=rng)
452455
if group_ballots:
453456
for bloc in pp_by_bloc:
454457
pp_by_bloc[bloc] = pp_by_bloc[bloc].group_ballots()
@@ -480,7 +483,8 @@ def name_bt_profile_generator(
480483
"""
481484
_check_name_bt_memory(config)
482485
config.is_valid(raise_errors=True)
483-
pp_by_bloc = _inner_name_bradley_terry(config, random_seed=random_seed)
486+
rng = random.Random(random_seed)
487+
pp_by_bloc = _inner_name_bradley_terry(config, rng=rng)
484488

485489
# combine the profiles
486490
pp = RankProfile()
@@ -525,12 +529,13 @@ def name_bt_profile_generator_using_mcmc(
525529
RankProfile: Generated preference profile.
526530
"""
527531
config.is_valid(raise_errors=True)
532+
rng = random.Random(random_seed)
528533
pp_by_bloc = _inner_name_bradley_terry_mcmc(
529534
config,
530535
verbose=verbose,
531536
burn_in_time=burn_in_time,
532537
chain_length=chain_length,
533-
random_seed=random_seed,
538+
rng=rng,
534539
)
535540
# combine the profiles
536541
pp = RankProfile()
@@ -576,12 +581,13 @@ def name_bt_profiles_by_bloc_generator_using_mcmc(
576581
dict[str, RankProfile]: Generated preference profiles by bloc.
577582
"""
578583
config.is_valid(raise_errors=True)
584+
rng = random.Random(random_seed)
579585
pp_by_bloc = _inner_name_bradley_terry_mcmc(
580586
config,
581587
verbose=verbose,
582588
burn_in_time=burn_in_time,
583589
chain_length=chain_length,
584-
random_seed=random_seed,
590+
rng=rng,
585591
)
586592

587593
if group_ballots:

src/votekit/ballot_generator/bloc_slate_generator/name_plackett_luce.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import apportionment.methods as apportion
1515
import numpy as np
1616
import pandas as pd
17+
from numpy.random import Generator
1718

1819
from votekit.ballot_generator.bloc_slate_generator.config import BlocSlateConfig
1920
from votekit.pref_profile import RankProfile
@@ -27,7 +28,7 @@ def _inner_name_plackett_luce(
2728
config: BlocSlateConfig,
2829
*,
2930
ballot_length: Optional[int] = None,
30-
random_seed: Optional[int] = None,
31+
numpy_rng: Optional[Generator] = None,
3132
) -> dict[str, RankProfile]:
3233
"""
3334
Inner function to generate preference profiles by bloc using the name-Plackett-Luce model.
@@ -40,8 +41,9 @@ def _inner_name_plackett_luce(
4041
working with a bloc-slate ballot generator.
4142
ballot_length (Optional[int]): Number of ranking positions allowed per ballot. If None,
4243
this is set to the total number of candidates in the configuration. Defaults to None.
43-
random_seed (int | None): seed for RNG, allows for reproducible results given the same
44-
inputs. Seed set to None by default, different results will be generated each time.
44+
numpy_rng (Generator) | None): Random Number Generator, allows for reproducible results
45+
given the same inputs. RNG set to None by default, different results will be generated
46+
each time.
4547
4648
Returns:
4749
dict[str, RankProfile]: Dictionary whose keys are bloc strings and values are
@@ -66,7 +68,7 @@ def _inner_name_plackett_luce(
6668

6769
pp_by_bloc = {b: RankProfile() for b in bloc_lst}
6870
pref_interval_by_bloc_dict = config.get_combined_preference_intervals_by_bloc()
69-
rng = np.random.default_rng(seed=random_seed)
71+
rng = np.random.default_rng() if numpy_rng is None else numpy_rng
7072

7173
for bloc in config.blocs:
7274
n_ballots = ballots_per_bloc[bloc]
@@ -141,7 +143,8 @@ def name_pl_profile_generator(
141143
config.is_valid(raise_errors=True)
142144

143145
# pp_by_bloc = _inner_name_plackett_luce(config, ballot_length=ballot_length)
144-
pp_by_bloc = _inner_name_plackett_luce(config, random_seed=random_seed)
146+
rng = np.random.default_rng(seed=random_seed)
147+
pp_by_bloc = _inner_name_plackett_luce(config, numpy_rng=rng)
145148

146149
pp = RankProfile(ballots=tuple())
147150
for profile in pp_by_bloc.values():
@@ -182,7 +185,8 @@ def name_pl_profiles_by_bloc_generator(
182185
config.is_valid(raise_errors=True)
183186

184187
# pp_by_bloc = _inner_name_plackett_luce(config, ballot_length=ballot_length)
185-
pp_by_bloc = _inner_name_plackett_luce(config, random_seed=random_seed)
188+
rng = np.random.default_rng(seed=random_seed)
189+
pp_by_bloc = _inner_name_plackett_luce(config, numpy_rng=rng)
186190

187191
if group_ballots:
188192
for bloc, profile in pp_by_bloc.items():

0 commit comments

Comments
 (0)