Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
spiralulam committed Nov 2, 2023
2 parents 5537627 + 64927b8 commit 1b54203
Show file tree
Hide file tree
Showing 20 changed files with 1,333 additions and 240 deletions.
358 changes: 358 additions & 0 deletions docs/notebooks/constraint_classes.ipynb
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
}
Loading

0 comments on commit 1b54203

Please sign in to comment.