')"
]
},
{
"cell_type": "markdown",
"id": "fb0f3edb",
"metadata": {},
"source": [
"##### MILPRouter"
]
},
{
"cell_type": "markdown",
"id": "5040c87d",
"metadata": {},
"source": [
"MILP reports a bound and an optimality gap for its length model; routing detours can increase the delivered length. See [Exact optimization](../routers.md#exact-optimization)."
]
},
{
"cell_type": "markdown",
"id": "f421ae7d",
"metadata": {},
"source": [
"> Note:\n",
">\n",
"> - [MILPRouter](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.MILPRouter) warm-starts by default: it reuses the solution already stored in the [WindFarmNetwork](../autoapi/optiwindnet/api/index.rst#optiwindnet.api.WindFarmNetwork), and builds one with a fast heuristic when there is none or the stored one does not fit the model — see [Warm-starting](../routers.md#warm-starting).\n",
"> - To solve cold instead, pass `warmup=False`; `warmup_time` is the budget for building a warm start.\n",
"> - Set `verbose=True` to see warm-start messages in the logs."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "64e22c2f-1339-48b6-86a8-635874e8b382",
"metadata": {},
"outputs": [],
"source": [
"res_milp = wfn.optimize(router=milp_router)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "c4d1f9b9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'router': 'MILPRouter',\n",
" 'capacity': 8,\n",
" 'solver_name': 'ortools.cp_sat',\n",
" 'mip_gap': 0.005,\n",
" 'time_limit': 20,\n",
" 'topology': ,\n",
" 'feeder_route': ,\n",
" 'balanced': False,\n",
" 'runtime': 20.038915,\n",
" 'bound': 229556.31575552459,\n",
" 'objective': 240847.58781760116,\n",
" 'relgap': 0.04688139982795958,\n",
" 'termination': 'FEASIBLE'}"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn.solution_info()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "acd659f9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('MILPRouter', 'branched', '240998.6')"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results.append(\n",
" (\n",
" wfn.solution_info()['router'],\n",
" wfn.G.graph['topology'].value,\n",
" f'{wfn.length():.1f}',\n",
" )\n",
")\n",
"results[-1]"
]
},
{
"cell_type": "markdown",
"id": "28e8ad3e",
"metadata": {},
"source": [
"The two length values describe different stages of the solution. `wfn.solution_info()['objective']` is the length optimized by the MILP on the available-links graph, before route reconstruction. `wfn.length()` sums the physical cable routes after [PathFinder](../autoapi/optiwindnet/pathfinding/index.rst#optiwindnet.pathfinding.PathFinder) introduces any detours required by the navigation mesh. The routed length can therefore be slightly longer. Use `objective` to interpret the solver's bound and relative gap, and `wfn.length()` for the final installed cable length. If cable prices are supplied, `wfn.cost()` reports the cost of the routed network. Length and cost often track each other, but the MILP gap is a guarantee about modeled length, not a bound on cost suboptimality. See [Objective and reported cost](../problem.md#objective-and-reported-cost)."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "1569125c",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n"
],
"text/plain": [
""
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wfn"
]
},
{
"cell_type": "markdown",
"id": "6a7dda5e",
"metadata": {},
"source": [
"Compare the results of the three routers:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "28225f11",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"