Skip to content

Commit

Permalink
Merge pull request #41 from TobyBoyne/typing
Browse files Browse the repository at this point in the history
Improve type hinting throughout ENTMOOT
  • Loading branch information
TobyBoyne authored Jul 3, 2024
2 parents 1ae5042 + b1f7269 commit 835694e
Show file tree
Hide file tree
Showing 28 changed files with 552 additions and 292 deletions.
166 changes: 155 additions & 11 deletions docs/notebooks/multi_obj_with_constraints.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "4c8da843-670c-4f07-bd66-471ec19d3601",
"metadata": {
"tags": []
Expand All @@ -38,7 +38,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"id": "311c88fa-f757-44f3-8ae5-555f715fc1b4",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -77,10 +77,31 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 4,
"id": "c0dc70de-f14f-42a4-9202-0e4777d33bec",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter WLSAccessID\n",
"Set parameter WLSSecret\n",
"Set parameter LicenseID to value 2512524\n",
"Academic license 2512524 - for non-commercial use only - registered to [email protected]\n"
]
},
{
"data": {
"text/plain": [
"<gurobi.Constr *Awaiting Model Update*>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get optimization model\n",
"model_gur = problem_config.get_gurobi_model_core()\n",
Expand All @@ -103,7 +124,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 5,
"id": "61a8e860-92ee-4535-ad73-9b2acf132ad8",
"metadata": {},
"outputs": [],
Expand All @@ -121,7 +142,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 6,
"id": "15bc6c5d-011f-4c57-8af0-4da9704af41e",
"metadata": {},
"outputs": [],
Expand All @@ -133,20 +154,120 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 7,
"id": "e987e159-d855-4476-b7f8-3a041ab45d5f",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter MIPGap to value 0\n",
"Set parameter NonConvex to value 2\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (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",
"Academic license 2512524 - for non-commercial use only - registered to [email protected]\n",
"Optimize a model with 3172 rows, 1828 columns and 11438 nonzeros\n",
"Model fingerprint: 0xa6d043a3\n",
"Model has 100 SOS constraints\n",
"Variable types: 1803 continuous, 25 integer (24 binary)\n",
"Coefficient statistics:\n",
" Matrix range [5e-08, 2e+04]\n",
" Objective range [1e+00, 2e+00]\n",
" Bounds range [1e+00, 6e+00]\n",
" RHS range [2e-04, 8e+03]\n",
"Presolve removed 367 rows and 248 columns\n",
"Presolve time: 0.12s\n",
"Presolved: 2805 rows, 1580 columns, 9988 nonzeros\n",
"Presolved model has 94 SOS constraint(s)\n",
"Variable types: 1557 continuous, 23 integer (23 binary)\n",
"\n",
"Root relaxation: unbounded, 647 iterations, 0.04 seconds (0.02 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 postponed 0 - - - - 0s\n",
" 0 0 postponed 0 - - - - 0s\n",
" 0 2 postponed 0 - - - - 0s\n",
"H 32 25 -0.2625355 - - 421 1s\n",
"H 34 25 -0.3766411 - - 397 1s\n",
"\n",
"Explored 145 nodes (22173 simplex iterations) in 1.69 seconds (1.08 work units)\n",
"Thread count was 8 (of 8 available processors)\n",
"\n",
"Solution count 2: -0.376641 -0.262536 \n",
"\n",
"Optimal solution found (tolerance 0.00e+00)\n",
"Best objective -3.766411305508e-01, best bound -3.766411305508e-01, gap 0.0000%\n"
]
}
],
"source": [
"res_gur = opt_gur.solve(enting, model_core=model_gur)"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 8,
"id": "4ae2f026-dded-4376-866b-6ca34adc5bf9",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter MIPGap to value 0\n",
"Set parameter NonConvex to value 2\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (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",
"Academic license 2512524 - for non-commercial use only - registered to [email protected]\n",
"Optimize a model with 3172 rows, 1828 columns and 11438 nonzeros\n",
"Model fingerprint: 0x7f777cf7\n",
"Model has 100 SOS constraints\n",
"Variable types: 1803 continuous, 25 integer (24 binary)\n",
"Coefficient statistics:\n",
" Matrix range [5e-08, 2e+04]\n",
" Objective range [1e+00, 2e+00]\n",
" Bounds range [1e+00, 6e+00]\n",
" RHS range [2e-04, 8e+03]\n",
"Presolve removed 367 rows and 248 columns\n",
"Presolve time: 0.07s\n",
"Presolved: 2805 rows, 1580 columns, 9984 nonzeros\n",
"Presolved model has 94 SOS constraint(s)\n",
"Variable types: 1557 continuous, 23 integer (23 binary)\n",
"\n",
"Root relaxation: unbounded, 862 iterations, 0.06 seconds (0.05 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 postponed 0 - - - - 0s\n",
" 0 0 postponed 0 - - - - 0s\n",
" 0 2 postponed 0 - - - - 0s\n",
"H 35 31 -0.1298538 - - 261 0s\n",
"H 42 31 -0.3409819 - - 218 0s\n",
"H 72 30 -0.3462866 - - 138 1s\n",
"H 110 18 -0.3557482 - - 138 1s\n",
"H 125 16 -0.4010824 - - 146 1s\n",
"H 144 7 -0.4035719 -0.79918 98.0% 144 1s\n",
"\n",
"Explored 167 nodes (25805 simplex iterations) in 1.47 seconds (1.03 work units)\n",
"Thread count was 8 (of 8 available processors)\n",
"\n",
"Solution count 6: -0.403572 -0.401082 -0.355748 ... -0.129854\n",
"\n",
"Optimal solution found (tolerance 0.00e+00)\n",
"Best objective -4.035719394214e-01, best bound -4.035719394214e-01, gap 0.0000%\n"
]
}
],
"source": [
"# Build GurobiOptimizer object and solve optimization problem\n",
"params_gurobi = {\"MIPGap\": 0}\n",
Expand All @@ -158,6 +279,29 @@
"assert round(x_opt, 5) == round(y_opt, 5) and round(y_opt, 5) == round(z_opt, 5)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "f6e5a95f",
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "list index out of range",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[14], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m al \u001b[38;5;241m=\u001b[39m opt_gur\u001b[38;5;241m.\u001b[39m_active_leaves\n\u001b[0;32m 2\u001b[0m \u001b[38;5;66;03m# list[tuple[int, str]]\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[43mal\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m]\u001b[49m\n",
"\u001b[1;31mIndexError\u001b[0m: list index out of range"
]
}
],
"source": [
"al = opt_gur._active_leaves\n",
"# list[tuple[int, str]]\n"
]
},
{
"cell_type": "markdown",
"id": "24b3d335-d601-41c1-ad46-d80949c0cfcc",
Expand Down Expand Up @@ -228,7 +372,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
"version": "3.10.11"
}
},
"nbformat": 4,
Expand Down
42 changes: 36 additions & 6 deletions docs/notebooks/single_obj_maximisation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -21,7 +21,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -39,7 +39,7 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -58,16 +58,46 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 10,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING: The gurobipy module (an optional Pyomo dependency) failed to import:\n",
"NameError: name 'GurobiDirect' is not defined\n"
]
},
{
"ename": "ApplicationError",
"evalue": "No Python bindings available for <class 'pyomo.solvers.plugins.solvers.gurobi_direct.GurobiDirect'> solver plugin",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mApplicationError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[10], line 4\u001b[0m\n\u001b[0;32m 1\u001b[0m params_pyomo \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msolver_name\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgurobi_direct\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[0;32m 2\u001b[0m opt_pyo \u001b[38;5;241m=\u001b[39m PyomoOptimizer(problem_config, params\u001b[38;5;241m=\u001b[39mparams_pyomo)\n\u001b[1;32m----> 4\u001b[0m res_pyo \u001b[38;5;241m=\u001b[39m \u001b[43mopt_pyo\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msolve\u001b[49m\u001b[43m(\u001b[49m\u001b[43menting\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 5\u001b[0m res_pyo\n",
"File \u001b[1;32m~\\phd\\entmoot\\entmoot\\optimizers\\pyomo_opt.py:104\u001b[0m, in \u001b[0;36mPyomoOptimizer.solve\u001b[1;34m(self, tree_model, model_core, weights)\u001b[0m\n\u001b[0;32m 102\u001b[0m \u001b[38;5;66;03m# Solve optimization model\u001b[39;00m\n\u001b[0;32m 103\u001b[0m verbose \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_params\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mverbose\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[1;32m--> 104\u001b[0m \u001b[43mopt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msolve\u001b[49m\u001b[43m(\u001b[49m\u001b[43mopt_model\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtee\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverbose\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 106\u001b[0m \u001b[38;5;66;03m# update current solution\u001b[39;00m\n\u001b[0;32m 107\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_curr_sol, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_active_leaves \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_sol(opt_model)\n",
"File \u001b[1;32mc:\\Users\\tobyb\\phd\\phdvenv\\lib\\site-packages\\pyomo\\solvers\\plugins\\solvers\\direct_solver.py:75\u001b[0m, in \u001b[0;36mDirectSolver.solve\u001b[1;34m(self, *args, **kwds)\u001b[0m\n\u001b[0;32m 72\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msolve\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwds):\n\u001b[0;32m 73\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Solve the problem\"\"\"\u001b[39;00m\n\u001b[1;32m---> 75\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mavailable\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexception_flag\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[0;32m 76\u001b[0m \u001b[38;5;66;03m#\u001b[39;00m\n\u001b[0;32m 77\u001b[0m \u001b[38;5;66;03m# If the inputs are models, then validate that they have been\u001b[39;00m\n\u001b[0;32m 78\u001b[0m \u001b[38;5;66;03m# constructed! Collect suffix names to try and import from solution.\u001b[39;00m\n\u001b[0;32m 79\u001b[0m \u001b[38;5;66;03m#\u001b[39;00m\n\u001b[0;32m 80\u001b[0m _model \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n",
"File \u001b[1;32mc:\\Users\\tobyb\\phd\\phdvenv\\lib\\site-packages\\pyomo\\solvers\\plugins\\solvers\\gurobi_direct.py:215\u001b[0m, in \u001b[0;36mGurobiDirect.available\u001b[1;34m(self, exception_flag)\u001b[0m\n\u001b[0;32m 213\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m exception_flag:\n\u001b[0;32m 214\u001b[0m gurobipy\u001b[38;5;241m.\u001b[39mlog_import_warning(logger\u001b[38;5;241m=\u001b[39m\u001b[38;5;18m__name__\u001b[39m)\n\u001b[1;32m--> 215\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ApplicationError(\n\u001b[0;32m 216\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo Python bindings available for \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m solver plugin\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m (\u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m),)\n\u001b[0;32m 217\u001b[0m )\n\u001b[0;32m 218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m 220\u001b[0m \u001b[38;5;66;03m# Ensure environment is started to check for a valid license\u001b[39;00m\n",
"\u001b[1;31mApplicationError\u001b[0m: No Python bindings available for <class 'pyomo.solvers.plugins.solvers.gurobi_direct.GurobiDirect'> solver plugin"
]
}
],
"source": [
"params_pyomo = {\"solver_name\": \"gurobi\"}\n",
"params_pyomo = {\"solver_name\": \"gurobi_direct\"}\n",
"opt_pyo = PyomoOptimizer(problem_config, params=params_pyomo)\n",
"\n",
"res_pyo = opt_pyo.solve(enting)\n",
"res_pyo"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand Down
7 changes: 6 additions & 1 deletion entmoot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from entmoot.problem_config import ProblemConfig
from entmoot.models.enting import Enting
from entmoot.models.model_params import (
EntingParams,
TrainParams,
TreeTrainParams,
UncParams,
)
from entmoot.optimizers.gurobi_opt import GurobiOptimizer
from entmoot.optimizers.pyomo_opt import PyomoOptimizer
from entmoot.models.model_params import EntingParams, UncParams, TreeTrainParams, TrainParams
8 changes: 5 additions & 3 deletions entmoot/benchmarks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from typing import Sequence

import numpy as np
from numpy.typing import ArrayLike
from typing import Iterable

from entmoot import ProblemConfig


Expand Down Expand Up @@ -38,7 +40,7 @@ def eval_small_single_obj_cat_testfunc(X: ArrayLike, no_cat=False) -> np.ndarray
# without the dtype=object paramer, each entry of X is converted into a string
X = np.array(X, dtype=object)

def compute_objectives(xi: Iterable, no_cat=False):
def compute_objectives(xi: Sequence, no_cat=False):
if no_cat:
return (
xi[1] * xi[2] * np.sin(sum(xi[3:]))
Expand Down Expand Up @@ -110,7 +112,7 @@ def eval_multi_obj_cat_testfunc(
# without the dtype=object paramer, each entry of X is converted into a string
X = np.array(X, dtype=object)

def compute_objectives(xi: Iterable, no_cat=False):
def compute_objectives(xi: Sequence, no_cat=False):
if no_cat:
return (
xi[1] * xi[2] * np.sin(sum(xi[3:]))
Expand Down
2 changes: 1 addition & 1 deletion entmoot/constraints.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import TYPE_CHECKING, Callable
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Callable

import pyomo.environ as pyo

Expand Down
Loading

0 comments on commit 835694e

Please sign in to comment.