using JuMP, HiGHSPakete & JuMP
Grundlagen Operations Research
Auf dieser Seite bauen wir Schritt für Schritt ein vollständiges Optimierungsmodell in JuMP auf, vom leeren Modell bis zur ausgewerteten Lösung. Als Beispiel dient das Frachtführer-Problem aus Vorlesung 1:
Pakete laden
Julia-Funktionalität steckt in Paketen. Installiert haben Sie die Kurs-Pakete bereits in Installation & Setup (einmalig, mit ] add JuMP HiGHS Plots IJulia); in jedem Skript werden sie dann mit using geladen:
- JuMP ist die Modellierungssprache: Sie schreiben Variablen, Zielfunktion und Nebenbedingungen fast wie auf dem Papier.
- HiGHS ist der Solver, der das Modell tatsächlich löst, ein Open-Source-Verfahren, das den (dualen) Simplex-Algorithmus und Branch & Bound implementiert.
Das Modell aufbauen
Schritt 1: leeres Modell anlegen. Dabei geben wir an, welcher Solver später rechnen soll:
modell = Model(HiGHS.Optimizer)Schritt 2: Entscheidungsvariablen deklarieren. @variable legt eine Variable an; Schranken schreiben Sie direkt dazu:
@variable(modell, X1 >= 0) # Abnahmemenge Auftrag 1
@variable(modell, X2 >= 0) # Abnahmemenge Auftrag 2Schritt 3: Zielfunktion. Max oder Min, danach der Term:
@objective(modell, Max, 20X1 + 30X2) # Umsatzerlöse in EURSchritt 4: Nebenbedingungen. Jede Nebenbedingung bekommt einen Namen (hier Paletten und Gewicht), so können wir später gezielt auf sie zugreifen:
@constraint(modell, Paletten, X1 + X2 <= 5) # max. 5 Paletten
@constraint(modell, Gewicht, X1 + 3X2 <= 9) # max. 9 TonnenMit print(modell) können Sie jederzeit prüfen, ob das Modell so aussieht wie auf dem Papier:
print(modell)Max 20 X1 + 30 X2
Subject to
Paletten : X1 + X2 ≤ 5
Gewicht : X1 + 3 X2 ≤ 9
X1 ≥ 0
X2 ≥ 0
Lösen und die Solver-Ausgabe lesen
optimize! übergibt das Modell an HiGHS. Beim ersten Mal lassen wir die Solver-Ausgabe bewusst eingeschaltet:
optimize!(modell)Running HiGHS 1.15.1 (git hash: 04024d701f): Copyright (c) 2026 under MIT licence terms
Includes third-party software components, see THIRD_PARTY_NOTICES.md for full details
Using BLAS: Apple Accelerate
LP has 2 rows; 2 cols; 4 nonzeros
Coefficient ranges:
Matrix [1e+00, 3e+00]
Cost [2e+01, 3e+01]
Bound [0e+00, 0e+00]
RHS [5e+00, 9e+00]
Presolving model
2 rows, 2 cols, 4 nonzeros 0s
2 rows, 2 cols, 4 nonzeros 0s
Presolve reductions: rows 2(-0); columns 2(-0); nonzeros 4(-0) - Not reduced
Problem not reduced by presolve: solving the LP
Using dual simplex solver
Iteration Objective Infeasibilities num(sum)
0 -4.9999960786e+01 Ph1: 2(6); Du: 2(50) 0.0s
2 1.2000000000e+02 Pr: 0(0) 0.0s
Model status : Optimal
Simplex iterations: 2
Objective value : 1.2000000000e+02
P-D objective error : 0.0000000000e+00
HiGHS run time : 0.00
Die wichtigsten Zeilen dieser Ausgabe:
- Presolve: HiGHS vereinfacht das Modell vor dem eigentlichen Lösen (entfernt z. B. überflüssige Zeilen und Spalten).
- Simplex iterations: so viele Basistausche hat der Simplex-Algorithmus gebraucht (vgl. Vorlesung 2).
- Model status: Optimal, es wurde eine optimale Lösung gefunden.
- Objective value: der optimale Zielfunktionswert .
Im Kurs schalten wir die Ausgabe fast immer mit set_silent(modell) ab. Direkt nach Model(...) aufgerufen, bleibt die Konsole sauber und nur unsere eigenen println-Zeilen erscheinen.
Ergebnisse abfragen
Nach dem Lösen prüfen Sie zuerst den Status und lesen dann die Werte aus:
termination_status(modell)OPTIMAL::TerminationStatusCode = 1
OPTIMAL heißt: Die Lösung ist optimal. Erst dann sind die folgenden Abfragen sinnvoll:
println("F* = ", objective_value(modell),
" mit X1 = ", value(X1), ", X2 = ", value(X2))F* = 120.0 mit X1 = 3.0, X2 = 2.0
Auf dieser Seite steht unter jeder Zelle automatisch der Wert ihrer letzten Zeile. Beim eigenen Ausführen ist das anders: Zed zeigt nur den Wert der letzten Zeile einer Zelle, im Terminal (julia datei.jl) erscheint gar nichts. Geben Sie Ergebnisse deshalb ausdrücklich aus, mit println wie oben oder kürzer mit @show. So gibt @show value(X1) die Zeile value(X1) = 3.0 aus.
Die drei Statuswerte, die Ihnen in diesem Kurs begegnen (vgl. Vorlesung 5):
| Status | Bedeutung |
|---|---|
OPTIMAL |
optimale Lösung gefunden |
INFEASIBLE |
das Modell ist unzulässig (kein zulässiger Punkt) |
DUAL_INFEASIBLE |
das Modell ist unbeschränkt (Zielfunktion wächst ohne Grenze) |
Indizierte Variablen
Bei zwei Variablen reichen die Namen X1 und X2. Größere Modelle schreiben wir wie auf dem Papier mit Index, also . Dazu stehen die Daten getrennt vom Modell in Vektoren und einer Matrix:
c = [20, 30] # c[j]: Zielfunktionskoeffizient von X[j]
a = [1 1; # a[i, j]: Koeffizient von X[j] in Nebenbedingung i
1 3] # (Zeile 1: Paletten, Zeile 2: Gewicht)
b = [5, 9] # b[i]: rechte Seite der Nebenbedingung iX[1:2] legt die Variablen X[1] und X[2] auf einmal an (JuMP nennt das einen Container). Die Zielfunktion ist eine Summe über den Index , und eine einzige @constraint-Zeile mit [i in 1:2] erzeugt je Index eine Nebenbedingung:
modell = Model(HiGHS.Optimizer)
set_silent(modell)
@variable(modell, X[1:2] >= 0)
@objective(modell, Max, sum(c[j] * X[j] for j in 1:2))
@constraint(modell, [i in 1:2], sum(a[i, j] * X[j] for j in 1:2) <= b[i])
print(modell)Max 20 X[1] + 30 X[2]
Subject to
X[1] + X[2] ≤ 5
X[1] + 3 X[2] ≤ 9
X[1] ≥ 0
X[2] ≥ 0
Das ist dasselbe Modell wie oben. Die Zahlen stehen aber nur noch in den Daten, das Modell selbst besteht aus Symbolen und Indexmengen. Diese Schreibweise heißt implizite Form: Ändern sich die Zahlen, passen Sie nur die Daten an, die Modellformulierung bleibt gleich.
Nach dem Lösen liefert value.(X) (mit Broadcasting-Punkt) die Werte aller Variablen auf einmal:
optimize!(modell)
value.(X)2-element Vector{Float64}:
3.0
2.0
JuMP merkt sich jede Variable unter ihrem Namen im Modell. Mit modell[:X] (Doppelpunkt vor dem Namen) rufen Sie den Container darüber ab, auch wenn die Julia-Variable X gerade nicht zur Hand ist. Die Hinweise in den Übungen nutzen diese Schreibweise, etwa um eine Variable auf einen Wert festzusetzen (force = true ist nötig, weil X[2] schon die Schranke >= 0 hat):
fix(modell[:X][2], 0; force = true) # X[2] auf 0 festsetzen
optimize!(modell)
println("F = ", objective_value(modell), " mit X = ", value.(X))F = 100.0 mit X = [5.0, 0.0]
Das Muster für jedes Kurs-Modell
Alle Modelle des Kurses folgen genau diesem Aufbau:
using JuMP, HiGHS
modell = Model(HiGHS.Optimizer)
set_silent(modell)
@variable(modell, ...) # 1. Entscheidungsvariablen
@objective(modell, ..., ...) # 2. Zielfunktion (Max/Min)
@constraint(modell, ...) # 3. Nebenbedingungen
optimize!(modell) # 4. Lösen
@show termination_status(modell) # 5. Status prüfen, dann:
@show objective_value(modell) # Zielfunktionswert und
@show value.(...) # Variablenwerte ausgebenWie geht es weiter?
Damit können Sie jede Julia-Zelle des Kurses lesen und selbst ausführen. Alle weiteren JuMP-Bausteine (Variablenmatrizen, ganzzahlige Variablen, Sensitivitätswerte wie shadow_price und reduced_cost) finden Sie kompakt im Cheatsheet JuMP und in Aktion ab Übung 1.