-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/master'
- Loading branch information
Showing
20 changed files
with
1,333 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,358 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Constraint Classes\n", | ||
"\n", | ||
"To make applying constraints to your model easier, some constraints have been \n", | ||
"provided as a part of ENTMOOT." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from entmoot.problem_config import ProblemConfig\n", | ||
"from entmoot.models.enting import Enting\n", | ||
"from entmoot.optimizers.pyomo_opt import PyomoOptimizer" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### NChooseKConstraint\n", | ||
"\n", | ||
"This constraint is often used in the design of experiments. This applies a bound on the \n", | ||
"number of non-zero variables." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from entmoot.benchmarks import build_reals_only_problem, eval_reals_only_testfunc\n", | ||
"\n", | ||
"# standard setting up of problem\n", | ||
"problem_config = ProblemConfig(rnd_seed=73)\n", | ||
"build_reals_only_problem(problem_config)\n", | ||
"rnd_sample = problem_config.get_rnd_sample_list(num_samples=50)\n", | ||
"testfunc_evals = eval_reals_only_testfunc(rnd_sample)\n", | ||
"\n", | ||
"params = {\"unc_params\": {\"dist_metric\": \"l1\", \"acq_sense\": \"penalty\"}}\n", | ||
"enting = Enting(problem_config, params=params)\n", | ||
"# fit tree ensemble\n", | ||
"enting.fit(rnd_sample, testfunc_evals)\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Set parameter Username\n", | ||
"Academic license - for non-commercial use only - expires 2024-09-06\n", | ||
"Read LP format model from file C:\\Users\\tobyb\\AppData\\Local\\Temp\\tmpzrofd3mo.pyomo.lp\n", | ||
"Reading time = 0.02 seconds\n", | ||
"x1: 2774 rows, 1913 columns, 9130 nonzeros\n", | ||
"Gurobi Optimizer version 10.0.2 build v10.0.2rc0 (win64)\n", | ||
"\n", | ||
"CPU model: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, instruction set [SSE2|AVX|AVX2|AVX512]\n", | ||
"Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n", | ||
"\n", | ||
"Optimize a model with 2774 rows, 1913 columns and 9130 nonzeros\n", | ||
"Model fingerprint: 0x31e842ff\n", | ||
"Variable types: 1292 continuous, 621 integer (621 binary)\n", | ||
"Coefficient statistics:\n", | ||
" Matrix range [1e-06, 1e+06]\n", | ||
" Objective range [1e+00, 2e+00]\n", | ||
" Bounds range [1e+00, 5e+00]\n", | ||
" RHS range [1e-04, 5e+00]\n", | ||
"Presolve removed 273 rows and 260 columns\n", | ||
"Presolve time: 0.05s\n", | ||
"Presolved: 2501 rows, 1653 columns, 8080 nonzeros\n", | ||
"Variable types: 1282 continuous, 371 integer (371 binary)\n", | ||
"Found heuristic solution: objective 24.8382603\n", | ||
"Found heuristic solution: objective 19.1248452\n", | ||
"\n", | ||
"Root relaxation: objective 2.576224e+00, 577 iterations, 0.01 seconds (0.01 work units)\n", | ||
"\n", | ||
" Nodes | Current Node | Objective Bounds | Work\n", | ||
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", | ||
"\n", | ||
" 0 0 2.57622 0 18 19.12485 2.57622 86.5% - 0s\n", | ||
"H 0 0 3.6397521 2.57622 29.2% - 0s\n", | ||
"H 0 0 3.3869028 3.24087 4.31% - 0s\n", | ||
" 0 0 3.38690 0 6 3.38690 3.38690 0.00% - 0s\n", | ||
"\n", | ||
"Cutting planes:\n", | ||
" Gomory: 4\n", | ||
" Cover: 83\n", | ||
" Implied bound: 402\n", | ||
" Clique: 185\n", | ||
" MIR: 4\n", | ||
" Flow cover: 13\n", | ||
" Network: 8\n", | ||
" RLT: 30\n", | ||
" Relax-and-lift: 98\n", | ||
" PSD: 4\n", | ||
"\n", | ||
"Explored 1 nodes (877 simplex iterations) in 0.14 seconds (0.13 work units)\n", | ||
"Thread count was 8 (of 8 available processors)\n", | ||
"\n", | ||
"Solution count 4: 3.3869 3.63975 19.1248 24.8383 \n", | ||
"\n", | ||
"Optimal solution found (tolerance 1.00e-04)\n", | ||
"Best objective 3.386902819809e+00, best bound 3.386902819809e+00, gap 0.0000%\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"from entmoot.constraints import NChooseKConstraint\n", | ||
"model_pyo = problem_config.get_pyomo_model_core()\n", | ||
"\n", | ||
"# define the constraint\n", | ||
"# then immediately apply it to the model\n", | ||
"model_pyo.nchoosek = NChooseKConstraint(\n", | ||
" feature_keys=[\"x1\", \"x2\", \"x3\", \"x4\", \"x5\"], \n", | ||
" min_count=1,\n", | ||
" max_count=3,\n", | ||
" none_also_valid=True\n", | ||
").as_pyomo_constraint(model_pyo, problem_config.feat_list)\n", | ||
"\n", | ||
"\n", | ||
"# optimise the model\n", | ||
"params_pyomo = {\"solver_name\": \"gurobi\"}\n", | ||
"opt_pyo = PyomoOptimizer(problem_config, params=params_pyomo)\n", | ||
"res_pyo = opt_pyo.solve(enting, model_core=model_pyo)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 4, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"[0.0, 0.0, 4.088297585401641, 4.888952150927435, 4.944564863420855]\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"print(res_pyo.opt_point)\n", | ||
"assert 1 <= sum(x > 1e-6 for x in res_pyo.opt_point) <= 3" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Defining your own constraint\n", | ||
"\n", | ||
"We have provided some constraints already as a part of ENTMOOT. If these do not \n", | ||
"fit your needs, then you can define your own!\n", | ||
"\n", | ||
"The easiest approach is to subclass ExpressionConstraint, and define some custom expression\n", | ||
"that is a function of the variables. From that, you should be able to use the constraint \n", | ||
"as shown above. This needs to return a pyomo.Expression object. If you need to do \n", | ||
"a more involved procedure that modifies the model, you can use a FunctionalConstraint \n", | ||
"instead (see NChooseKConstraint)." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 5, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from entmoot.constraints import ExpressionConstraint\n", | ||
"\n", | ||
"class SumLessThanTen(ExpressionConstraint):\n", | ||
" \"\"\"A constraint that enforces selected features to sum to less than ten.\"\"\"\n", | ||
" def _get_expr(self, features):\n", | ||
" return sum(features) <= 10" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Constraint Lists\n", | ||
"\n", | ||
"For a problem definition, it may be easier to define a set of constraints." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"problem_config = ProblemConfig(rnd_seed=73)\n", | ||
"build_reals_only_problem(problem_config)\n", | ||
"rnd_sample = problem_config.get_rnd_sample_list(num_samples=50)\n", | ||
"testfunc_evals = eval_reals_only_testfunc(rnd_sample)\n", | ||
"\n", | ||
"params = {\"unc_params\": {\"dist_metric\": \"l1\", \"acq_sense\": \"penalty\"}}\n", | ||
"enting = Enting(problem_config, params=params)\n", | ||
"# fit tree ensemble\n", | ||
"enting.fit(rnd_sample, testfunc_evals)\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 10, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from entmoot.constraints import LinearInequalityConstraint, ConstraintList\n", | ||
"import pyomo.environ as pyo\n", | ||
"model_pyo = problem_config.get_pyomo_model_core()\n", | ||
"\n", | ||
"# define the constraint\n", | ||
"# then immediately apply it to the model\n", | ||
"constraints = [\n", | ||
" NChooseKConstraint(\n", | ||
" feature_keys=[\"x1\", \"x2\", \"x3\", \"x4\", \"x5\"], \n", | ||
" min_count=1,\n", | ||
" max_count=4,\n", | ||
" none_also_valid=True\n", | ||
" ),\n", | ||
" LinearInequalityConstraint(\n", | ||
" feature_keys=[\"x3\", \"x4\", \"x5\"],\n", | ||
" coefficients=[1, 1, 1],\n", | ||
" rhs=12.0\n", | ||
" )\n", | ||
"]\n", | ||
"\n", | ||
"model_pyo.problem_constraints = pyo.ConstraintList()\n", | ||
"ConstraintList(constraints).apply_pyomo_constraints(\n", | ||
" model_pyo, problem_config.feat_list, model_pyo.problem_constraints\n", | ||
")\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 12, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Set parameter Username\n", | ||
"Academic license - for non-commercial use only - expires 2024-09-06\n", | ||
"Read LP format model from file C:\\Users\\tobyb\\AppData\\Local\\Temp\\tmp0tfjrq6i.pyomo.lp\n", | ||
"Reading time = 0.01 seconds\n", | ||
"x1: 2775 rows, 1913 columns, 9133 nonzeros\n", | ||
"Gurobi Optimizer version 10.0.2 build v10.0.2rc0 (win64)\n", | ||
"\n", | ||
"CPU model: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, instruction set [SSE2|AVX|AVX2|AVX512]\n", | ||
"Thread count: 4 physical cores, 8 logical processors, using up to 8 threads\n", | ||
"\n", | ||
"Optimize a model with 2775 rows, 1913 columns and 9133 nonzeros\n", | ||
"Model fingerprint: 0x8879e895\n", | ||
"Variable types: 1292 continuous, 621 integer (621 binary)\n", | ||
"Coefficient statistics:\n", | ||
" Matrix range [1e-06, 1e+06]\n", | ||
" Objective range [1e+00, 2e+00]\n", | ||
" Bounds range [1e+00, 5e+00]\n", | ||
" RHS range [1e-04, 1e+01]\n", | ||
"Presolve removed 273 rows and 260 columns\n", | ||
"Presolve time: 0.05s\n", | ||
"Presolved: 2502 rows, 1653 columns, 8084 nonzeros\n", | ||
"Variable types: 1282 continuous, 371 integer (371 binary)\n", | ||
"Found heuristic solution: objective 10.1607516\n", | ||
"\n", | ||
"Root relaxation: objective 2.501750e+00, 463 iterations, 0.00 seconds (0.00 work units)\n", | ||
"\n", | ||
" Nodes | Current Node | Objective Bounds | Work\n", | ||
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", | ||
"\n", | ||
" 0 0 2.50175 0 11 10.16075 2.50175 75.4% - 0s\n", | ||
"H 0 0 10.1529199 2.50175 75.4% - 0s\n", | ||
"H 0 0 2.8865055 2.50175 13.3% - 0s\n", | ||
" 0 0 2.60484 0 3 2.88651 2.60484 9.76% - 0s\n", | ||
" 0 0 2.60484 0 6 2.88651 2.60484 9.76% - 0s\n", | ||
" 0 0 2.60484 0 5 2.88651 2.60484 9.76% - 0s\n", | ||
"H 0 0 2.8764756 2.60484 9.44% - 0s\n", | ||
" 0 0 2.69557 0 11 2.87648 2.69557 6.29% - 0s\n", | ||
"H 0 0 2.8510016 2.69557 5.45% - 0s\n", | ||
"* 0 0 0 2.8510016 2.85100 0.00% - 0s\n", | ||
"\n", | ||
"Cutting planes:\n", | ||
" Cover: 1\n", | ||
" Clique: 5\n", | ||
" RLT: 1\n", | ||
" Relax-and-lift: 6\n", | ||
"\n", | ||
"Explored 1 nodes (786 simplex iterations) in 0.16 seconds (0.12 work units)\n", | ||
"Thread count was 8 (of 8 available processors)\n", | ||
"\n", | ||
"Solution count 5: 2.851 2.87648 2.88651 ... 10.1608\n", | ||
"\n", | ||
"Optimal solution found (tolerance 1.00e-04)\n", | ||
"Best objective 2.851001621749e+00, best bound 2.851001621749e+00, gap 0.0000%\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"# optimise the model\n", | ||
"params_pyomo = {\"solver_name\": \"gurobi\"}\n", | ||
"opt_pyo = PyomoOptimizer(problem_config, params=params_pyomo)\n", | ||
"res_pyo = opt_pyo.solve(enting, model_core=model_pyo)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 13, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"[0.0, 2.43082, 3.2917799999999997, 4.888964463647816, 3.6007]\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"print(res_pyo.opt_point)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "enttest", | ||
"language": "python", | ||
"name": "enttest" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.9.9" | ||
}, | ||
"orig_nbformat": 4 | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |
Oops, something went wrong.