bitten: update bitten.py

This commit is contained in:
Connor Olding 2022-06-19 01:03:14 -07:00
parent f6c6610aad
commit c66e25da24

View file

@ -20,6 +20,12 @@ def pack(*args):
return args
def lament(*args, **kwargs):
from sys import stderr
print(*args, **kwargs, file=stderr)
def _is_in(needle, *haystack):
# like the "in" keyword, but uses "is" instead of "==".
for thing in haystack:
@ -125,83 +131,95 @@ def _flatten(it):
return res
def _penalize(constraints, *x, tol=1e-5, scale=1e10, growth=4.0):
# 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)
def _penalize_v2021_23(penalties, tol=1e-5, scale=1e10):
n_con = len(penalties)
growth = growth ** (1.0 / n_con) # "ps"
increment = n_con**-0.5 # "pnsi"
penalty, nominal = 0.0, 0.0 # "pns", "pnsm"
unmet = sum(p > 0.0 for p in penalties)
growth = 4.0 ** (1.0 / n_con) # "ps"
penalty = 0.0
for p in penalties:
p = max(p - tol, 0.0)
penalty = penalty * growth + increment + p + p * p * p
nominal = nominal * growth + increment
return scale * (penalty - nominal + 1.0)
p = max(p, 0.0)
squared = p * p
penalty = growth * penalty + p + squared + p * squared
return scale * (unmet + penalty)
def _penalize4(constraints, *x, tol=1e-5, scale=1e9):
# updated from upstream (v2022.23)
# NOTE: i've reduced `scale` by a factor of ten
# out of concern for numeric precision.
# also, i've removed the growth keyword, since it very likely
# needs to coincide with the order of the polynomial (always 3).
penalties = _flatten(cons(*x) for cons in constraints)
def _penalize_v2022_11(penalties, tol=1e-5, scale=1e10):
n_con = len(penalties)
unmet = sum(p > 0.0 for p in penalties)
growth = 3.0 ** (1.0 / n_con) # "ps"
penalty = 0.0
for p in penalties:
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)
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 = max(p - tol, 0.0)
v = p # * coeff # 2022.25 drops the coeff
v2 = v * v
poly = v + v2 + v * v2
penalty = penalty * growth + increment + poly
nominal = nominal * growth + increment
p = max(p, 0.0)
penalty = growth * penalty + increment + p + p * p * p
nominal = growth * nominal + increment
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):
# DEPRECATED
return _penalize(constraints, x, tol=tol, scale=scale, growth=growth)
assert False, "deprecated; use _penalize_v2021_23 instead"
def count_unsat(x, constraints, tol=1e-5):
# DEPRECATED
penalties = [cons(*x) for cons in constraints]
return sum(p > tol for p in penalties)
assert False, "deprecated; do it yourself"
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):
def __init__(self, *constraints, tol=1e-5, version=1):
def __init__(self, *constraints, tol=1e-5, version=5):
for cons in constraints:
assert callable(cons)
self.constraints = constraints
@ -312,17 +330,19 @@ class Constrain(Objective):
@property
def version(self):
return self._version + 1
return self._version
@version.setter
def version(self, version):
penalizers = [_penalize, _penalize2, _penalize3, _penalize4]
assert 1 <= version <= len(penalizers), version
self._version = version - 1
assert version in penalizers, f"unknown version of penalty function: {version}"
self._version = version
self._penalize = penalizers[self._version]
def penalize(self, *args):
return self._penalize(self.constraints, *args, tol=self.tol)
def penalize(self, *x):
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):
return self.penalize(*kw_hypers.values())
@ -737,12 +757,12 @@ def _bite(
cache.hash(h)
cached = cache.get()
if _debug:
print("HASH:", cache.hashed, sep="\n")
lament("HASH:", cache.hashed, sep="\n")
if cached is None:
if _debug: # dirty test for exceptions
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:
res = _biteopt(objective, linear_bounds, iters=budget, **optimizer_kwargs)
@ -753,7 +773,7 @@ def _bite(
res = _evaluator(objective, budget)
if _debug:
print("RES:", res, sep="\n")
lament("RES:", res, sep="\n")
optimized = res.x
else: