Ga naar hoofdinhoud

Gurobi: Wiskundige optimalisatie voor complexe problemen

Beheers Gurobi-optimalisatie van setup tot toepassingen in de praktijk. Leer modellen bouwen, solvers configureren en lineaire, integer- en niet-lineaire problemen efficiënt aanpakken.
· 14 min lezen

Verken met AI

Basale solvers en trial-and-error-methoden lopen vast zodra het aantal variabelen toeneemt. Gurobi gaat razendsnel en nauwkeurig met die complexiteit om en lost in seconden op wat anders uren kan duren. Dat is het verschil tussen hopen op een haalbaar plan en wiskundig bewijzen dat je het optimale plan hebt.

Deze tutorial gidst je door de krachtige commerciële solver genaamd Gurobi. Ik laat je zien hoe je de Python-bibliotheek installeert, je eerste model opstelt en opschaalt naar geavanceerde problemen zoals mixed-integer programming.

Wiskundige optimalisatie en Gurobi's rol begrijpen

Voor je code schrijft is het handig te snappen wat optimalisatie écht betekent. Deze context laat zien wanneer je Gurobi gebruikt in plaats van standaard programmeerbenaderingen.

Wat is wiskundige optimalisatie?

Wiskundige optimalisatie zoekt de beste oplossing uit alle mogelijke oplossingen. Jij bepaalt wat "beste" betekent (kosten minimaliseren, winst maximaliseren, tijd minimaliseren), wat je kunt sturen (beslissingsvariabelen) en welke grenzen er zijn (restricties). De optimizer doorzoekt de haalbare oplossingen om de optimale te vinden.

Dit verschilt van traditioneel programmeren, waarbij je exacte stappen specificeert. Bij optimalisatie beschrijf je het probleem wiskundig en laat je de solver bepalen hoe het op te lossen. Je schrijft geen "als dit dan dat"-logica, je definieert relaties en laat algoritmen de beste waarden vinden.

Gurobi's betekenis binnen optimalisatie

Waarom Gurobi eruit springt

Gurobi behoort consequent tot de snelste commerciële optimizers. Benchmarks laten vaak zien dat het open-source alternatieven ruimschoots verslaat bij grootschalige problemen. Die snelheid telt als je elk uur optimaliseert of binnen strikte tijdslimieten resultaat nodig hebt.

Adoptie in de industrie

Bedrijven in logistiek, finance, energie en zorg gebruiken Gurobi in productieomgevingen. FedEx optimaliseert bezorgroutes, terwijl financiële partijen miljarden alloceren in portefeuilles. Energiebedrijven plannen stroomopwekking. Dit zijn geen academische oefeningen, maar systemen die echt geld en echte beperkingen afhandelen.

Solver-capaciteiten

Gurobi ondersteunt lineaire programmering (LP), mixed-integer programming (MIP), quadratische programmering (QP) en meer. Het bevat state-of-the-art algoritmen zoals concurrent optimization, waarbij meerdere oplossingsmethoden parallel draaien en de snelste wint. Je krijgt betrouwbaarheid en snelheid zonder handmatig algoritmen te kiezen.

Gurobi instellen

Gurobi installeren kost maar een paar stappen. Eenmaal geconfigureerd werkt het naadloos met Python.

Installatie en setup

Downloaden en installeren

Ga naar Gurobi's website en download de installer voor je besturingssysteem. Deze bevat de optimizer, de gurobipy interface en documentatie. Voer de installer uit met de standaardinstellingen, tenzij je een aangepast pad nodig hebt.

Controleer na installatie of alles werkt:

import gurobipy as gp
print(f"Gurobi version: {gp.__version__}")

Hier verifiëren we de installatie door het versienummer af te drukken. Een succesvolle print bevestigt dat je omgeving klaar is.

Jupyter Notebook-cel met Python-code om gurobipy te importeren en de geïnstalleerde versie af te drukken, met als output Gurobi version 13.0.0.

Licentieopties

Gurobi vereist een licentie voor alles buiten kleine testproblemen. Je opties:

  • Gratis proeflicentie: 30 dagen evaluatie. Vraag deze aan via Gurobi's website met je werkmail.
  • Academische licentie: Gratis voor docenten en studenten. Verifieer je academische e-mail voor onbeperkte toegang, ideaal om te leren.
  • Commerciële licentie: Vereist voor zakelijke inzet. Prijzen variëren naar schaal. Named-user-licenties zijn geschikt voor individuele data scientists, terwijl floating-licenties teams capaciteit laten delen.
  • Cloud-licentie: Pay-as-you-go. Geen opstartkosten, je betaalt wat je gebruikt. Handig voor incidentele optimalisatietaken.

Ik gebruik de academische licentie om te leren en de commerciële voor productie. De proefperiode geeft je genoeg tijd om te verkennen voordat je beslist.

Omgevingsconfiguratie

Python-setup

Gurobi werkt met Python 3.7+. Ik raad een virtual environment aan om conflicten te vermijden:

python -m venv gurobi_env
gurobi_env\Scripts\activate
source gurobi_env/bin/activate
pip install gurobipy

Deze commando's zetten een schone werkomgeving op en installeren de bibliotheek.

Jupyter Notebook-integratie

Voor interactieve ontwikkeling werken notebooks uitstekend met Gurobi:

pip install jupyter
jupyter notebook
import gurobipy as gp
print("Gurobi ready!")

Deze snippet installeert Jupyter, start de server en voert een snelle check uit om te zien of Gurobi binnen de notebook correct laadt.

Wanneer je Gurobi NIET moet gebruiken

Gurobi is krachtig, maar niet altijd de juiste keuze.

  • Eenvoudige problemen: Heb je 50 variabelen en basis lineaire restricties, dan zijn gratis solvers zoals scipy.optimize.linprog of Excel Solver prima.

  • Kosten: Commerciële licenties zijn prijzig. Voor niet-kritische interne tools kunnen open-source opties zoals CBC of GLPK (via PuLP) volstaan.

  • Heuristieken: Heb je in milliseconden een "goed genoeg" antwoord nodig voor een realtime game, dan winnen heuristieken (zoals genetische algoritmen) vaak van exacte solvers.

Gebruik Gurobi bij grote problemen (10k+ variabelen) of als je geavanceerde features zoals quadratische restricties nodig hebt.

Platformcompatibiliteit

Gurobi draait op Windows, macOS en Linux. Cloudomgevingen zoals AWS, Azure en Google Cloud ondersteunen Gurobi via hun marketplaces. Ik heb Gurobi-modellen op alle drie de grote clouds ingezet zonder platformspecifieke issues.

Optimalisatiemodellen bouwen

Met Gurobi geïnstalleerd, bouwen we je eerste model. Deze kernconcepten gelden voor elk probleem dat je oplost.

Belangrijkste componenten van een optimalisatiemodel

Elk model heeft drie essentiële onderdelen:

Beslissingsvariabelen

Dit zijn de keuzes die de optimizer maakt. In productieplanning kan dat zijn: "aantal stuks van Product A" en "aantal stuks van Product B". Jij definieert de variabelen; Gurobi kent de waarden toe.

Doelfunctie

Dit is je doel: wat je wilt maximaliseren of minimaliseren. Maximaliseer winst (3*product_a + 5*product_b) of minimaliseer kosten. Je hebt één doel per model.

Restricties

Dit zijn je grenzen. Je kunt niet meer produceren dan de fabriek aankan of meer uitgeven dan je budget. Restricties bepalen welke oplossingen echt mogelijk zijn.

Best practices voor formuleren

Gebruik duidelijke variabelnamen. In plaats van x[1], gebruik produce_product_a. Dat scheelt uren debuggen later, zoals ik in de troubleshooting-sectie uitleg.

Houd restricties simpel. Complexe logica opsplitsen in kleinere, eenvoudigere restricties maakt je model leesbaarder en makkelijker te debuggen.

Moderne Gurobi: de Matrix-API

Kom je uit data science, dan voelt modelleren met lussen misschien traag. Gurobi 13.0+ biedt een Matrix-API die NumPy-arrays gebruikt, waardoor modelbouw 10-50x sneller is.

Zo ziet een bakkerijmodel eruit met vectoren:

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()

Deze A @ x <= b-syntaxis is schoner en standaard voor productie.

Een eenvoudig lineair programmeringsmodel maken

Laten we een praktisch probleem oplossen: een bakkerij die beslist hoeveel broden en taarten te bakken om de winst te maximaliseren.

Probleemopzet:

- Winst brood: $2 | Bloem: 0,5 kg

- Winst taart: $5 | Bloem: 2 kg

- Bloem beschikbaar: 100 kg

- Ovencapaciteit: 80 items

Hier is het model:

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")

Deze code initialiseert het model, definieert variabelen en stelt het winstdoel in. Het past de restricties toe die we hierboven hebben gedefinieerd en lost het optimale plan op.

Solverlogs begrijpen

Als je model.optimize() draait, print Gurobi een log. Zo lees je die:

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 vereenvoudigt je probleem vóór het oplossen. "Removed rows" betekent dat het redundante restricties vond.
  • Simplex-iteraties: Hoeveel stappen het algoritme nam. Hoge aantallen bij kleine problemen kunnen wijzen op formulatiefouten.
  • Optimale doelwaarde: Je eindresultaat (bijv. $280 winst).
  • Gap: Bij integerproblemen toont dit het verschil tussen de beste tot nu toe gevonden oplossing en de theoretisch beste.

Het log volgen helpt je te zien of een model "vastzit" of gewoon hard werkt.

De concepten schalen rechtstreeks. Of je nu 10 of 10.000 variabelen hebt, het patroon blijft hetzelfde.

Geavanceerde probleemtypen die Gurobi ondersteunt

Lineaire programmering is pas het begin. Gurobi ondersteunt complexe typen die nuances uit de praktijk vangen, waaronder de nieuwe Non-Convex MINLP-mogelijkheden in versie 13.0.

Mixed-integer lineaire programmering

Wat het anders maakt

Mixed-integer programming (MIP) staat toe dat variabelen gehele waarden aannemen. Je kunt geen 0,7 fabriek bouwen, dus heb je integeroplossingen nodig.

Toepassingen in de praktijk

Locatiekeuze faciliteiten: Open je een magazijn in Stad A? Ja (1) of Nee (0).

Productieplanning: Batchgroottes moeten vaak hele aantallen zijn.

Netwerkontwerp: Routeringsbeslissingen zijn meestal binair. Verkeer gaat door een link of niet.

Voorbeeld: locatiekeuze faciliteiten

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}")

Hier gebruiken we binaire variabelen om te beslissen of we een magazijn openen. Het model minimaliseert vaste kosten en zorgt ervoor dat er minstens twee faciliteiten open zijn.

Quadratische en niet-lineaire programmering

Quadratische programmering (QP)

Als je doel kwadratische termen of producten van variabelen bevat, heb je een QP. Portefeuille-optimalisatie is een klassiek voorbeeld, omdat risico (variantie) kwadratisch is.

Quadratisch begrensde programmering (QCP)

Restricties kunnen ook kwadratisch zijn. Denk aan problemen met cirkels of gebogen grenzen.

Voorbeeld: portefeuille-optimalisatie

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}%")

Deze opzet pakt portefeuille-optimalisatie aan. Het maximaliseert rendement terwijl het totale risico (variantie) strikt wordt begrensd met kwadratische restricties.

Overige ondersteunde probleemtypen

Second-order cone programming (SOCP)

Problemen met geometrische restricties, zoals het definiëren van een kegel of cirkelgrens, vallen in deze categorie. Veelvoorkomend in robuuste optimalisatie en engineeringontwerp.

Mixed-integer quadratische programmering (MIQP)

Combineert integer-variabelen met quadratische doelen of restricties. Nuttig bij discrete optimalisatie met niet-lineaire relaties.

Gurobi ondersteunt al deze typen native. Je hoeft problemen niet te herformuleren voor een specifieke solver; Gurobi detecteert het probleemtype automatisch en past de juiste algoritmen toe.

Stroomschema van Gurobi-optimalisatietypen: Lineair (LP), Integer (MIP) en Niet-lineair (QP, MINLP).

Optimalisatieprobleemtypen die door Gurobi worden ondersteund. Afbeelding door de auteur.

Gurobi-algoritmen en hun toepassingen

Algoritmeselectie

Gurobi kiest algoritmen automatisch, maar de opties kennen helpt bij het troubleshooten van langzame solves.

Method-waarden:

  • -1 (standaard): Laat Gurobi kiezen

  • 0: Primal simplex (goed voor de meeste LP's)

  • 1: Dual simplex (goed bij starten vanuit infeasibiliteit)

  • 2: Barrier (beste voor zeer grote modellen of QP)

  • 3: Concurrent (draait meerdere methoden, kiest de snelste)

  • 4: Deterministic Concurrent

  • 5: Deterministic Concurrent Simplex

  • PDHG (First-Order Method: Gurobi 13.0 bevat ook PDHG, een gradiëntmethode ideaal voor enorme LP's die op GPU's past.

Bekijk in het solverlog welke methode draaide (bijv. "Barrier solved model...").

Wanneer je moet overrulen: Als de standaard uren duurt, probeer Barrier voor enorme modellen of Concurrent voor onvoorspelbare problemen.

model.Params.Method = 2
model.Params.BarConvTol = 1e-8

Dit selecteert expliciet de Barrier-methode en verstrakt de tolerantie, nuttig bij enorme of gevoelige datasets.

Gebruikmaken van Gurobi's geavanceerde features

Als je de basis beheerst, biedt Gurobi krachtige tools voor enterprise-schaal problemen.

Distributed optimization

Voor gigantische modellen die te lang duren op één machine kun je het werk splitsen. Gurobi's distributed optimization gebruikt meerdere computers om één probleem sneller op te lossen.

Je wijst één machine aan als "manager" en andere als "workers". De manager stuurt delen van de zoekboom naar workers, die ze parallel oplossen. Overkill voor kleine modellen, maar essentieel voor wereldwijde supply chains of planningen bij luchtvaartmaatschappijen.

Multi-objective optimalisatie

In het echte leven is er zelden maar één doel. Je wilt bijvoorbeeld winst maximaliseren en milieuschade minimaliseren.

Gurobi kan dit aan door prioriteiten toe te kennen. Je kunt aangeven: "maximaliseer eerst winst, minimaliseer daarna afval zonder de winst meer dan 10% te verlagen." Of je weegt ze: 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")

Realtime interactie en callbacks

Soms wil je ingrijpen terwijl Gurobi oplost. Callbacks laten je eigen code draaien op specifieke momenten, bijvoorbeeld telkens als er een nieuwe oplossing is.

Ik gebruik callbacks om eerder te stoppen als een oplossing "goed genoeg" is of om heuristieken in te brengen waarvan ik weet dat ze voor mijn data werken.

Cloudmogelijkheden

Optimalisatie draaien op je laptop is prima voor ontwikkeling, maar productie heeft vaak meer power nodig. Met Gurobi Cloud kun je het zware werk verplaatsen naar AWS, Azure of Gurobi's eigen Instant Cloud. Je betaalt alleen voor de oplostijd, perfect voor sporadische, zware workloads.

Praktische best practices voor modelleren

Efficiënte modellen schrijven vergt strategie. Hier zijn tips om je code schoon en snel te houden.

Strategieën voor modelformulering

Begin simpel, voeg geleidelijk complexiteit toe

Bouw niet op dag één een model met 50 restricties. Begin met de kernlogica, verifieer die, verfijn daarna. Ik heb te vaak complex gedebugd om te ontdekken dat de fout in de basis zat.

Gebruik betekenisvolle namen voor variabelen en restricties

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"
)

Zie het verschil: het tweede blok beschrijft duidelijk de businesslogica. Zoals eerder genoemd scheelt dit debughoofdpijn.

Preprocess je data

Valideer je data vóór het modelleren. Controleer op missende waarden of onmogelijke restricties. Datenfouten vinden na een solve van 3 uur is pijnlijk.

Schaal je data passend

Gurobi gaat goed om met numerieke precisie, maar extreme waarden geven problemen. Als kosten in miljoenen zijn en hoeveelheden in duizendsten, schaal ze naar vergelijkbare ordes van grootte:

# In plaats van cost = 10000000 en quantity = 0.0001
# Schaal naar cost = 10 (in miljoenen) en quantity = 100 (in 1/1000-eenheden)

Numerieke issues zijn zeldzaam in goed geschaalde modellen, en veelvoorkomend in slecht geschaalde.

Numerieke stabiliteit

Gurobi gebruikt floating-point-rekenen, wat niet oneindig precies is. Als je restrictie zegt x <= 1000000000 en je tolerantie is 1e-6, kun je rare resultaten krijgen.

Vermijd "Big M"-restricties zoals if y=0 then x <= 1,000,000,000. Kies in plaats daarvan de kleinst geldige bovengrens (bijv. totale marktomvang). Strakkere grenzen betekenen snellere, stabielere solves.

Warm starts en modelwijzigingen

Je lost zelden een probleem maar één keer op. Meestal komen er nieuwe data binnen (bijv. geüpdatete vraag).

In plaats van het model from scratch op te bouwen, gebruik een warm start. Je geeft Gurobi de vorige oplossing als startpunt. Omdat het optimale plan van gisteren waarschijnlijk dicht bij dat van vandaag ligt, kan Gurobi veel zoekwerk overslaan.

# Warm start example
bread.Start = 40  # Begin de zoekactie met de aanname dat we 40 broden bakken

Programmeerkoppelingen en integratie

Gurobi's Python-API

We gebruikten gurobipy omdat het Pythonic is. Het ondersteunt operator overloading (x + y <= 5) en integreert direct met pandas en NumPy. Het is de populairste manier om Gurobi in data science te gebruiken.

Integratie met andere talen

Productiesystemen draaien vaak op C++, Java of .NET voor snelheid. Gurobi ondersteunt ze allemaal. De concepten (Variabelen, Restricties, Doelen) zijn identiek; alleen de syntaxis verschilt.

Voor cross-language workflows prototyping ik vaak in Python (snel coderen) en deploy ik in C++ (snelle uitvoering), waarbij ik Gurobi's bestandsformaten (.mps of .lp) gebruik om modellen uit te wisselen.

Ondersteuning voor modelleertalen

Gurobi koppelt ook aan dedicated modelleertalen zoals AMPL en GAMS, of zelfs Excel.

- AMPL/GAMS: Ideaal voor puur wiskundige onderzoekers die geen softwarecode willen schrijven.

- Excel: Handig voor snelle prototypes of om business-stakeholders een model te tonen dat ze kunnen gebruiken zonder Python te installeren.

Prestatie-optimalisatie en tuning

Tuningworkflow

Tune niet lukraak. Volg dit proces:

  1. Meet baseline: Draai met defaults, noteer oplostijd.

  2. Zet eerst harde limieten: Productiesystemen hebben TimeLimit nodig.

  3. Ruil nauwkeurigheid voor snelheid: Versoepel MIPGap alleen als het zakelijk kan.

  4. Test thread-schaal: Meer threads ≠ sneller (afnemend rendement na 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

Prestatiebenchmarks

Benchmarks tonen dat Gurobi open-source solvers consequent met orders van grootte overtreft op lastige MIP-problemen. Terwijl scipy kleine LP's prima aankan, schittert Gurobi wanneer de complexiteit toeneemt. Je vindt vergelijkende benchmarks op de website van Hans Mittelmann, die solverprestaties onafhankelijk bijhoudt.

Debugging en troubleshooting

Modelinfeasibiliteit

Als Gurobi meldt "Model is infeasible", botsen je restricties. Er bestaat geen oplossing die ze allemaal tegelijk respecteert.

Vind de conflicterende restricties:

model.computeIIS()
model.write("infeasible.ilp")

print("Conflicting constraints:")
for c in model.getConstrs():
    if c.IISConstr:
        print(f"  {c.ConstrName}: {c.ConstrExpr}")

Dit berekent het Irreducible Inconsistent Subsystem (IIS) en print precies welke restricties elkaar tegenspreken.

Onbegrensde modellen

Een onbegrensd model betekent dat de doelwaarde oneindig kan verbeteren. Dit wijst meestal op ontbrekende restricties.

Check:

- Heb je alle variabelen begrensd? addVar(lb=0, ub=1000)

- Zijn restricties correct geformuleerd?

- Is de doelfunctie correct?

Solverlogs gebruiken

Zet uitgebreide logging aan om te zien wat Gurobi doet:

model.Params.OutputFlag = 1
model.Params.LogToConsole = 1
model.Params.LogFile = "gurobi.log"

Dit schakelt verbose output in en slaat een kopie lokaal op, zodat je een gedetailleerde solvegeschiedenis hebt.

Data verwerken

In echte apps komen data uit bestanden, niet uit hardgecodeerde lijsten.

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")

Dit patroon, Load-Validate-Model, voorkomt stille fouten.

Toepassingen van Gurobi in de praktijk

Theorie is mooi, maar zo gebruiken sectoren dit echt.

Supply chain-optimalisatie

Ontwerp van distributienetwerken

FedEx, UPS en Amazon gebruiken de concepten uit de cursus Supply Chain Analytics in Python om kosten te minimaliseren en toch levergaranties te halen.

Voor dit voorbeeld gebruiken we de Matrix-API om de schaal efficiënt aan te kunnen.

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}")

Deze gevectoriseerde aanpak (addMVar, sum(axis=1)) verwerkt duizenden variabelen direct en vermijdt trage Python-lussen.

Netwerkgrafiek die een supply chain-optimalisatiemodel toont met stromen van Fabrieken (rood) naar Distributiecentra (blauw) en vervolgens naar Klanten (groen).

Supply chain-netwerkgrafiek. Afbeelding door de auteur.

Voorraadoptimalisatie

Balans vinden tussen bestel-, voorraad- en tekortkosten is lastig. Met onzekere vraag wordt dit stochastische optimalisatie, wat Gurobi via scenariomodellering aankan.

Personeelsroostering

Dienstroosterplanning

Ziekenhuizen en callcenters moeten medewerkers inplannen met respect voor bezettingsbehoeften, arbeidsregels en voorkeuren.

Dit is pure mixed-integer programming. Elke toewijzing is een binaire keuze. Een ziekenhuis met 100 verpleegkundigen en 50 diensten heeft 5.000 variabelen om te managen.

Voorbeeld: verpleegkundigenrooster

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)}")

Dit modelleert het roosterprobleem met binaire variabelen. Het minimaliseert het totaal aantal diensten en respecteert bezettingsbehoeften, maximumuren en verplichte rusttijden.

Financiële portefeuille-optimalisatie

Risico-rendement-afruil

Portefeuille-optimalisatie balanceert verwacht rendement tegen risico. Het klassieke Markowitz-model:

  • Maximaliseer: Verwacht rendement
  • Onder voorbehoud van: Risico (variantie) onder drempel
  • Restricties: Budgetrestricties, diversificatie-eisen, regelgeving

Dit is quadratische programmering. Rendement is lineair in portefeuillegewichten, maar variantie is quadratisch (betreft producten van gewichten).

Praktische overwegingen

Echte portefeuille-optimalisatie omvat:

  • Transactiekosten (kopen/verkopen kost fees)
  • Integerrestricties (sommige assets kun je niet in fracties kopen)
  • Regelgeving (niet meer dan 10% in één effect)
  • Rebalancingrestricties (verander bestaande portefeuille niet te drastisch)

Vermogensbeheerders draaien dit dagelijks en beheren miljarden. Oplostijd beïnvloedt direct de uitvoering van handelsstrategieën.

Conclusie

We hebben veel behandeld, van Gurobi installeren tot het bouwen van complexe supply chain-modellen.

Nu hoef je niet meer te palaveren in een vergaderzaal, maar kun je de code tonen. "Dit is het wiskundig optimale plan." Dat verandert het gesprek. Je stopt met gissen en begint te bewijzen.

Ga ermee aan de slag bij je volgende rommelige probleem, dat roosterconflict of budgetknelpunt dat je uitstelde. Misschien blijkt het perfecte antwoord al die tijd in je data te zitten.

En als je je optimalisatievaardigheden verder wilt aanscherpen, bekijk dan onze tutorial Introduction to Linear Programming en de cursus Introduction to Optimization in Python om door te bouwen.

Khalid Abdelaty

Auteur

Khalid Abdelaty

Ik ben een data-engineer en communitybouwer die werkt aan datapijplijnen, cloud en AI-tools, en tegelijkertijd praktische, impactvolle tutorials schrijft voor DataCamp en beginnende developers.

Gurobi FAQ's

Wat is het verschil tussen de Gurobi Optimizer en gurobipy?

De Gurobi Optimizer is de rekenmotor die de wiskundige problemen oplost. gurobipy is de Python-bibliotheek waarmee je modellen bouwt en met die motor communiceert. Je schrijft code in gurobipy en die stuurt het probleem naar de Optimizer om de oplossing te vinden.

Heb ik geavanceerde wiskunde nodig om Gurobi effectief te gebruiken?

Niet echt. Als je basisalgebra begrijpt (zoals 2x + y <= 10), ben je klaar voor de meeste problemen. De tool handelt de complexe algoritmen af. Je moet je probleem logisch kunnen beschrijven.

Kan ik Gurobi gebruiken met andere talen dan Python?

Ja! Hoewel Python de populairste keuze is in data science, heeft Gurobi ook robuuste API's voor C++, Java, .NET en MATLAB. Het integreert zelfs met R voor statistische workflows.

Wat als mijn model te lang duurt om op te lossen?

Je hebt niet altijd het "perfecte" antwoord nodig. Je kunt een "MIPGap" instellen om de solver te laten stoppen zodra er een oplossing is binnen 1% of 5% van het optimum. In de praktijk is een goed antwoord nu vaak beter dan een perfect antwoord morgen.

Is Gurobi lastig te onderhouden in productie?

Het is eigenlijk behoorlijk stabiel. De grootste uitdaging is niet de software, maar de data. Als je inputdata van formaat verandert of fouten bevat, kan je model stuklopen. Goede datavalidatiepipelines zijn kritischer dan de Gurobi-code zelf.

Kan ik de resultaten eenvoudig visualiseren?

Gurobi geeft cijfers terug, maar omdat het zo goed integreert met Python, kun je die resultaten direct doorzetten naar bibliotheken zoals Matplotlib, Seaborn of Plotly. Je maakt met een paar regels code Gantt-diagrammen of netwerkgrafieken van je optimalisatieresultaten.

Onderwerpen