From d92e9f85d5c6b8c55ab7503daa3da41fbce2c2fc Mon Sep 17 00:00:00 2001 From: WalzDS Date: Fri, 9 Sep 2022 15:49:34 +0200 Subject: [PATCH 1/3] require 0.5 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 050ba6c..1719ee3 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ def get_version(): description="Advanced & flexible design of experiments", packages=["doe"], install_requires=[ - "formulaic", + "formulaic>=0.5", "loguru", "mopti", "numpy", From 329739956f526fe4896e391dbd3eb0b58302a9fb Mon Sep 17 00:00:00 2001 From: WalzDS Date: Fri, 9 Sep 2022 18:23:48 +0200 Subject: [PATCH 2/3] fix test_utils --- doe/utils.py | 76 +++------ test/test_utils.py | 401 +++++++++++++++++---------------------------- 2 files changed, 173 insertions(+), 304 deletions(-) diff --git a/doe/utils.py b/doe/utils.py index 157ca25..dc876fd 100644 --- a/doe/utils.py +++ b/doe/utils.py @@ -1,4 +1,3 @@ -import sys import warnings from copy import deepcopy from itertools import combinations @@ -148,13 +147,8 @@ def original_problem(self) -> opti.Problem: def get_formula_from_string( self, model_type: Union[str, Formula] = "linear", - rhs_only: bool = True, ) -> Formula: - return get_formula_from_string( - model_type=model_type, - problem_context=self, - rhs_only=rhs_only, - ) + return get_formula_from_string(model_type=model_type, problem_context=self) def value2cat(value: pd.Series, input: opti.Categorical): @@ -176,7 +170,6 @@ def value2discrete(value: np.float64, input: opti.Discrete): def get_formula_from_string( model_type: Union[str, Formula] = "linear", problem_context: Optional[ProblemContext] = None, - rhs_only: bool = True, ) -> Formula: """Reformulates a string describing a model or certain keywords as Formula objects. @@ -184,52 +177,37 @@ def get_formula_from_string( model_type (str or Formula): A formula containing all model terms. problem_context (ProblemContext): A problem context that nests necessary information on how to translate a problem to a formula. Contains a problem. - rhs_only (bool): The function returns only the right hand side of the formula if set to True. - Returns: - A Formula object describing the model that was given as string or keyword. - """ - # set maximum recursion depth to higher value - recursion_limit = sys.getrecursionlimit() - sys.setrecursionlimit(2000) + Returns: + A Formula object describing the model that was given as string or keyword. + """ if isinstance(model_type, Formula): return model_type - # build model if a keyword and a problem are given. - else: - # linear model - if model_type == "linear": - formula = linear_formula(problem_context=problem_context) - - # linear and interactions model - elif model_type == "linear-and-quadratic": - formula = linear_and_quadratic_formula(problem_context=problem_context) - - # linear and quadratic model - elif model_type == "linear-and-interactions": - formula = linear_and_interactions_formula(problem_context=problem_context) - # fully quadratic model - elif model_type == "fully-quadratic": - formula = fully_quadratic_formula(problem_context=problem_context) - - else: - formula = model_type + " " + # linear model + if model_type == "linear": + formula = linear_formula(problem_context=problem_context) + # linear and interactions model + elif model_type == "linear-and-quadratic": + formula = linear_and_quadratic_formula(problem_context=problem_context) + # linear and quadratic model + elif model_type == "linear-and-interactions": + formula = linear_and_interactions_formula(problem_context=problem_context) + # fully quadratic model + elif model_type == "fully-quadratic": + formula = fully_quadratic_formula(problem_context=problem_context) + else: + formula = model_type + " " formula = Formula(formula[:-3]) - if rhs_only: - if hasattr(formula, "rhs"): - formula = formula.rhs - - # set recursion limit to old value - sys.setrecursionlimit(recursion_limit) + if hasattr(formula, "rhs"): + return formula.rhs return formula -def linear_formula( - problem_context: Optional[ProblemContext], -) -> str: +def linear_formula(problem_context: Optional[ProblemContext]) -> str: """Reformulates a string describing a linear-model or certain keywords as Formula objects. formula = model_type + " " @@ -358,18 +336,16 @@ def fully_quadratic_formula( def n_zero_eigvals( problem_context: ProblemContext, model_type: Union[str, Formula], epsilon=1e-7 ) -> int: - """Determine the number of eigenvalues of the information matrix that are necessarily zero because of - equality constraints.""" + """Determine the number of eigenvalues of the information matrix that are + necessarily zero because of equality constraints.""" # sample points (fulfilling the constraints) - model_formula = problem_context.get_formula_from_string( - model_type=model_type, rhs_only=True - ) - N = len(model_formula.terms) + 3 + formula = problem_context.get_formula_from_string(model_type) + N = sum(1 for _ in formula) + 3 X = problem_context.problem.sample_inputs(N) # compute eigenvalues of information matrix - A = model_formula.get_model_matrix(X) + A = formula.get_model_matrix(X) eigvals = np.abs(np.linalg.eigvalsh(A.T @ A)) return len(eigvals) - len(eigvals[eigvals > epsilon]) diff --git a/test/test_utils.py b/test/test_utils.py index a3fdffa..d0d1034 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -1,5 +1,3 @@ -import sys - import numpy as np import opti import pytest @@ -20,259 +18,170 @@ ) -def get_formula_from_string_recursion_limit(): - # save recursion limit - recursion_limit = sys.getrecursionlimit() - - # get formula for very large model - model = "" - for i in range(350): - model += f"x{i} + " - model = model[:-3] - model = get_formula_from_string(model_type=model) - - terms = [f"x{i}" for i in range(350)] - terms.append("1") - - for i in range(351): - assert model.terms[i] in terms - assert terms[i] in model.terms - - assert recursion_limit == sys.getrecursionlimit() - - -def test_get_formula_from_string(): +def test_formula_from_string(): problem = opti.Problem( inputs=[opti.Continuous(f"x{i}", [0, 1]) for i in range(3)], outputs=[opti.Continuous("y")], ) - problem_context = ProblemContext(problem) # linear model - terms = ["1", "x0", "x1", "x2"] - model_formula = get_formula_from_string( - problem_context=problem_context, model_type="linear" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) + formula = get_formula_from_string("linear", problem_context) + terms = [str(t) for t in formula] + assert set(terms) == set(["1", "x0", "x1", "x2"]) # linear and interaction - terms = ["1", "x0", "x1", "x2", "x0:x1", "x0:x2", "x1:x2"] - model_formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-interactions" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) + formula = get_formula_from_string("linear-and-interactions", problem_context) + terms = [str(t) for t in formula] + assert set(terms) == set(["1", "x0", "x1", "x2", "x0:x1", "x0:x2", "x1:x2"]) # linear and quadratic - terms = ["1", "x0", "x1", "x2", "x0**2", "x1**2", "x2**2"] - model_formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-quadratic" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) + formula = get_formula_from_string("linear-and-quadratic", problem_context) + terms = [str(t) for t in formula] + assert set(terms) == set(["1", "x0", "x1", "x2", "x0**2", "x1**2", "x2**2"]) # fully quadratic - terms = [ - "1", - "x0", - "x1", - "x2", - "x0:x1", - "x0:x2", - "x1:x2", - "x0**2", - "x1**2", - "x2**2", - ] - model_formula = get_formula_from_string( - problem_context=problem_context, model_type="fully-quadratic" + formula = get_formula_from_string("fully-quadratic", problem_context) + terms = [str(t) for t in formula] + assert set(terms) == set( + [ + "1", + "x0", + "x1", + "x2", + "x0:x1", + "x0:x2", + "x1:x2", + "x0**2", + "x1**2", + "x2**2", + ] ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) # custom model - terms_lhs = ["y"] - terms_rhs = ["1", "x0", "x0**2", "x0:x1"] - model_formula = get_formula_from_string( - problem_context=problem_context, - model_type="y ~ 1 + x0 + x0:x1 + {x0**2}", - rhs_only=False, - ) - assert all(term in terms_lhs for term in model_formula.terms.lhs) - assert all(term in model_formula.terms.lhs for term in terms_lhs) - assert all(term in terms_rhs for term in model_formula.terms.rhs) - assert all(term in model_formula.terms.rhs for term in terms_rhs) - - # get formula without model: valid input - model = "x1 + x2 + x3" - model = get_formula_from_string(model_type=model) - assert str(model) == "1 + x1 + x2 + x3" - - # get formula without model: invalid input - with pytest.raises(AssertionError): - model = get_formula_from_string("linear") + formula = get_formula_from_string( + problem_context=problem_context, model_type="y ~ 1 + x0 + x0:x1 + {x0**2}" + ) + terms = [str(t) for t in formula] + assert set(terms) == set(["1", "x0", "x0**2", "x0:x1"]) - # get formula for very large model - model = "" - for i in range(350): - model += f"x{i} + " - model = model[:-3] - model = get_formula_from_string(model_type=model) + # get formula without problem: valid input + formula = get_formula_from_string("x1 + x2 + x3") + terms = [str(t) for t in formula] + assert set(terms) == set(["1", "x1", "x2", "x3"]) - terms = [f"x{i}" for i in range(350)] - terms.append("1") + # get formula without problem: invalid input + with pytest.raises(AssertionError): + model = get_formula_from_string("linear") - for i in range(351): - assert model.terms[i] in terms - assert terms[i] in model.terms + # get formula for very large model (formulaic < 0.5) runs into python's recursion limit + model = " + ".join(["1"] + [f"x{i}" for i in range(350)]) + formula = get_formula_from_string(model_type=model) + assert set(model.split(" + ")) == set([str(t) for t in formula]) def test_formula_from_string_with_categoricals(): - d = 2 - inputs = [opti.Categorical(f"x{i+1}", ["a", "b", "c"]) for i in range(d)] - inputs.append(opti.Continuous(f"x{4}", [0, 1])) problem = opti.Problem( - inputs=inputs, + inputs=[ + opti.Categorical("x1", ["a", "b", "c"]), + opti.Categorical("x2", ["a", "b", "c"]), + opti.Continuous("x4", [0, 1]), + ], outputs=[opti.Continuous("y")], ) + + # without relaxation problem_context = ProblemContext(problem) - model_formula = problem_context.get_formula_from_string(model_type="linear") - # linear and interaction - terms = [ - "1", - "x1", - "x2", - "x4", - ] - model_formula = problem_context.get_formula_from_string(model_type="linear") - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1", - "x2", - "x4", - "x4**2", - ] - model_formula = problem_context.get_formula_from_string( - model_type="linear-and-quadratic" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1", - "x2", - "x4", - "x1:x4", - "x2:x4", - ] - model_formula = problem_context.get_formula_from_string( - model_type="linear-and-interactions" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1", - "x2", - "x4", - "x4**2", - "x1:x4", - "x2:x4", - ] - model_formula = problem_context.get_formula_from_string( - model_type="fully-quadratic" + formula = problem_context.get_formula_from_string("linear") + assert set([str(t) for t in formula]) == set(["1", "x1", "x2", "x4"]) + + formula = problem_context.get_formula_from_string("linear-and-quadratic") + assert set([str(t) for t in formula]) == set(["1", "x1", "x2", "x4", "x4**2"]) + + formula = problem_context.get_formula_from_string("linear-and-interactions") + assert set([str(t) for t in formula]) == set( + ["1", "x1", "x2", "x4", "x1:x4", "x2:x4"] + ) + + formula = problem_context.get_formula_from_string("fully-quadratic") + assert set([str(t) for t in formula]) == set( + ["1", "x1", "x2", "x4", "x4**2", "x1:x4", "x2:x4"] ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) + # with relaxation problem_context = ProblemContext(problem) problem_context.relax_problem() - model_formula = problem_context.get_formula_from_string(model_type="linear") - # linear and interaction - terms = [ - "1", - "x1____a", - "x1____b", - "x1____c", - "x2____a", - "x2____b", - "x2____c", - "x4", - ] - model_formula = problem_context.get_formula_from_string(model_type="linear") - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1____a", - "x1____b", - "x1____c", - "x2____a", - "x2____b", - "x2____c", - "x4", - "x4**2", - ] - model_formula = problem_context.get_formula_from_string( - model_type="linear-and-quadratic" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1____a", - "x1____b", - "x1____c", - "x2____a", - "x2____b", - "x2____c", - "x4", - "x1____a:x4", - "x1____b:x4", - "x1____c:x4", - "x2____a:x4", - "x2____b:x4", - "x2____c:x4", - ] - model_formula = problem_context.get_formula_from_string( - model_type="linear-and-interactions" - ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) - - terms = [ - "1", - "x1____a", - "x1____b", - "x1____c", - "x2____a", - "x2____b", - "x2____c", - "x4", - "x1____a:x4", - "x1____b:x4", - "x1____c:x4", - "x2____a:x4", - "x2____b:x4", - "x2____c:x4", - "x4**2", - ] - model_formula = problem_context.get_formula_from_string( - model_type="fully-quadratic" + formula = problem_context.get_formula_from_string("linear") + assert set([str(t) for t in formula]) == set( + [ + "1", + "x1____a", + "x1____b", + "x1____c", + "x2____a", + "x2____b", + "x2____c", + "x4", + ] + ) + + formula = problem_context.get_formula_from_string("linear-and-quadratic") + assert set([str(t) for t in formula]) == set( + [ + "1", + "x1____a", + "x1____b", + "x1____c", + "x2____a", + "x2____b", + "x2____c", + "x4", + "x4**2", + ] + ) + + formula = problem_context.get_formula_from_string("linear-and-interactions") + assert set([str(t) for t in formula]) == set( + [ + "1", + "x1____a", + "x1____b", + "x1____c", + "x2____a", + "x2____b", + "x2____c", + "x4", + "x1____a:x4", + "x1____b:x4", + "x1____c:x4", + "x2____a:x4", + "x2____b:x4", + "x2____c:x4", + ] + ) + + formula = problem_context.get_formula_from_string("fully-quadratic") + assert set([str(t) for t in formula]) == set( + [ + "1", + "x1____a", + "x1____b", + "x1____c", + "x2____a", + "x2____b", + "x2____c", + "x4", + "x1____a:x4", + "x1____b:x4", + "x1____c:x4", + "x2____a:x4", + "x2____b:x4", + "x2____c:x4", + "x4**2", + ] ) - assert all(term in terms for term in model_formula.terms) - assert all(term in model_formula.terms for term in terms) def test_n_zero_eigvals_unconstrained(): @@ -332,25 +241,17 @@ def test_number_of_model_terms(): problem_context = ProblemContext(problem) - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear" - ) - assert len(formula.terms) == 6 + formula = get_formula_from_string("linear", problem_context) + assert sum(1 for _ in formula) == 6 - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-quadratic" - ) - assert len(formula.terms) == 11 + formula = get_formula_from_string("linear-and-quadratic", problem_context) + assert sum(1 for _ in formula) == 11 - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-interactions" - ) - assert len(formula.terms) == 16 + formula = get_formula_from_string("linear-and-interactions", problem_context) + assert sum(1 for _ in formula) == 16 - formula = get_formula_from_string( - problem_context=problem_context, model_type="fully-quadratic" - ) - assert len(formula.terms) == 21 + formula = get_formula_from_string("fully-quadratic", problem_context) + assert sum(1 for _ in formula) == 21 # 3 continuous & 2 discrete inputs problem = opti.Problem( @@ -366,25 +267,17 @@ def test_number_of_model_terms(): problem_context = ProblemContext(problem) - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear" - ) - assert len(formula.terms) == 6 + formula = get_formula_from_string("linear", problem_context) + assert sum(1 for _ in formula) == 6 - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-quadratic" - ) - assert len(formula.terms) == 11 + formula = get_formula_from_string("linear-and-quadratic", problem_context) + assert sum(1 for _ in formula) == 11 - formula = get_formula_from_string( - problem_context=problem_context, model_type="linear-and-interactions" - ) - assert len(formula.terms) == 16 + formula = get_formula_from_string("linear-and-interactions", problem_context) + assert sum(1 for _ in formula) == 16 - formula = get_formula_from_string( - problem_context=problem_context, model_type="fully-quadratic" - ) - assert len(formula.terms) == 21 + formula = get_formula_from_string("fully-quadratic", problem_context) + assert sum(1 for _ in formula) == 21 def test_constraints_as_scipy_constraints(): From 236c65dd6eafb7d575b5fe2eef0964587b268017 Mon Sep 17 00:00:00 2001 From: WalzDS Date: Sat, 10 Sep 2022 07:41:56 +0200 Subject: [PATCH 3/3] fix jacobian, WIP --- doe/jacobian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doe/jacobian.py b/doe/jacobian.py index aaee718..eba3a7a 100644 --- a/doe/jacobian.py +++ b/doe/jacobian.py @@ -72,7 +72,7 @@ def __init__( self.vars = self.problem.inputs.names self.n_vars = self.problem.n_inputs - self.model_terms = np.array(model.terms, dtype=str) + self.model_terms = np.array([str(t) for t in model], dtype=str) self.n_model_terms = len(self.model_terms) if jacobian_building_block is not None: