Initial project import
This commit is contained in:
60
src/planejador_missao/optimizer.py
Normal file
60
src/planejador_missao/optimizer.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Modelo MILP para escolher a combinacao final de escalas."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.optimize import Bounds, LinearConstraint, milp
|
||||
|
||||
|
||||
def resolver_milp(colunas: pd.DataFrame, rotas: pd.DataFrame, aeronaves: pd.DataFrame) -> pd.DataFrame:
|
||||
if colunas.empty:
|
||||
return colunas
|
||||
|
||||
n = len(colunas)
|
||||
integrality = np.ones(n)
|
||||
bounds = Bounds(np.zeros(n), np.ones(n))
|
||||
restricoes = []
|
||||
|
||||
# R1 - Cada tripulante aparece no maximo uma vez.
|
||||
for trip in sorted(set(colunas["trip1"]).union(set(colunas["trip2"]))):
|
||||
linha = np.zeros(n)
|
||||
linha[(colunas["trip1"] == trip) | (colunas["trip2"] == trip)] = 1
|
||||
restricoes.append(LinearConstraint(linha, -np.inf, 1))
|
||||
|
||||
livres = aeronaves[(aeronaves["disponivel_sede"]) & (~aeronaves["em_pane"]) & (~aeronaves["em_missao_rota"])]["aeronave"].unique()
|
||||
for av in livres:
|
||||
idx_rota = (colunas["aeronave"] == av) & (colunas["tipo_escala"] == "ROTA_ACIONADA")
|
||||
idx_local = (colunas["aeronave"] == av) & (colunas["tipo_escala"] == "MISSAO_LOCAL")
|
||||
idx_sbv = (colunas["aeronave"] == av) & (colunas["tipo_escala"] == "SBV")
|
||||
if not (idx_rota | idx_sbv).any():
|
||||
raise RuntimeError(f"Modelo inviavel: nao ha rota nem SBV candidato para a aeronave {av}.")
|
||||
linha = np.zeros(n)
|
||||
linha[idx_rota | idx_sbv] = 1
|
||||
restricoes.append(LinearConstraint(linha, 1, 1))
|
||||
if idx_rota.any():
|
||||
linha = np.zeros(n)
|
||||
linha[idx_rota] = 1
|
||||
restricoes.append(LinearConstraint(linha, -np.inf, 1))
|
||||
if idx_local.any():
|
||||
linha = np.zeros(n)
|
||||
linha[idx_local] = 1
|
||||
restricoes.append(LinearConstraint(linha, -np.inf, 1))
|
||||
if idx_rota.any() and idx_local.any():
|
||||
linha = np.zeros(n)
|
||||
linha[idx_rota | idx_local] = 1
|
||||
restricoes.append(LinearConstraint(linha, -np.inf, 1))
|
||||
|
||||
for rota_id in range(1, len(rotas) + 1):
|
||||
idx = (colunas["tipo_escala"] == "ROTA_ACIONADA") & (colunas["rota_id"] == rota_id)
|
||||
if not idx.any():
|
||||
raise RuntimeError(f"Modelo inviavel: nao ha coluna candidata para a rota acionada {rota_id}.")
|
||||
linha = np.zeros(n)
|
||||
linha[idx] = 1
|
||||
restricoes.append(LinearConstraint(linha, 1, 1))
|
||||
|
||||
resultado = milp(c=-colunas["score_milp"].to_numpy(dtype=float), integrality=integrality, bounds=bounds, constraints=restricoes)
|
||||
if not resultado.success:
|
||||
raise RuntimeError(f"MILP nao encontrou solucao viavel: {resultado.message}")
|
||||
selecionadas = resultado.x > 0.5
|
||||
return colunas.loc[selecionadas].reset_index(drop=True)
|
||||
Reference in New Issue
Block a user