Files
Escalante-Pastor/scripts/05_modelo_milp.py
2026-06-26 18:15:55 -03:00

59 lines
2.2 KiB
Python

"""Modelo MILP do Planejador Missão usando scipy.optimize.milp/HiGHS.
Entradas:
Colunas candidatas viáveis geradas por 04_gerar_colunas_candidatas.py.
Saidas:
Subconjunto de colunas selecionadas pelo solver, representando a escala
diaria otimizada.
Metodologia:
Variavel de decisao: x_j em {0,1}, onde cada j representa uma coluna
candidata. Como scipy.optimize.milp minimiza, o modelo minimiza -score_milp,
equivalente a maximizar o score operacional total.
Restricoes implementadas no motor src.planejador_missao.optimizer:
a) rota acionada obrigatoria coberta exatamente uma vez;
b) cada tripulante aparece no máximo uma vez no dia;
c) cada aeronave recebe cobertura principal por rota acionada ou SBV;
d) aeronave não executa escalas conflitantes de rota/local no mesmo dia;
e) qualificacao, indisponibilidade e regras de instrucao entram no filtro de
colunas candidatas, logo colunas inviáveis não chegam ao MILP;
f) regras específicas de rota, missão local e SBV são preservadas por tipo.
"""
from __future__ import annotations
import importlib
gerador = importlib.import_module("04_gerar_colunas_candidatas")
io_utils = importlib.import_module("02_io_utils")
preparar = importlib.import_module("03_preparar_dados")
def resolver_escala(colunas, rotas, aeronaves):
"""Resolve o MILP e retorna as colunas escolhidas."""
from src.planejador_missao.optimizer import resolver_milp
if colunas.empty:
raise RuntimeError("Erro: não há colunas candidatas viáveis para montar o MILP.")
# O resolvedor cria x_j binario para cada linha de colunas.
# A função objetivo usa -score_milp porque o HiGHS via SciPy resolve
# minimizacao; isso preserva a interpretacao de maximizar score.
return resolver_milp(colunas, rotas, aeronaves)
def main() -> None:
"""Executa o MILP em modo diagnostico."""
logger = io_utils.configurar_logger("modelo_milp")
bases = preparar.carregar_bases()
colunas = gerador.gerar_colunas_do_dia(bases)
solucao = resolver_escala(colunas, bases["rotas"], bases["aeronaves"])
logger.info("Colunas candidatas: %s", len(colunas))
logger.info("Escalas selecionadas: %s", len(solucao))
if __name__ == "__main__":
main()