Corso
I solver di base e i metodi a tentativi vanno in crisi quando le variabili crescono. Gurobi gestisce queste complessità con velocità e precisione, risolvendo in secondi problemi che altrimenti richiederebbero ore. È la differenza tra sperare in un piano fattibile e dimostrare matematicamente di avere quello ottimale.
Questo tutorial ti guida attraverso il potente solver commerciale noto come Gurobi. Ti mostrerò come installare la libreria Python, formulare il tuo primo modello e passare a problemi avanzati come la programmazione mista intera.
Capire l’ottimizzazione matematica e il ruolo di Gurobi
Prima di scrivere codice, è utile capire cosa significa davvero ottimizzare. Questo contesto ti aiuta a capire quando usare Gurobi invece degli approcci di programmazione standard.
Che cos’è l’ottimizzazione matematica?
L’ottimizzazione matematica trova la migliore soluzione tra tutte le soluzioni possibili. Definisci cosa significa "migliore" (minimizzare i costi, massimizzare i profitti, minimizzare il tempo), cosa controlli (variabili decisionali) e quali limiti hai (vincoli). L’ottimizzatore cerca tra le soluzioni fattibili per trovare quella ottimale.
Questo differisce dalla programmazione tradizionale, in cui specifichi passi esatti. Con l’ottimizzazione descrivi il problema matematicamente e lasci che il solver capisca come risolverlo. Non stai scrivendo logiche "se questo allora quello", stai definendo relazioni e lasciando che gli algoritmi trovino i valori migliori.
L’importanza di Gurobi nell’ottimizzazione
Perché Gurobi si distingue
Gurobi è costantemente tra i più veloci optimizer commerciali disponibili. I benchmark spesso mostrano prestazioni nettamente superiori rispetto alle alternative open source su problemi su larga scala. Questa velocità conta quando esegui ottimizzazioni ogni ora o hai limiti di tempo stringenti.
Adozione nell’industria
Aziende in logistica, finanza, energia e sanità usano Gurobi in produzione. FedEx ottimizza i percorsi di consegna, mentre le società finanziarie allocano miliardi nei portafogli. Le utility energetiche pianificano la generazione. Non sono esercizi accademici, sono sistemi che gestiscono soldi veri e vincoli reali.
Capacità del solver
Gurobi gestisce la programmazione lineare (LP), la programmazione mista intera (MIP), la programmazione quadratica (QP) e altro. Include algoritmi all’avanguardia come l’ottimizzazione concorrente, in cui più metodi di soluzione girano in parallelo e vince il più veloce. Ottieni affidabilità e velocità senza dover scegliere manualmente gli algoritmi.
Configurare Gurobi
Installare Gurobi richiede pochi passaggi. Una volta configurato, funziona senza intoppi con Python.
Installazione e setup
Download e installazione
Vai al sito di Gurobi e scarica l’installer per il tuo sistema operativo. Include l’optimizer, l’interfaccia gurobipy e la documentazione. Esegui l’installer con le impostazioni predefinite, a meno che tu non abbia bisogno di un percorso personalizzato.
Dopo l’installazione, verifica che tutto funzioni:
import gurobipy as gp
print(f"Gurobi version: {gp.__version__}")
Qui verifichiamo l’installazione stampando il numero di versione. Una stampa riuscita conferma che l’ambiente è pronto.

Opzioni di licenza
Gurobi richiede una licenza per tutto ciò che va oltre piccoli problemi di test. Le opzioni:
- Licenza di prova gratuita: valutazione di 30 giorni. Richiedila dal sito di Gurobi con la tua email di lavoro.
- Licenza accademica: gratuita per docenti e studenti. Verifica la tua email accademica per accesso illimitato, ideale per imparare.
- Licenza commerciale: necessaria in ambito business. I prezzi variano in base alla scala. Le licenze nominali sono adatte a singoli data scientist, mentre le floating consentono ai team di condividere la capacità.
- Licenza cloud: pay-as-you-go. Nessun costo iniziale, paghi solo l’uso. Utile per attività di ottimizzazione occasionali.
Uso la licenza accademica per imparare e quella commerciale in produzione. La prova ti dà tempo a sufficienza per esplorare prima di decidere.
Configurazione dell’ambiente
Setup Python
Gurobi funziona con Python 3.7+. Ti consiglio un ambiente virtuale per evitare conflitti:
python -m venv gurobi_env
gurobi_env\Scripts\activate
source gurobi_env/bin/activate
pip install gurobipy
Questi comandi creano uno spazio di lavoro pulito e installano la libreria.
Integrazione con Jupyter Notebook
Per lo sviluppo interattivo, i notebook funzionano alla grande con Gurobi:
pip install jupyter
jupyter notebook
import gurobipy as gp
print("Gurobi ready!")
Questo snippet installa Jupyter, avvia il server ed esegue un rapido check per assicurarsi che Gurobi si carichi correttamente nel notebook.
Quando NON usare Gurobi
Gurobi è potente, ma non sempre è la scelta giusta.
-
Problemi semplici: Se hai 50 variabili e vincoli lineari di base, solver gratuiti come
scipy.optimize.linprogo Excel Solver funzionano bene. -
Costo: Le licenze commerciali sono costose. Per strumenti interni non critici, opzioni open source come CBC o GLPK (via PuLP) possono bastare.
-
Euristiche: Se ti serve una risposta "abbastanza buona" in millisecondi per un gioco in tempo reale, le euristiche (come gli algoritmi genetici) spesso battono i solver esatti.
Usa Gurobi quando i problemi sono grandi (10k+ variabili) o ti servono funzionalità avanzate come vincoli quadratici.
Compatibilità tra piattaforme
Gurobi gira su Windows, macOS e Linux. Ambienti cloud come AWS, Azure e Google Cloud supportano Gurobi tramite i rispettivi marketplace. Ho distribuito modelli Gurobi su tutti e tre i principali cloud senza problemi specifici di piattaforma.
Costruire modelli di ottimizzazione
Installato Gurobi, costruiamo il tuo primo modello. Questi concetti base si applicano a ogni problema che risolverai.
Componenti chiave di un modello di ottimizzazione
Ogni modello ha tre parti essenziali:
Variabili decisionali
Sono le scelte che fa l’ottimizzatore. Nella pianificazione della produzione, potrebbero essere "unità del Prodotto A" e "unità del Prodotto B". Tu definisci le variabili; Gurobi assegna i valori.
Funzione obiettivo
È il tuo traguardo, ciò che vuoi massimizzare o minimizzare. Massimizzare il profitto (3*product_a + 5*product_b) o minimizzare i costi. Un solo obiettivo per modello.
Vincoli
Sono i tuoi limiti. Non puoi produrre oltre la capacità dell’impianto o spendere più del budget. I vincoli definiscono quali soluzioni sono effettivamente possibili.
Buone pratiche di formulazione
Usa nomi di variabili chiari. Invece di x[1], usa produce_product_a. Ti farà risparmiare ore di debug più avanti, come spiegherò nella sezione di troubleshooting.
Mantieni i vincoli semplici. Suddividere logiche complesse in vincoli più piccoli e chiari rende il modello più leggibile e facile da debug.
Gurobi moderno: la Matrix API
Se arrivi dal data science, il modeling a loop potrebbe sembrarti lento. Gurobi 13.0+ offre una Matrix API che usa array NumPy, velocizzando la costruzione dei modelli di 10-50x.
Ecco come appare un modello da panificio usando i vettori:
import gurobipy as gp
import numpy as np
# Data
prices = np.array([2.0, 5.0])
resources = np.array([[0.5, 2.0], [1.0, 1.0]])
limits = np.array([100.0, 80.0])
model = gp.Model("bakery_matrix")
x = model.addMVar(shape=2, name="x")
model.setObjective(prices @ x, gp.GRB.MAXIMIZE)
model.addConstr(resources @ x <= limits, name="constraints")
model.optimize()
Questa sintassi A @ x <= b è più pulita e standard per i sistemi in produzione.
Creare un semplice modello di programmazione lineare
Risolviamo un problema pratico: un panificio decide quante pagnotte e torte sfornare per massimizzare il profitto.
Impostazione del problema:
- Profitto pane: $2 | Farina: 0,5 kg
- Profitto torta: $5 | Farina: 2 kg
- Farina disponibile: 100 kg
- Capacità del forno: 80 pezzi
Ecco il modello:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model("bakery_optimization")
bread = model.addVar(name="bread", vtype=GRB.CONTINUOUS, lb=0)
cake = model.addVar(name="cake", vtype=GRB.CONTINUOUS, lb=0)
model.setObjective(2 * bread + 5 * cake, GRB.MAXIMIZE)
model.addConstr(0.5 * bread + 2 * cake <= 100, name="flour_limit")
model.addConstr(bread + cake <= 80, name="oven_capacity")
model.optimize()
if model.status == GRB.OPTIMAL:
print(f"Optimal profit: ${model.objVal:.2f}")
print(f"Bake {bread.X:.2f} loaves of bread")
print(f"Bake {cake.X:.2f} cakes")
else:
print("No optimal solution found")
Questo codice inizializza il modello, definisce le variabili e imposta l’obiettivo di profitto. Applica i vincoli definiti sopra e risolve il piano ottimale.
Capire i log del solver
Quando esegui model.optimize(), Gurobi stampa un log. Ecco come leggerlo:
Gurobi Optimizer version 13.0.0...
Presolve removed 0 rows and 0 columns
...
Explored 0 nodes (2 simplex iterations) in 0.01 seconds
Optimal objective 2.800000000e+02
- Presolve: Gurobi semplifica il problema prima di risolverlo. "Removed rows" indica vincoli ridondanti trovati.
- Iterazioni simplex: Quanti passi ha fatto l’algoritmo. Numeri alti su problemi piccoli possono indicare problemi di formulazione.
- Obiettivo ottimale: Il risultato finale (ad es., $280 di profitto).
- Gap: Per problemi interi, mostra la differenza tra la miglior soluzione trovata finora e la migliore teorica.
Osservare il log aiuta a capire se un modello è "bloccato" o sta solo lavorando sodo.
I concetti qui si scalano direttamente. Che tu abbia 10 o 10.000 variabili, il pattern resta lo stesso.
Problemi avanzati supportati da Gurobi
La programmazione lineare è solo l’inizio. Gurobi gestisce tipi complessi che catturano le sfumature del mondo reale, incluse le nuove capacità MINLP non convesse nella versione 13.0.
Programmazione lineare mista intera
Cosa la rende diversa
La programmazione mista intera (MIP) consente variabili intere. Non puoi costruire 0,7 stabilimenti, quindi ti servono soluzioni intere.
Applicazioni reali
Localizzazione impianti: Aprire un magazzino nella Città A? Sì (1) o No (0).
Schedulazione della produzione: Le dimensioni dei lotti spesso devono essere numeri interi.
Progettazione di reti: Le decisioni di instradamento sono di solito binarie. Il traffico passa su un collegamento oppure no.
Esempio: localizzazione di impianti
import gurobipy as gp
from gurobipy import GRB
model = gp.Model("facility_location")
cities = ['CityA', 'CityB', 'CityC']
fixed_costs = {'CityA': 10000, 'CityB': 8000, 'CityC': 12000}
open_warehouse = model.addVars(cities, vtype=GRB.BINARY, name="open")
model.setObjective(
gp.quicksum(fixed_costs[city] * open_warehouse[city] for city in cities),
GRB.MINIMIZE
)
model.addConstr(gp.quicksum(open_warehouse[city] for city in cities) >= 2)
model.optimize()
print("Open warehouses in:")
for city in cities:
if open_warehouse[city].X > 0.5:
print(f" {city}")
Qui usiamo variabili binarie per decidere se aprire un magazzino. Il modello minimizza i costi fissi garantendo che restino aperte almeno due strutture.
Programmazione quadratica e non lineare
Programmazione quadratica (QP)
Quando l’obiettivo include termini al quadrato o prodotti di variabili, hai un QP. L’ottimizzazione di portafoglio è un classico perché il rischio (varianza) è quadratico.
Programmazione con vincoli quadratici (QCP)
Anche i vincoli possono essere quadratici. Pensa a problemi che coinvolgono cerchi o frontiere curve.
Esempio: ottimizzazione di portafoglio
import gurobipy as gp
from gurobipy import GRB
import numpy as np
model = gp.Model("portfolio")
assets = ['StockA', 'StockB', 'StockC']
expected_returns = [0.12, 0.18, 0.10]
covariance = np.array([
[0.04, 0.01, 0.02],
[0.01, 0.09, 0.03],
[0.02, 0.03, 0.05]
])
weights = model.addVars(len(assets), name="weight", lb=0, ub=1)
model.setObjective(
gp.quicksum(expected_returns[i] * weights[i] for i in range(len(assets))),
GRB.MAXIMIZE
)
model.addConstr(gp.quicksum(weights[i] for i in range(len(assets))) == 1)
risk_expr = gp.quicksum(
covariance[i][j] * weights[i] * weights[j]
for i in range(len(assets))
for j in range(len(assets))
)
model.addConstr(risk_expr <= 0.05, name="risk_limit")
model.optimize()
print("Optimal portfolio allocation:")
for i, asset in enumerate(assets):
print(f"{asset}: {weights[i].X * 100:.1f}%")
Questa impostazione affronta l’ottimizzazione di portafoglio. Massimizza i rendimenti limitando rigorosamente il rischio totale (varianza) con vincoli quadratici.
Altri tipi di problemi supportati
Second-order cone programming (SOCP)
Problemi con vincoli geometrici, come la definizione di un cono o di un limite circolare, rientrano in questa categoria. Comuni in ottimizzazione robusta e progettazione ingegneristica.
Programmazione quadratica mista intera (MIQP)
Combina variabili intere con obiettivi o vincoli quadratici. Utile nell’ottimizzazione discreta con relazioni non lineari.
Gurobi supporta nativamente tutti questi tipi. Non devi riformulare i problemi per adattarli a un solver specifico: Gurobi rileva automaticamente il tipo e applica gli algoritmi appropriati.

Tipi di problemi di ottimizzazione supportati da Gurobi. Immagine dell’autore.
Algoritmi di Gurobi e loro applicazioni
Selezione dell’algoritmo
Gurobi sceglie automaticamente gli algoritmi, ma conoscere le opzioni aiuta a risolvere rallentamenti.
Valori di Method:
-
-1(default): lascia decidere a Gurobi -
0: Primal simplex (buono per la maggior parte degli LP) -
1: Dual simplex (utile quando si parte da infeasibile) -
2: Barrier (il migliore per modelli enormi o QP) -
3: Concurrent (esegue più metodi, sceglie il più veloce) -
4: Concurrent deterministico -
5: Simplex concorrente deterministico -
PDHG (metodo del primo ordine: Gurobi 13.0 include anche PDHG, un metodo basato su gradienti ideale per LP enormi che sfruttano le GPU.
Controlla quale è stato usato guardando il log del solver (es., "Barrier solved model...").
Quando forzare la scelta: se il default impiega ore, prova Barrier per modelli enormi o Concurrent per problemi imprevedibili.
model.Params.Method = 2
model.Params.BarConvTol = 1e-8
Questo seleziona esplicitamente il metodo Barrier e stringe la tolleranza, utile con dataset enormi o sensibili.
Sfruttare le funzionalità avanzate di Gurobi
Dopo aver padroneggiato le basi, Gurobi offre strumenti potenti per problemi a livello enterprise.
Ottimizzazione distribuita
Per modelli enormi che su una singola macchina impiegano troppo, puoi suddividere il lavoro. L’ottimizzazione distribuita di Gurobi usa più computer per risolvere un singolo problema più velocemente.
Designi una macchina come "manager" e le altre come "worker". Il manager invia parti dell’albero di ricerca ai worker, che le risolvono in parallelo. È eccessivo per modelli piccoli ma essenziale per supply chain globali o scheduling aereo.
Ottimizzazione multi-obiettivo
La vita reale raramente ha un solo obiettivo. Potresti voler massimizzare il profitto e ridurre l’impatto ambientale.
Gurobi lo gestisce permettendoti di impostare priorità. Puoi dire "massimizza prima il profitto, poi minimizza gli sprechi senza ridurre il profitto di oltre il 10%". In alternativa, puoi pesare gli obiettivi: Objective = 0.7 * Profit - 0.3 * Waste.
# Multi-objective example
model.setObjectiveN(profit_expr, index=0, priority=2, name="Profit")
model.setObjectiveN(waste_expr, index=1, priority=1, name="Waste")
Interazione in tempo reale e callback
A volte vuoi intervenire mentre Gurobi sta risolvendo. I callback ti permettono di eseguire codice personalizzato in momenti specifici, ad esempio ogni volta che viene trovata una nuova soluzione.
Uso i callback per fermarmi prima se una soluzione è "abbastanza buona" o per inserire euristiche personalizzate che so funzionare sui miei dati.
Capacità cloud
Eseguire l’ottimizzazione in locale va bene per lo sviluppo, ma la produzione spesso richiede più potenza. Gurobi Cloud ti consente di spostare il carico su AWS, Azure o sull’Instant Cloud di Gurobi. Paghi solo il tempo di solve, perfetto per carichi pesanti ma sporadici.
Buone pratiche di modellazione pratica
Scrivere modelli efficienti richiede strategia. Ecco suggerimenti per mantenere il codice pulito e veloce.
Strategie di formulazione del modello
Parti semplice, aggiungi complessità gradualmente
Non costruire un modello con 50 vincoli il primo giorno. Parti dalla logica centrale, verificala, poi affina. Ho passato troppo tempo a fare debug di modelli complessi per poi scoprire che l’errore stava nelle basi.
Usa nomi significativi per variabili e vincoli
x1 = model.addVar(name="x1")
model.addConstr(x1 + x2 <= 100, name="c1")
units_produced = model.addVar(name="units_produced")
model.addConstr(
units_produced + units_stored <= warehouse_capacity,
name="warehouse_capacity_limit"
)
Nota la differenza: il secondo blocco descrive chiaramente la logica business. Come detto prima, ti evita mal di testa in debug.
Preprocessa i dati
Valida i dati prima di modellare. Controlla valori mancanti o vincoli impossibili. Scoprire errori nei dati dopo 3 ore di solve è doloroso.
Scala correttamente i dati
Gurobi gestisce bene la precisione numerica, ma valori estremi creano problemi. Se i costi sono in milioni e le quantità in millesimi, portali a ordini di grandezza simili:
# Instead of cost = 10000000 and quantity = 0.0001
# Scale to cost = 10 (in millions) and quantity = 100 (in 1/1000 units)
I problemi numerici sono rari in modelli ben scalati, comuni in quelli mal scalati.
Considerazioni sulla stabilità numerica
Gurobi usa aritmetica in virgola mobile, che non è infinita. Se un tuo vincolo dice x <= 1000000000 e la tolleranza è 1e-6, potresti ottenere risultati strani.
Evita i vincoli "Big M" come if y=0 then x <= 1,000,000,000. Scegli invece il più piccolo upper bound valido (ad es., dimensione totale del mercato). Limiti più stretti significano solve più veloci e stabili.
Warm start e modifiche al modello
Raramente risolvi un problema una sola volta. Di solito arrivano nuovi dati (ad es., domanda aggiornata).
Invece di ricostruire il modello da zero, usa un warm start. Fornisci a Gurobi la soluzione precedente come punto di partenza. Poiché il piano ottimale di ieri probabilmente è vicino a quello di oggi, Gurobi può saltare molta parte della ricerca.
# Warm start example
bread.Start = 40 # Start search assuming we bake 40 loaves
Interfacce di programmazione e integrazione
L’API Python di Gurobi
Abbiamo usato gurobipy perché è "pythonic". Supporta l’overloading degli operatori (x + y <= 5) e si integra direttamente con pandas e NumPy. È il modo più popolare per usare Gurobi nel data science.
Integrazione con altri linguaggi
I sistemi di produzione spesso girano in C++, Java o .NET per velocità. Gurobi li supporta tutti. I concetti (Variabili, Vincoli, Obiettivi) sono identici; cambia solo la sintassi.
Per flussi di lavoro cross-language, prototipo spesso in Python (sviluppo rapido) e distribuisco in C++ (esecuzione rapida), usando i formati file di Gurobi (.mps o .lp) per spostare i modelli.
Supporto per linguaggi di modellazione
Gurobi si integra anche con linguaggi di modellazione dedicati come AMPL e GAMS, o persino con Excel.
- AMPL/GAMS: ottimi per ricercatori puramente matematici che non vogliono scrivere codice software.
- Excel: utile per prototipi rapidi o per mostrare ai referenti business un modello "tangibile" senza installare Python.
Ottimizzazione delle prestazioni e tuning
Workflow di tuning
Non fare tuning a caso. Segui questo processo:
-
Misura la baseline: esegui con i default e annota il tempo di solve.
-
Imposta prima i limiti rigidi: i sistemi in produzione hanno bisogno di
TimeLimit. -
Scambia accuratezza con velocità: allenta
MIPGapsolo se il business lo tollera. -
Testa lo scaling dei thread: più thread ≠ più veloce (rendimento decrescente dopo 4-8).
# Example: Production deployment
model.Params.TimeLimit = 300 # Hard 5-min cutoff for API response
baseline_time = model.Runtime
if baseline_time > 240: # If cutting it close
model.Params.MIPGap = 0.02 # Accept 2% suboptimality
# Business rationale: 2% cost increase < late delivery penalty
Benchmark delle prestazioni
I benchmark mostrano che Gurobi supera costantemente i solver open source di ordini di grandezza sui MIP difficili. Mentre scipy gestisce bene piccoli LP, Gurobi brilla quando la complessità cresce. Puoi trovare benchmark comparativi sul sito di Hans Mittelmann, che traccia le prestazioni dei solver in modo indipendente.
Debug e troubleshooting
Infeasibilità del modello
Quando Gurobi segnala "Model is infeasible", i tuoi vincoli sono in conflitto. Nessuna soluzione li soddisfa tutti contemporaneamente.
Trova i vincoli in conflitto:
model.computeIIS()
model.write("infeasible.ilp")
print("Conflicting constraints:")
for c in model.getConstrs():
if c.IISConstr:
print(f" {c.ConstrName}: {c.ConstrExpr}")
Questo calcola l’Irreducible Inconsistent Subsystem (IIS) e stampa esattamente quali vincoli si scontrano.
Modelli illimitati (unbounded)
Un modello illimitato significa che l’obiettivo può migliorare all’infinito. Di solito indica vincoli mancanti.
Verifica:
- Hai limitato tutte le variabili? addVar(lb=0, ub=1000)
- I vincoli sono formulati correttamente?
- La funzione obiettivo è corretta?
Uso dei log del solver
Abilita il logging dettagliato per vedere cosa fa Gurobi:
model.Params.OutputFlag = 1
model.Params.LogToConsole = 1
model.Params.LogFile = "gurobi.log"
Questo abilita l’output verboso e salva una copia su file locale, dandoti una cronologia dettagliata del processo di solve.
Gestione dei dati
Nelle app reali, i dati arrivano da file, non da liste hardcoded.
import pandas as pd
# Load data
df = pd.read_csv("facility_costs.csv")
fixed_costs = dict(zip(df['city'], df['fixed_cost']))
# Validate BEFORE modeling
if any(c < 0 for c in fixed_costs.values()):
raise ValueError("Costs cannot be negative")
Questo pattern, Carica-Valida-Modella, previene errori silenziosi.
Applicazioni reali di Gurobi
La teoria è ottima, ma vediamo come le industrie usano davvero questi strumenti.
Ottimizzazione della supply chain
Progettazione della rete di distribuzione
FedEx, UPS e Amazon usano i concetti del corso Supply Chain Analytics in Python per minimizzare i costi rispettando le garanzie di consegna.
Useremo la Matrix API in questo esempio per gestire la scala in modo efficiente.
import gurobipy as gp
from gurobipy import GRB
import numpy as np
# 1. Data Generation
factories = ["F1", "F2"]
warehouses = ["W1", "W2", "W3"]
customers = ["C1", "C2", "C3", "C4"]
# Cost matrices (Factory->Warehouse, Warehouse->Customer)
transport_fw = np.array([[2.0, 4.0, 5.0], [3.0, 1.0, 6.0]])
transport_wc = np.array([
[1.5, 2.0, 3.0, 4.0],
[3.0, 1.0, 2.0, 2.5],
[5.0, 4.0, 1.0, 1.5]
])
# Capacity and Demand
factory_cap = np.array([1000, 1000])
demand = np.array([300, 500, 400, 600])
try:
with gp.Model("SupplyChain_Matrix") as model:
# 2. Variables (Matrix Form)
# Flow F->W and W->C
flow_fw = model.addMVar((len(factories), len(warehouses)), name="fw")
flow_wc = model.addMVar((len(warehouses), len(customers)), name="wc")
# Binary decision: Open warehouse?
open_w = model.addMVar(len(warehouses), vtype=GRB.BINARY, name="open")
# 3. Objective: Minimize Transport + Fixed Costs
fixed_cost = 5000
obj = (transport_fw * flow_fw).sum() + \
(transport_wc * flow_wc).sum() + \
(fixed_cost * open_w).sum()
model.setObjective(obj, GRB.MINIMIZE)
# 4. Constraints
# Factory capacity (sum rows of flow_fw <= cap)
model.addConstr(flow_fw.sum(axis=1) <= factory_cap, name="Cap")
# Customer demand (sum cols of flow_wc >= demand)
model.addConstr(flow_wc.sum(axis=0) >= demand, name="Demand")
# Flow balance: Inflow to W == Outflow from W
model.addConstr(flow_fw.sum(axis=0) == flow_wc.sum(axis=1), name="Balance")
# Warehouse capacity linking: Flow out <= BigM * OpenBinary
model.addConstr(flow_wc.sum(axis=1) <= 2000 * open_w, name="Link")
model.optimize()
if model.Status == GRB.OPTIMAL:
print(f"Optimal Cost: ${model.ObjVal:,.2f}")
print(f"Open Warehouses: {open_w.X}")
except gp.GurobiError as e:
print(f"Error: {e}")
Questo approccio vettorializzato (addMVar, sum(axis=1)) gestisce migliaia di variabili all’istante, evitando i loop Python lenti.

Grafo della rete di supply chain. Immagine dell’autore.
Ottimizzazione dell’inventario
Bilanciare costi d’ordine, di giacenza e di carenza è complesso. Con domanda incerta, diventa ottimizzazione stocastica, che Gurobi gestisce con la modellazione a scenari.
Schedulazione della forza lavoro
Pianificazione dei turni
Ospedali e call center devono programmare il personale rispettando fabbisogni, regole sul lavoro e preferenze.
Questo è puro mixed-integer programming. Ogni assegnazione è una scelta binaria. Un ospedale con 100 infermieri e 50 turni ha 5.000 variabili da gestire.
Esempio: schedulazione infermieri
import gurobipy as gp
from gurobipy import GRB
model = gp.Model("nurse_scheduling")
nurses = ['Alice', 'Bob', 'Carol', 'Dave']
shifts = ['Mon_Day', 'Mon_Night', 'Tue_Day', 'Tue_Night']
min_required = {'Mon_Day': 2, 'Mon_Night': 1, 'Tue_Day': 2, 'Tue_Night': 1}
schedule = model.addVars(nurses, shifts, vtype=GRB.BINARY, name="schedule")
model.setObjective(
gp.quicksum(schedule[n, s] for n in nurses for s in shifts),
GRB.MINIMIZE
)
for shift in shifts:
model.addConstr(
gp.quicksum(schedule[n, shift] for n in nurses) >= min_required[shift],
name=f"min_staff_{shift}"
)
for nurse in nurses:
model.addConstr(
gp.quicksum(schedule[nurse, s] for s in shifts) <= 3,
name=f"max_shifts_{nurse}"
)
for nurse in nurses:
# Labor rule: 8-hour rest required between shifts
model.addConstr(
schedule[nurse, 'Mon_Day'] + schedule[nurse, 'Mon_Night'] <= 1,
name=f"no_double_{nurse}_Mon"
)
model.addConstr(
schedule[nurse, 'Tue_Day'] + schedule[nurse, 'Tue_Night'] <= 1,
name=f"no_double_{nurse}_Tue"
)
model.optimize()
print("Optimal schedule:")
for nurse in nurses:
assigned = [s for s in shifts if schedule[nurse, s].X > 0.5]
print(f"{nurse}: {', '.join(assigned)}")
Questo modella il problema di schedulazione con variabili binarie. Minimizza i turni totali rispettando fabbisogni, ore massime e periodi di riposo obbligatori.
Ottimizzazione di portafoglio finanziario
Trade-off rischio-rendimento
L’ottimizzazione di portafoglio bilancia rendimenti attesi e rischio. Il classico modello di Markowitz:
- Massimizza: rendimento atteso
- Soggetto a: rischio (varianza) sotto soglia
- Vincoli: vincoli di budget, requisiti di diversificazione, limiti regolamentari
Questa è programmazione quadratica. Il rendimento è lineare nei pesi di portafoglio, ma la varianza è quadratica (coinvolge prodotti tra pesi).
Considerazioni pratiche
L’ottimizzazione reale include:
- Costi di transazione (comprare/vendere comporta commissioni)
- Vincoli interi (non puoi comprare frazioni di alcune azioni)
- Requisiti normativi (non superare il 10% in un singolo titolo)
- Vincoli di ribilanciamento (non stravolgere troppo il portafoglio esistente)
I gestori patrimoniali eseguono queste ottimizzazioni quotidianamente, gestendo miliardi di capitale. Il tempo di solve impatta direttamente l’esecuzione delle strategie di trading.
Conclusione
Abbiamo coperto molto, dall’installazione di Gurobi alla costruzione di modelli complessi di supply chain.
Ora, invece di discutere opinioni in sala riunioni, puoi mettere il codice sullo schermo. "Ecco il piano matematicamente ottimale". Questo cambia la conversazione. Smetti di indovinare e inizi a dimostrare.
Provalo sul tuo prossimo problema spinoso, quel conflitto di pianificazione o il budget stretto che stai evitando. Potresti scoprire che la risposta perfetta era nascosta nei tuoi dati.
E se vuoi affinare ancora le tue skill di ottimizzazione, dai un’occhiata al nostro tutorial Introduzione alla Programmazione Lineare e ai corsi Introduzione all’Ottimizzazione in Python per continuare a costruire competenze.
Sono un data engineer e community builder: lavoro su pipeline dati, cloud e strumenti di AI, e scrivo tutorial pratici e ad alto impatto per DataCamp e per sviluppatori alle prime armi.
FAQ su Gurobi
Qual è la differenza tra Gurobi Optimizer e gurobipy?
Il Gurobi Optimizer è il motore computazionale che risolve i problemi matematici. gurobipy è la libreria Python che usi per costruire modelli e comunicare con quel motore. Scrivi codice in gurobipy e lui invia il problema all’Optimizer per trovare la soluzione.
Mi servono matematiche avanzate per usare bene Gurobi?
Non proprio. Se capisci l’algebra di base (tipo 2x + y <= 10), sei a posto per la maggior parte dei problemi. Lo strumento gestisce per te gli algoritmi complessi. Devi solo saper descrivere logicamente il tuo problema.
Posso usare Gurobi con altri linguaggi oltre a Python?
Sì! Anche se Python è la scelta più popolare nel data science, Gurobi ha API robuste anche per C++, Java, .NET e MATLAB. Integra persino R per workflow di analisi statistica.
Cosa succede se il mio modello impiega troppo tempo a risolversi?
Non serve sempre la risposta "perfetta". Puoi impostare un "MIPGap" per fermare il solver quando trova una soluzione entro l’1% o il 5% dell’ottimo. Nelle decisioni reali, una buona risposta adesso è spesso meglio di una perfetta domani.
Gurobi è difficile da mantenere in produzione?
In realtà è abbastanza stabile. La vera sfida non è il software, sono i dati. Se i dati in input cambiano formato o contengono errori, il tuo modello può rompersi. Pipeline di validazione dei dati solide sono più critiche del codice Gurobi in sé.
Posso visualizzare facilmente i risultati?
Gurobi produce numeri, ma dato che si integra bene con Python, puoi passare i risultati direttamente a librerie come Matplotlib, Seaborn o Plotly. Puoi trasformare i risultati in Gantt o mappe di rete in poche righe di codice.


