Source code for glyph.gp.constraints
import pathlib
import sys
import logging
import importlib
from .individual import simplify_this, AExpressionTree
from glyph.utils import Timeout
logger = logging.getLogger(__name__)
[docs]class Constraint:
def __init__(self, spaces):
self.spaces = spaces
def _contains(self, element):
if not self.spaces:
return False
return any(element in subspace for subspace in self.spaces)
def __contains__(self, element):
try:
return self._contains(element)
except Exception as e:
logger.debug(f"Exception was raised during constraints check: {e}.")
return False
[docs]class NonFiniteExpression(Constraint):
def __init__(self, zero=True, infty=True, constant=False):
"""Use sympy to check for finite expressions.
Args:
zero: flag to check for zero expressions
infty: flag to check for infinite expressions
constant: flag to check for constant expressions
"""
self.zero = zero
self.infty = infty
self.constant = constant
def _contains(self, element):
expr = simplify_this(element)
if isinstance(expr, str):
logger.debug(f"Could not simplify {element}.")
return False
if self.constant:
if expr.is_constant():
return True
elif all(t.name in element.pset.constants for t in element.terminals):
return True
if self.infty:
if "zoo" in str(expr):
return True
if self.zero:
if expr.is_zero:
return True
return False
[docs]class PreTest(Constraint):
def __init__(self, fn, fun="chi"):
"""Apply pre-testing to check for constraint violation.
The python script needs to provide a callable fun(ind).
Args:
fn: filename of the python script.
fun: name of the function in fn.
"""
fn = pathlib.Path(fn)
sys.path.append(str(fn.parent))
try:
mod = importlib.import_module(fn.stem)
self.f = getattr(mod, fun)
except (AttributeError, ImportError):
logger.error(f"Funktion {fun} not available in {fn}")
self.f = lambda *args: False
finally:
sys.path.pop()
def _contains(self, element):
if self.f is None:
logger.warning("Using invalid PretestNullSpace")
return self.f(element)
[docs]class PreTestService(Constraint): # todo com cannot be pickled !
def __init__(self, assessment_runner):
self.assessment_runner = assessment_runner
@property
def com(self):
return self.assessment_runner.com
@property
def make_str(self):
return self.assessment_runner.make_str
def _contains(self, element):
payload = self.make_str(element)
self.com.send(dict(action="PRETEST", payload=payload))
return self.com.recv()["ok"] == "True"
[docs]def reject_constrain_violation(constraint, n_trials=30, timeout=60):
"""Create constraints decorators based on rules.
:param constraint:
:param n_trials: Number of tries. Give up afterwards (return input).
:return: list of constraint decorators
"""
def reject(operator):
def inner(*inds, **kw):
with Timeout(timeout) as to_ctx:
for i in range(n_trials):
out = operator(*inds, **kw)
if isinstance(out, AExpressionTree): # can this be done w/o type checking?
t = out
elif isinstance(out, (list, tuple)):
t = out[0]
else:
raise RuntimeError
if t not in constraint:
break
else:
logger.warning(
f"Number of tries exhausted during constrained operation {operator} on individual {inds}."
) # noqa
if inds:
return inds
else:
raise UserWarning
if to_ctx.state == to_ctx.TIMED_OUT:
logger.warning(f"Timeout during constrained operation {operator} on individual {inds}.")
return out
return inner
return reject
[docs]def constrain(funcs, constraint, n_trials=30, timeout=60):
"""Decorate a list of genetic operators with constraints.
:param funcs: list of operators (mate, mutate, create)
:param constraint: instance of Nullspace
:return: constrained operators
"""
if not constraint:
return funcs
return [reject_constrain_violation(constraint, n_trials=n_trials, timeout=timeout)(f) for f in funcs]