# -*- coding: utf-8 -*-
"""
Created on Tue Oct 28 19:02:32 2025

@author: RaquelMonteirodeNobr
"""


#importa todas as funções do PuLP
from pulp import *


import networkx as nx
import numpy as np
import random

#criação da instância

#conj das cidades
cidades = [0, "A", "B", "C", "D", "E"]

#matriz de custos
matriz_custos = [[0,28,31,20,25,34],
                 [28,0,21,29,26,20],
                 [31,21,0,38,20,32],
                 [20,29,38,0,30,27],
                 [25,26,20,30,0,25],
                 [34,20,32,27,25,0]]

procura = [0, 37, 35, 30, 25, 32]

Q = 100
K = 2

D = 0 #procura total
for i in procura:
    D = D + i

#correspondência entre os valores do vetor
#cidades e a matriz de custos
custos = makeDict([cidades, cidades],
                  matriz_custos)

d = makeDict ([cidades], procura)


n = len(cidades)

#estrutura que ignora entrada (i,i)
arcos = [(i,j) for i in cidades 
         for j in cidades if i!=j]

#fim de criação da instância

#início da criação do modelo

#variavel que guarda a formulação para o problema que queremos resolver
#recebe o nome do problems e o objetivo
formulacaoSCF = LpProblem("Formulacao SCF para VRP", LpMinimize)

#criar as variáveis
#LpVariable.dicts(nome variavel, conj. onde esta definida, limiteinferior, limite superior, tipo)
x = LpVariable.dicts("x", (cidades, cidades), 0, 1, LpInteger)

f = LpVariable.dicts("f", (cidades, cidades), 0, Q, LpContinuous)


#adicionar as restrições
#----a primeira a adicionar é a FO
formulacaoSCF += (lpSum([x[i][j]*custos[i][j] for (i,j) in arcos]),
                  "FO",
                  )

#----adicionar as restrições
#sai K arcos do depósito
formulacaoSCF += (
        lpSum([x[cidades[0]][j] for j in cidades
               if (cidades[0],j) in arcos]) == K,
        f"sai_K_arcos_deposito",
        )

for i in cidades:
    if i != cidades[0]: #deposito na primeira pos. do vetor cidades
        formulacaoSCF += (
            lpSum([x[i][j] for j in cidades if (i,j) in arcos]) == 1,
            f"sai_{i}",
            )
    
        formulacaoSCF += (
            lpSum([x[j][i] for j in cidades if (j,i) in arcos]) == 1,
            f"entra_{i}",
            )
    
#sai D de fluxo do deposito
formulacaoSCF += (
    lpSum([f[cidades[0]][j] for j in cidades 
           if (cidades[0], j) in arcos]) == D,
          f"fluxo_sai_deposito",
    )

#conservação fluxo
for i in cidades:
    if i != cidades[0]:
        formulacaoSCF += (
            lpSum([f[j][i] for j in cidades if (j,i) in arcos]) == 
            lpSum([f[i][j] for j in cidades if (i,j) in arcos]) + d[i],
            f"conservacao_fluxo_{i}",
            )
        
#relação f com x   
for (i,j) in arcos:
    formulacaoSCF += (f[i][j] <= (Q-d[i])*x[i][j],
            f"relacao_f_x_ub_{i}_{j}",
            )
    
for (i,j) in arcos:
    formulacaoSCF += (d[j]*x[i][j] <= f[i][j],
            f"relacao_f_x_lb_{i}_{j}",
            )
        
        
#extrair o modelo para um modelo para um ficheiro
formulacaoSCF.writeLP("SCF.lp")

#resolve o modelo
formulacaoSCF.solve()

#estado da resolução
print("Estado:", LpStatus[formulacaoSCF.status])

#valor otimo
print("Custo total = ", value(formulacaoSCF.objective))

#escrever a solução
for v in formulacaoSCF.variables():
    if v.varValue > 0:
        print(v.name, "=", v.varValue)


# Create a graph representing the TSP tour
G = nx.DiGraph()
G.add_nodes_from(cidades)
 
graph_edges = [(i,j) for (i, j) in arcos if x[i][j].varValue == 1]
G.add_edges_from(graph_edges)
 
# Create a dictionary to store the edge values (weights) based on x[i][j].varValue
edge_values = {(i, j): x[i][j].varValue for (i, j) in arcos if x[i][j].varValue == 1}
 
 
# Draw the graph with nodes and edges
pos = nx.spring_layout(G)  # Positioning of nodes
nx.draw(G, pos, with_labels=True, node_color="red", node_size=2500)
 
# Draw the edge labels using the x[i][j].varValue values
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_values, font_color='black')
 
#output diferente


