max-div 0.0.3__py3-none-any.whl → 0.1.1__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,32 @@
1
+ max_div/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ max_div/_cli.py,sha256=WMGA-kw3hi3un6MCngjxbQ_WUMVvKxKtz02n2ydqWU0,3276
3
+ max_div/benchmark/__init__.py,sha256=9lru2A83WlZCYeM6Ppy0XWN9K4dzb6twpCqpo8CQA3I,102
4
+ max_div/benchmark/_formatting.py,sha256=zFUMU0Nz6HHngvDqgo7vzi6v5LtVWrXvk2P9PdAKIgM,8991
5
+ max_div/benchmark/randint.py,sha256=-SMK59LVXCZIxDbuEFmtsxAXU-zGeVyP2KyDXxTCJ0Y,3502
6
+ max_div/benchmark/randint_constrained.py,sha256=SF5XaCiOEnvpkqlbjWBJtJmYXzjJF7oRIgUFJt3dS4o,12477
7
+ max_div/constraints/__init__.py,sha256=K2CnAb9llDxNu8qtGEAx4vZ8SLWVqs5zXfhbXzkGBog,72
8
+ max_div/constraints/_numba.py,sha256=VyQqmsF8Kcfay73dRVnGmOPcWrpsXL-yqhd6mH71uAw,4485
9
+ max_div/constraints/constraint.py,sha256=MK2kbexHCrxYkYMT_n76KVzwC9gARIyF3nMdjk01UAk,246
10
+ max_div/constraints/constraints.py,sha256=ARn7v8tu1ESx66KcXyGpfKyqM0SNAxHk0T4V48OEboI,1675
11
+ max_div/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ max_div/internal/benchmarking/__init__.py,sha256=WNjrLlGI_vkI-nZkQMPICgi67rTVole_MCc4jNe_OxI,83
13
+ max_div/internal/benchmarking/_micro_benchmark.py,sha256=cZGkx3ngZSJDRAAF5TP56bEkEvNpfAAg1-1tuiGeBPQ,5992
14
+ max_div/internal/benchmarking/_timer.py,sha256=gpA3KgreDTtK2QUARJKI4Ir9dTDMKW7B5GQuThRbYh8,1050
15
+ max_div/internal/formatting/__init__.py,sha256=3TzBpe_KfCY_3ETvOoRIl_qAtfgjT1b-8o3iYpVK9ZE,182
16
+ max_div/internal/formatting/_markdown.py,sha256=Nf0fSDSmmJF2pYbnLoG23-1_d1S8RLYCWSYrNghTgO8,1441
17
+ max_div/internal/formatting/_time_duration.py,sha256=dO88GPZeMbxVk0YF9Zr2Y0NLCXaiW_HwTa7S-PT9e9E,6740
18
+ max_div/internal/math/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
19
+ max_div/internal/math/fast_log.py,sha256=e3rGXdEqBJN_v1DGNniE5CtPfF7zzTzr9sfeda_V73A,5609
20
+ max_div/internal/math/random.py,sha256=XnNnCxkFitdl6nBmCi7gI_6VGNhOabqXXYIm1EU2kRY,6548
21
+ max_div/internal/math/select_k_minmax.py,sha256=q2z6uLYAwfcAox4RzFyZwa4jJ9CoiPK50mCrEMEKEhY,10112
22
+ max_div/internal/utils/__init__.py,sha256=Xqb7Rh9Lq6WsUK-5B9aouwWMhAh0c1a7pF0rdaP5kgo,62
23
+ max_div/internal/utils/_clip.py,sha256=kWUCKG9uFxNvFWvThMyFdWLlzVb9cFADngpBYl5b38U,230
24
+ max_div/internal/utils/_precision.py,sha256=3eEUdJQmXA3Q6Tg4jb3vG0RTwOBNgnc09j5ts0gcIdw,125
25
+ max_div/sampling/__init__.py,sha256=HNmw64ps3rXURPYuXvVUcePT6aKi5PJWXu_Ex7yavRg,57
26
+ max_div/sampling/con.py,sha256=bgkeRagZ2Epv2sSajqqvdhmIec6Dn2EI4osHwYIZRS4,13856
27
+ max_div/sampling/uncon.py,sha256=jmrLIvqr7FcPaST5uBgXtFVj_ZDiJYuPbkY-_i1BtIU,13219
28
+ max_div-0.1.1.dist-info/METADATA,sha256=NG3ccEDS75fPE80bfHZNCFl0dkG1bxYfZOsslwibgQM,1884
29
+ max_div-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
+ max_div-0.1.1.dist-info/entry_points.txt,sha256=Q-xCOAOZ6vSRodScs7U1cF_S6IM_1GCRdD4JTeWzYKc,45
31
+ max_div-0.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
32
+ max_div-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ max-div = max_div._cli:cli
@@ -1,85 +0,0 @@
1
- import math
2
-
3
- import numpy as np
4
-
5
- from max_div.internal.benchmarking import BenchmarkResult, benchmark
6
- from max_div.internal.compat import is_numba_installed
7
- from max_div.sampling.discrete import sample_int
8
-
9
-
10
- def benchmark_sample_int(turbo: bool = False) -> None:
11
- """
12
- Benchmarks the `sample_int` function from `max_div.sampling.discrete`.
13
-
14
- Different scenarios are tested:
15
-
16
- * with & without replacement
17
- * uniform & non-uniform sampling
18
- * `use_numba` True and False
19
- * different sizes of (`n`, `k`):
20
- * (10, 1), (100, 1), (1000, 1), (5000, 1), (10000, 1)
21
- * (10000, 10), (10000, 100), (10000, 1000), (10000, 5000), (10000, 10000)
22
-
23
- :param turbo: If `True`, a much shorter (but less reliable) benchmark is run; intended for testing purposes.
24
- """
25
-
26
- if not is_numba_installed():
27
- print("====================================================================================")
28
- print(" WARNING: Numba is not installed!!!")
29
- print("====================================================================================")
30
-
31
- print("Benchmarking `sample_int`...")
32
- print()
33
- print("".ljust(30) + "use_numba=False".rjust(25) + "use_numba=True".rjust(25))
34
-
35
- for replace, use_p, desc in [
36
- (True, False, "with replacement, uniform"),
37
- (False, False, "without replacement, uniform"),
38
- (True, True, "with replacement, non-uniform"),
39
- (False, True, "without replacement, non-uniform"),
40
- ]:
41
- print(desc.upper())
42
-
43
- for n, k in [
44
- (10, 1),
45
- (100, 1),
46
- (1000, 1),
47
- (5000, 1),
48
- (10000, 1),
49
- (10000, 10),
50
- (10000, 100),
51
- (10000, 1000),
52
- (10000, 5000),
53
- (10000, 10000),
54
- ]:
55
- size_str = f"{k:<6_} out of {n:<6_}"
56
-
57
- results: list[BenchmarkResult] = []
58
- for use_numba in [False, True]:
59
- if use_p:
60
- p = np.random.rand(n)
61
- p /= p.sum()
62
- else:
63
- p = None
64
-
65
- def func_to_benchmark():
66
- sample_int(n=n, k=k, replace=replace, p=p, use_numba=use_numba)
67
-
68
- results.append(
69
- benchmark(
70
- f=func_to_benchmark,
71
- t_per_run=0.001 if turbo else 0.1,
72
- n_warmup=3 if turbo else 10,
73
- n_benchmark=3 if turbo else 30,
74
- silent=True,
75
- )
76
- )
77
-
78
- print(
79
- (" " * 4)
80
- + size_str.ljust(26)
81
- + results[0].t_sec_with_uncertainty_str.rjust(25)
82
- + results[1].t_sec_with_uncertainty_str.rjust(25)
83
- )
84
-
85
- print()
@@ -1 +0,0 @@
1
- from ._numba import is_numba_installed, numba
@@ -1,14 +0,0 @@
1
- # =================================================================================================
2
- # Transparant handling of original numba vs dummy numba
3
- # =================================================================================================
4
- try:
5
- import numba
6
- except ImportError:
7
- from ._dummy_numba import Numba as numba
8
-
9
-
10
- # =================================================================================================
11
- # Helpers
12
- # =================================================================================================
13
- def is_numba_installed() -> bool:
14
- return numba.__version__ != "0.0.0"
@@ -1,94 +0,0 @@
1
- """
2
- Implements a dummy version of Numba for environments where Numba is not available.
3
- Main goal is that code that uses @numba.njit or numba.tuped.Dict, ... does not break and transparently falls back to standard Python.
4
- """
5
-
6
- from ._helpers import dummy_decorator
7
-
8
-
9
- # =================================================================================================
10
- # Dummy typed containers
11
- # =================================================================================================
12
- class NumbaTypedDict(dict):
13
- @staticmethod
14
- def empty(*args, **kwargs):
15
- return dict()
16
-
17
-
18
- class NumbaTypedList(list):
19
- @staticmethod
20
- def empty_list(*args, **kwargs):
21
- return list()
22
-
23
-
24
- # =================================================================================================
25
- # Main dummy package tree
26
- # =================================================================================================
27
- class NumbaTyped:
28
- # implements subset
29
- Dict = NumbaTypedDict
30
- List = NumbaTypedList
31
-
32
-
33
- class NumbaTypes:
34
- # implements dummies for all members of numba.types.__all__
35
- int8 = object()
36
- int16 = object()
37
- int32 = object()
38
- int64 = object()
39
- uint8 = object()
40
- uint16 = object()
41
- uint32 = object()
42
- uint64 = object()
43
- intp = object()
44
- uintp = object()
45
- intc = object()
46
- uintc = object()
47
- ssize_t = object()
48
- size_t = object()
49
- boolean = object()
50
- float32 = object()
51
- float64 = object()
52
- complex64 = object()
53
- complex128 = object()
54
- bool_ = object()
55
- byte = object()
56
- char = object()
57
- uchar = object()
58
- short = object()
59
- ushort = object()
60
- int_ = object()
61
- uint = object()
62
- long_ = object()
63
- ulong = object()
64
- longlong = object()
65
- ulonglong = object()
66
- double = object()
67
- void = object()
68
- none = object()
69
- b1 = object()
70
- i1 = object()
71
- i2 = object()
72
- i4 = object()
73
- i8 = object()
74
- u1 = object()
75
- u2 = object()
76
- u4 = object()
77
- u8 = object()
78
- f4 = object()
79
- f8 = object()
80
- c8 = object()
81
- c16 = object()
82
- optional = object()
83
- ffi_forced_object = object()
84
- ffi = object()
85
- deferred_type = object()
86
- bool = object()
87
-
88
-
89
- class Numba:
90
- __version__ = "0.0.0"
91
- jit = dummy_decorator
92
- njit = dummy_decorator
93
- typed = NumbaTyped
94
- types = NumbaTypes
@@ -1,14 +0,0 @@
1
- from typing import Callable
2
-
3
-
4
- def dummy_decorator(*args, **kwargs):
5
- # dummy decorator that does nothing and can be used with or without arguments
6
- if len(args) == 1 and isinstance(args[0], Callable):
7
- # decorator used without arguments
8
- return args[0]
9
- else:
10
- # decorator used with arguments
11
- def decorator(func):
12
- return func
13
-
14
- return decorator
@@ -1,176 +0,0 @@
1
- import numpy as np
2
-
3
- from max_div.internal.compat import numba
4
-
5
-
6
- # =================================================================================================
7
- # sample_int
8
- # =================================================================================================
9
- def sample_int(
10
- n: int,
11
- k: int | None = None,
12
- replace: bool = True,
13
- p: np.ndarray[float] | None = None,
14
- seed: int | None = None,
15
- use_numba: bool = True,
16
- ) -> int | np.ndarray[np.int64]:
17
- """
18
- Randomly sample `k` integers from range `[0, n-1]`, optionally with replacement and per-value probabilities.
19
-
20
- Different implementation is used, depending on the case:
21
-
22
- | `use_numba` | `p` specified | `replace` | `k` | Method Used | Complexity |
23
- |----------------|----------------|------------|------|------------------------------------------|-----------------|
24
- | No | Any | Any | Any | `np.random.choice` | depends |
25
- | Yes | No | True | Any | `np.random.randint`, uniform sampling | O(k) |
26
- | Yes | No | False | Any | k-element Fisher-Yates shuffle | O(n) |
27
- | Yes | Yes | Any | 1 | Multinomial sampling using CDF | O(n + log(n)) |
28
- | Yes | Yes | True | >1 | Multinomial sampling using CDF | O(n + k log(n)) |
29
- | Yes | Yes | False | >1 | Efraimidis-Spirakis sampling + exponential key sampling (Gumbel-Max Trick) using the Ziggurat algorithm. | O(n) |
30
-
31
- :param n: defines population to sample from as range [0, n-1]. `n` must be >0.
32
- :param k: The number of integers to sample (>0). `k=None` indicates a single integer sample.
33
- :param replace: Whether to sample with replacement.
34
- :param p: Optional 1D array of probabilities associated with each integer in the range.
35
- Size must be equal to max_value + 1 and sum to 1.
36
- :param seed: Optional random seed for reproducibility.
37
- :param use_numba: Use the self-implemented algorithm (which is `numba`-accelerated if `numba` is installed)
38
- :return: `k=None` --> single integer; `k>=1` --> (k,)-sized array with sampled integers.
39
- """
40
-
41
- # -------------------------------------------------------------------------
42
- # Don't try using numba
43
- # -------------------------------------------------------------------------
44
- if not use_numba:
45
- # --- argument handling ---------------------------
46
- if (k == 1) or (k is None):
47
- replace = True # single sample, replacement makes no difference, so we can fall back to faster methods
48
-
49
- # --- argument validation -------------------------
50
- if n < 1:
51
- raise ValueError(f"n must be >=1. (here: {n})")
52
- if k is not None:
53
- if k < 1:
54
- raise ValueError(f"k must be >=1. (here: {k})")
55
- if (not replace) and (k > n):
56
- raise ValueError(f"Cannot sample {k} unique values from range [0, {n}) without replacement.")
57
-
58
- # --- sampling ------------------------------------
59
- if seed is not None:
60
- np.random.seed(seed)
61
-
62
- if k is None:
63
- # returns scalar
64
- return np.random.choice(n, size=None, replace=replace, p=p)
65
- else:
66
- # returns array
67
- return np.random.choice(n, size=k, replace=replace, p=p)
68
-
69
- # -------------------------------------------------------------------------
70
- # Try using numba
71
- # -------------------------------------------------------------------------
72
- else:
73
- # --- argument handling ---------------------------
74
- k_orig = k # remember, to know what return value we need
75
- if (k == 1) or (k is None):
76
- k = 1 # make sure we always have an integer for the numba function
77
- replace = True # single sample, replacement makes no difference, so we can fall back to faster methods
78
- if p is None:
79
- use_p = False
80
- p = np.zeros(0, dtype=np.float64) # dummy value
81
- else:
82
- use_p = True
83
- if seed is None:
84
- use_seed = False
85
- seed = 42 # dummy value
86
- else:
87
- use_seed = True
88
-
89
- # --- argument validation -------------------------
90
- if n < 1:
91
- raise ValueError(f"n must be >=1. (here: {n})")
92
- if k < 1:
93
- raise ValueError(f"k must be >=1. (here: {k})")
94
- if (not replace) and (k > n):
95
- raise ValueError(f"Cannot sample {k} unique values from range [0, {n}) without replacement.")
96
- if use_p:
97
- if (p.ndim != 1) or (p.size != n):
98
- raise ValueError(f"p must be a 1D array of size {n}. (here: shape={p.shape})")
99
-
100
- # --- call numba function -------------------------
101
- samples = sample_int_numba(n=n, k=k, replace=replace, use_p=use_p, p=p, use_seed=use_seed, seed=seed)
102
- if k_orig is None:
103
- return samples[0]
104
- else:
105
- return samples
106
-
107
-
108
- @numba.njit
109
- def sample_int_numba(
110
- n: int,
111
- k: int,
112
- replace: bool,
113
- use_p: bool = False,
114
- p: np.ndarray[float] = np.zeros(0),
115
- use_seed: bool = False,
116
- seed: int = 42,
117
- ) -> np.ndarray[int]:
118
- """
119
- See native python version docstring. We split the implementation into a core numba-decorated function and
120
- a Python front-end, because numba-decorated functions do not allow for multiple possible return types.
121
- """
122
- if use_seed:
123
- np.random.seed(seed)
124
-
125
- if (k == n) and (not replace):
126
- # corner case: return all elements in random order
127
- population = np.arange(n, dtype=np.int64)
128
- np.random.shuffle(population)
129
- return population
130
-
131
- if not use_p:
132
- if replace:
133
- # UNIFORM sampling with replacement
134
- return np.random.choice(n, size=k) # O(k)
135
- else:
136
- # UNIFORM sampling without replacement using Fisher-Yates shuffle
137
- population = np.arange(n, dtype=np.int64) # O(n)
138
- for i in range(k): # k x O(1)
139
- j = np.random.randint(i, n)
140
- population[i], population[j] = population[j], population[i]
141
- return population[:k] # O(k)
142
- else:
143
- if replace:
144
- # NON-UNIFORM sampling with replacement using CDF
145
- cdf = np.empty(n) # O(n)
146
- csum = 0.0
147
- for i in range(n): # n x O(1)
148
- csum += p[i]
149
- cdf[i] = csum
150
- samples = np.empty(k, dtype=np.int64) # O(k)
151
- for i in range(k): # k x O(log(n))
152
- r = np.random.random()
153
- idx = np.searchsorted(cdf, r)
154
- samples[i] = idx
155
- return samples
156
- else:
157
- # NON-UNIFORM sampling without replacement using Efraimidis-Spirakis + Exponential keys
158
- # algorithm description:
159
- # Efraimidis: select k elements corresponding to k largest values of u_i^{1/p_i} (u_i ~ U(0,1))
160
- # Gumbel-Max Trick: select k smallest values of -log(u_i)/p_i (u_i ~ U(0,1))
161
- # Ziggurat: (TODO) generate log(u_i) more efficiently, applying the Ziggurat algorithm
162
- # to the exponential distribution, which avoids usage of transcendental
163
- # functions for the majority of the samples.
164
- keys = np.empty(n, dtype=np.float64) # O(n)
165
- u = np.random.random(n) # O(n)
166
- for i in range(n): # n x O(1)
167
- if p[i] == 0.0:
168
- keys[i] = np.inf
169
- else:
170
- keys[i] = -np.log(u[i]) / p[i]
171
-
172
- # Get indices of k smallest keys
173
- if k > 1:
174
- return np.argpartition(keys, k)[:k] # O(n) average case
175
- else:
176
- return np.array([np.argmin(keys)]) # O(n)
@@ -1,23 +0,0 @@
1
- max_div/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- max_div/benchmark/__init__.py,sha256=wzyPThNW4GETpkPh5yXDKTkQbJQ1ulungL2jQL8Ak2s,45
3
- max_div/benchmark/sample_int.py,sha256=mueD3v0tqP0zTN9Gf1LjnvrCEm0fV3zlgcE-Eh3V0vQ,2817
4
- max_div/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- max_div/internal/benchmarking/__init__.py,sha256=WNjrLlGI_vkI-nZkQMPICgi67rTVole_MCc4jNe_OxI,83
6
- max_div/internal/benchmarking/_micro_benchmark.py,sha256=hvQxmKG72MnG9dANrLbYm8D7NsJP-2pGapl5idX_E7I,4183
7
- max_div/internal/benchmarking/_timer.py,sha256=gpA3KgreDTtK2QUARJKI4Ir9dTDMKW7B5GQuThRbYh8,1050
8
- max_div/internal/compat/__init__.py,sha256=ApHeMuO_9zlP34BnSZMRVLVyEEv1VEkGXNJzw5ddaK4,46
9
- max_div/internal/compat/_numba/__init__.py,sha256=fKwHEiafrYMmk_GOaOKZBb3rDU9q0sYqzi3BPPtV0A4,631
10
- max_div/internal/compat/_numba/_dummy_numba.py,sha256=FPguZfpQzpWRxmc4pYSGVCoPzgofsgQO_bknF_bK8aQ,2341
11
- max_div/internal/compat/_numba/_helpers.py,sha256=pQqAjHYbSwPos9011xpmbkJeZ5cPBUZq14qh8G6GtjQ,402
12
- max_div/internal/formatting/__init__.py,sha256=UMim6fo_e4JGJo7pl8FZ1nNpmStlfbGB7O49LhCZwWo,104
13
- max_div/internal/formatting/_time_duration.py,sha256=dO88GPZeMbxVk0YF9Zr2Y0NLCXaiW_HwTa7S-PT9e9E,6740
14
- max_div/internal/utils/__init__.py,sha256=Xqb7Rh9Lq6WsUK-5B9aouwWMhAh0c1a7pF0rdaP5kgo,62
15
- max_div/internal/utils/_clip.py,sha256=kWUCKG9uFxNvFWvThMyFdWLlzVb9cFADngpBYl5b38U,230
16
- max_div/internal/utils/_precision.py,sha256=3eEUdJQmXA3Q6Tg4jb3vG0RTwOBNgnc09j5ts0gcIdw,125
17
- max_div/sampling/__init__.py,sha256=_tsDiQNr4brsMiROQVbJFpq7Um79HV7P0ec6ChMd_iY,33
18
- max_div/sampling/discrete.py,sha256=VYBpeYYIUbLeuA3Ld4PTBkVwq6ziIBW0KlkJVqmqNuU,8048
19
- max_div-0.0.3.dist-info/METADATA,sha256=PwZtrIAsZRKs5bCVw_SzDOQpfPtCVOcDxHTvuBRrLAM,1604
20
- max_div-0.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- max_div-0.0.3.dist-info/entry_points.txt,sha256=EL6VQ4tIThMRtNqBtQvj2yAMBJm-7OM801VYPBsRwp4,80
22
- max_div-0.0.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
23
- max_div-0.0.3.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- benchmark_sample_int = max_div.benchmark:benchmark_sample_int