bitten: update bitten.py
This commit is contained in:
parent
f6c6610aad
commit
c66e25da24
1 changed files with 91 additions and 71 deletions
162
bitten/bitten.py
162
bitten/bitten.py
|
@ -20,6 +20,12 @@ def pack(*args):
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def lament(*args, **kwargs):
|
||||||
|
from sys import stderr
|
||||||
|
|
||||||
|
print(*args, **kwargs, file=stderr)
|
||||||
|
|
||||||
|
|
||||||
def _is_in(needle, *haystack):
|
def _is_in(needle, *haystack):
|
||||||
# like the "in" keyword, but uses "is" instead of "==".
|
# like the "in" keyword, but uses "is" instead of "==".
|
||||||
for thing in haystack:
|
for thing in haystack:
|
||||||
|
@ -125,83 +131,95 @@ def _flatten(it):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def _penalize(constraints, *x, tol=1e-5, scale=1e10, growth=4.0):
|
def _penalize_v2021_23(penalties, tol=1e-5, scale=1e10):
|
||||||
# NOTE: maybe growth shouldn't be configurable unless
|
|
||||||
# it also affects the order of the penalty equation (currently 3).
|
|
||||||
# NOTE: different ordering of constraints can result in different solutions.
|
|
||||||
# NOTE: this function doesn't use numpy, so you can copypaste it elsewhere.
|
|
||||||
# growth = growth ** (1.0 / len(constraints))
|
|
||||||
# penalties = [cons(*x) for cons in constraints]
|
|
||||||
penalties = _flatten(cons(*x) for cons in constraints)
|
|
||||||
growth = growth ** (1.0 / len(penalties))
|
|
||||||
unsat = sum(p > tol for p in penalties)
|
|
||||||
# cubic = [p + p * p + p * p * p for p in penalties]
|
|
||||||
penalsum = 0.0
|
|
||||||
for p in penalties:
|
|
||||||
penalsum *= growth # always happens so each penalty gets its own stratum
|
|
||||||
if p > tol:
|
|
||||||
penalsum += p + p * p + p * p * p
|
|
||||||
return scale * (unsat + penalsum)
|
|
||||||
|
|
||||||
|
|
||||||
def _penalize2(constraints, *x, tol=1e-5, scale=1e10, growth=3.0):
|
|
||||||
# updated from upstream (v2022.11)
|
|
||||||
penalties = _flatten(cons(*x) for cons in constraints)
|
|
||||||
growth = growth ** (1.0 / len(penalties))
|
|
||||||
unsat = sum(p > tol for p in penalties)
|
|
||||||
penalsum = 0.0
|
|
||||||
for p in penalties:
|
|
||||||
penalsum *= growth # always happens so each penalty gets its own stratum
|
|
||||||
if p > tol:
|
|
||||||
penalsum += p + p * p * p
|
|
||||||
return scale * (unsat + penalsum)
|
|
||||||
|
|
||||||
|
|
||||||
def _penalize3(constraints, *x, tol=1e-5, scale=1e10, growth=3.0):
|
|
||||||
# updated from upstream (v2022.19)
|
|
||||||
penalties = _flatten(cons(*x) for cons in constraints)
|
|
||||||
n_con = len(penalties)
|
n_con = len(penalties)
|
||||||
growth = growth ** (1.0 / n_con) # "ps"
|
unmet = sum(p > 0.0 for p in penalties)
|
||||||
increment = n_con**-0.5 # "pnsi"
|
growth = 4.0 ** (1.0 / n_con) # "ps"
|
||||||
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
|
penalty = 0.0
|
||||||
for p in penalties:
|
for p in penalties:
|
||||||
p = max(p - tol, 0.0)
|
p = max(p, 0.0)
|
||||||
penalty = penalty * growth + increment + p + p * p * p
|
squared = p * p
|
||||||
nominal = nominal * growth + increment
|
penalty = growth * penalty + p + squared + p * squared
|
||||||
return scale * (penalty - nominal + 1.0)
|
return scale * (unmet + penalty)
|
||||||
|
|
||||||
|
|
||||||
def _penalize4(constraints, *x, tol=1e-5, scale=1e9):
|
def _penalize_v2022_11(penalties, tol=1e-5, scale=1e10):
|
||||||
# updated from upstream (v2022.23)
|
n_con = len(penalties)
|
||||||
# NOTE: i've reduced `scale` by a factor of ten
|
unmet = sum(p > 0.0 for p in penalties)
|
||||||
# out of concern for numeric precision.
|
growth = 3.0 ** (1.0 / n_con) # "ps"
|
||||||
# also, i've removed the growth keyword, since it very likely
|
penalty = 0.0
|
||||||
# needs to coincide with the order of the polynomial (always 3).
|
for p in penalties:
|
||||||
penalties = _flatten(cons(*x) for cons in constraints)
|
p = max(p, 0.0)
|
||||||
|
penalty = growth * penalty + p + p * p * p
|
||||||
|
return scale * (unmet + penalty)
|
||||||
|
|
||||||
|
|
||||||
|
def _penalize_v2022_19(penalties, tol=1e-5, scale=1e10):
|
||||||
n_con = len(penalties)
|
n_con = len(penalties)
|
||||||
growth = 3.0 ** (1.0 / n_con) # "ps"
|
growth = 3.0 ** (1.0 / n_con) # "ps"
|
||||||
increment = n_con**-0.5 # "pnsi"
|
increment = n_con**-0.5 # "pnsi"
|
||||||
# coeff = increment**3.0 # "pnm"
|
|
||||||
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
|
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
|
||||||
for p in penalties:
|
for p in penalties:
|
||||||
p = max(p - tol, 0.0)
|
p = max(p, 0.0)
|
||||||
v = p # * coeff # 2022.25 drops the coeff
|
penalty = growth * penalty + increment + p + p * p * p
|
||||||
v2 = v * v
|
nominal = growth * nominal + increment
|
||||||
poly = v + v2 + v * v2
|
|
||||||
penalty = penalty * growth + increment + poly
|
|
||||||
nominal = nominal * growth + increment
|
|
||||||
return scale * (penalty - nominal + 1.0)
|
return scale * (penalty - nominal + 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def _penalize_v2022_22(penalties, tol=1e-5, scale=1e10):
|
||||||
|
n_con = len(penalties)
|
||||||
|
growth = 3.0 ** (1.0 / n_con) # "ps"
|
||||||
|
increment = n_con**-0.5 # "pnsi"
|
||||||
|
coeff = increment**3.0 # "pnm"
|
||||||
|
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
|
||||||
|
for p in penalties:
|
||||||
|
p = coeff * max(p, 0.0)
|
||||||
|
squared = p * p
|
||||||
|
penalty = growth * penalty + increment + p + squared + p * squared
|
||||||
|
nominal = growth * nominal + increment
|
||||||
|
return scale * (penalty - nominal + 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def _penalize_v2022_25(penalties, tol=1e-5, scale=1e10):
|
||||||
|
n_con = len(penalties)
|
||||||
|
growth = 3.0 ** (1.0 / n_con) # "ps"
|
||||||
|
increment = n_con**-0.5 # "pnsi"
|
||||||
|
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
|
||||||
|
for p in penalties:
|
||||||
|
p = max(p, 0.0)
|
||||||
|
squared = p * p
|
||||||
|
penalty = growth * penalty + increment + p + squared + p * squared
|
||||||
|
nominal = growth * nominal + increment
|
||||||
|
return scale * (penalty - nominal + 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def _penalize_v2022_25_1(penalties, tol=1e-5, scale=1e10):
|
||||||
|
n_con = len(penalties)
|
||||||
|
growth = 3.0 ** (1.0 / n_con) # "ps"
|
||||||
|
increment = n_con**-0.5 # "pnsi"
|
||||||
|
penalty = 0.0 # "pns"
|
||||||
|
for p in penalties:
|
||||||
|
p = max(p, 0.0)
|
||||||
|
penalty = growth * penalty + increment + p + p * p
|
||||||
|
return scale * (1.0 + penalty + penalty * penalty)
|
||||||
|
|
||||||
|
|
||||||
|
penalizers = {
|
||||||
|
1: _penalize_v2021_23,
|
||||||
|
2: _penalize_v2022_11,
|
||||||
|
3: _penalize_v2022_19,
|
||||||
|
4: _penalize_v2022_22,
|
||||||
|
5: _penalize_v2022_25,
|
||||||
|
6: _penalize_v2022_25_1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def penalize(x, constraints, tol=1e-5, *, scale=1e10, growth=4.0):
|
def penalize(x, constraints, tol=1e-5, *, scale=1e10, growth=4.0):
|
||||||
# DEPRECATED
|
assert False, "deprecated; use _penalize_v2021_23 instead"
|
||||||
return _penalize(constraints, x, tol=tol, scale=scale, growth=growth)
|
|
||||||
|
|
||||||
|
|
||||||
def count_unsat(x, constraints, tol=1e-5):
|
def count_unsat(x, constraints, tol=1e-5):
|
||||||
# DEPRECATED
|
assert False, "deprecated; do it yourself"
|
||||||
penalties = [cons(*x) for cons in constraints]
|
|
||||||
return sum(p > tol for p in penalties)
|
|
||||||
|
|
||||||
|
|
||||||
class Impure: # TODO: rename? volatile? aaa the word is on the tip of my tongue
|
class Impure: # TODO: rename? volatile? aaa the word is on the tip of my tongue
|
||||||
|
@ -303,7 +321,7 @@ class Crossentropy(AbstractError):
|
||||||
|
|
||||||
|
|
||||||
class Constrain(Objective):
|
class Constrain(Objective):
|
||||||
def __init__(self, *constraints, tol=1e-5, version=1):
|
def __init__(self, *constraints, tol=1e-5, version=5):
|
||||||
for cons in constraints:
|
for cons in constraints:
|
||||||
assert callable(cons)
|
assert callable(cons)
|
||||||
self.constraints = constraints
|
self.constraints = constraints
|
||||||
|
@ -312,17 +330,19 @@ class Constrain(Objective):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
return self._version + 1
|
return self._version
|
||||||
|
|
||||||
@version.setter
|
@version.setter
|
||||||
def version(self, version):
|
def version(self, version):
|
||||||
penalizers = [_penalize, _penalize2, _penalize3, _penalize4]
|
assert version in penalizers, f"unknown version of penalty function: {version}"
|
||||||
assert 1 <= version <= len(penalizers), version
|
self._version = version
|
||||||
self._version = version - 1
|
|
||||||
self._penalize = penalizers[self._version]
|
self._penalize = penalizers[self._version]
|
||||||
|
|
||||||
def penalize(self, *args):
|
def penalize(self, *x):
|
||||||
return self._penalize(self.constraints, *args, tol=self.tol)
|
penalties = _flatten(cons(*x) for cons in self.constraints)
|
||||||
|
if not any(p > 0.0 for p in penalties):
|
||||||
|
return 0.0
|
||||||
|
return self._penalize(penalties, tol=self.tol)
|
||||||
|
|
||||||
def compute_with(self, fun, **kw_hypers):
|
def compute_with(self, fun, **kw_hypers):
|
||||||
return self.penalize(*kw_hypers.values())
|
return self.penalize(*kw_hypers.values())
|
||||||
|
@ -737,12 +757,12 @@ def _bite(
|
||||||
cache.hash(h)
|
cache.hash(h)
|
||||||
cached = cache.get()
|
cached = cache.get()
|
||||||
if _debug:
|
if _debug:
|
||||||
print("HASH:", cache.hashed, sep="\n")
|
lament("HASH:", cache.hashed, sep="\n")
|
||||||
|
|
||||||
if cached is None:
|
if cached is None:
|
||||||
if _debug: # dirty test for exceptions
|
if _debug: # dirty test for exceptions
|
||||||
center = np.mean(linear_bounds, axis=-1) if hypers else ()
|
center = np.mean(linear_bounds, axis=-1) if hypers else ()
|
||||||
print("FIRST VALUE:", objective(center), sep="\n")
|
lament("FIRST VALUE:", objective(center), sep="\n")
|
||||||
|
|
||||||
if hypers:
|
if hypers:
|
||||||
res = _biteopt(objective, linear_bounds, iters=budget, **optimizer_kwargs)
|
res = _biteopt(objective, linear_bounds, iters=budget, **optimizer_kwargs)
|
||||||
|
@ -753,7 +773,7 @@ def _bite(
|
||||||
res = _evaluator(objective, budget)
|
res = _evaluator(objective, budget)
|
||||||
|
|
||||||
if _debug:
|
if _debug:
|
||||||
print("RES:", res, sep="\n")
|
lament("RES:", res, sep="\n")
|
||||||
|
|
||||||
optimized = res.x
|
optimized = res.x
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Add table
Reference in a new issue