Source code for kleinberg_grid_simulator.utils
from dataclasses import dataclass
from functools import cache
from typing import Optional
import logging
import numpy as np
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
[docs]
@dataclass
class Result:
"""
Dataclass to represent the results.
"""
edt: float
process_time: float
n: int
r: float
p: int
q: int
n_runs: int
julia: bool
numba: bool = True
parallel: bool = False
def __repr__(self):
exclude = {'numba', 'parallel'} if self.julia else {'julia'}
kws = [f"{key}={value!r}" for key, value in self.__dict__.items() if key not in exclude]
return "{}({})".format(type(self).__name__, ", ".join(kws))
[docs]
def cache_edt_of_r(compute_edt, n=10000, n_runs=10000, **kwargs):
"""
Parameters
----------
compute_edt: callable
Function that computes... EDT!
n: :class:`int`, default=10000
Grid siDe
n_runs: :class:`int`, default=10000
Number of routes to compute
kwargs: :class:`dict`
Other parameters
Returns
-------
callable
A cached function that computes the edt as a function of r.
"""
def f(r):
return compute_edt(r=r, n=n, n_runs=n_runs, **kwargs).edt
return cache(f)
[docs]
def get_target(f, a, b, t):
"""
Solve by dichotomy f(x)=t
Parameters
----------
f: callable
f is monotonic between a and b, possibly noisy.
a: :class:`float`
f(a) < t
b: :class:`float`
f(b) > t
t: :class:`float`
Target
Returns
-------
:class:`float`
The (possibly approximated) solution of f(x)=t
Examples
--------
>>> f = cache(lambda x: (x-2)**2)
>>> x = get_target(f, 2., 10., 2.)
>>> f"{x:.4f}"
'3.4142'
"""
fa = f(a)
fb = f(b)
c = (a+b)/2
fc = f(c)
while fa < fc < fb:
if fc < t:
a, fa = c, fc
else:
b, fv = c, fc
c = (a+b)/2
fc = f(c)
logger.info("Noise limit reached.")
return c
[docs]
def gss(f, a, b, tol=1e-5):
"""
Find by Golden-section search the minimum of a function f.
Parameters
----------
f: callable
f, possibly noisy, is convex on [a, b].
a: :class:`float`
Left guess.
b: :class:`float`
Right guess.
tol: :class:`float`
Exit thresold on x.
Returns
-------
:class:`float`
The (possibly approximated) value that minimizes f over [a, b].
:class:`float`
The (possibly approximated) minimum of f over [a, b].
Examples
--------
>>> f = cache(lambda x: (x-2)**2)
>>> x = gss(f, 1, 5)
>>> f"f({x[0]:.4f}) = {x[1]:.4f}"
'f(2.0000) = 0.0000'
"""
gr = (np.sqrt(5) + 1) / 2
c = b - (b - a) / gr
d = a + (b - a) / gr
while abs(b - a) > tol:
logger.info(f"Optimal between {a:.2f} and {b:.2f}")
if f(c) < f(d):
b = d
d = c
c = b - (b - a) / gr
if f(c) > f(a):
logger.info("Noise limit reached.")
break
else:
a = c
c = d
d = a + (b - a) / gr
if f(d) > f(b):
logger.info("Noise limit reached.")
break
return (c + d) / 2, (f(c)+f(d))/2
def get_alpha(v1, v2):
gap = 1 # int(big_int_log(v2.n)-big_int_log(v1.n))
return (np.log2(v2.edt)-np.log2(v1.edt))/gap
def get_best_n_values(v1, v2, budget=20):
n1: Optional[int] = None
alpha = get_alpha(v1, v2)
c = v2.process_time / (v2.n)**alpha
n1 = int((budget/(1+2**alpha)/c)**(1/alpha))
if n1 <= 2*v1.n:
n1 = None
return n1, alpha