Einführung in Xpress-Mosel
Transcription
Einführung in Xpress-Mosel
Einführung in Xpress-Mosel Matthias Silbernagl Computerblockpraktikum zur linearen Optimierung 1 Mosel-Grundlagen Gliederung 1.1 Xpress im Überblick Xpress im Überblick Inhaltsverzeichnis 1 Mosel-Grundlagen 1.1 Xpress im Überblick . . . . . . . . . . . . . . . . . . . . . . 1.2 Grundlegende Strukturen . . . . . . . . . . . . . . . . . . . 1.3 Ein einführendes Beispiel . . . . . . . . . . . . . . . . . . . 1 1 2 2 2 Mosel für Fortgeschrittene 2.1 Entscheidungsvariable . . . . . 2.2 Mengen, Felder und Parameter 2.3 Ein Beispiel . . . . . . . . . . . 2.4 Daten einlesen und ausgeben . 2.5 Entscheidungen und Schleifen . 2.6 Datensätze . . . . . . . . . . . 2.7 Unterprogramme . . . . . . . . 4 4 4 5 7 8 8 8 3 Optimizer-Modul: Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . • Xpress-MP ist ein Softwarepaket für lineare und gemischt-ganzzahlige Optimierung • Mehrere Komponenten: – Optimizer → Kernkomponente (primaler & dualer Simplex, Barrier, MIP, Presolver, Heuristiken) – Low-Level Bibliotheken (C/C++/Java/VB) – Programmier- und Modellierungssprache Mosel – Bibliotheken zur Einbindung von Mosel in C/C++/Java/VB – Entwicklungstools (Debugger, Profiler, IDE) – weitere Module (Datenzugriff, Graphik und GUI, Algorithmen) 9 Xpress im Überblick 1 Variablen-Deklaration Daten Modell • Variablen vor der Verwendung deklarieren! Umsetzung • Deklaration zwischen declarations und end-declarations Mosel Interaktion • Variablentypen in Mosel Optimizer integer, real: ganze bzw. rationale Zahlen string: Zeichenketten wie "Lineares Modell" boolean: Wahrheitswert (true oder false). Ausgabe set of . . . : Menge, z. B. set of integer Lösung range: ganzzahliges Intervall array(. . . ) of: Feld , z. B. array(1..3) of real 1.2 Grundlegende Strukturen record: Datensatz Mosel-Programmgerüst mpvar: Entscheidungsvariable model " Modellname " options noimplicit , explterm ; uses " mmxprs " ; linctr: linearer Ausdruck, z. B. Nebenbedingung mpproblem: lineares Problem declarations ! V a r i a b l endeklaration end-declarations Achtung: Entscheidungsvariablen sind implizit ≥ 0! ! Programmcode 1.3 Ein einführendes Beispiel end-model Einführendes Beispiel • Programmcode zwischen model "Modellname" und end-model Aufgabe Für die Produktion der Produkte P1 und P2 benötigt ein Unternehmen eine Maschine, die in jedem Monat 160 Stunden zur Verfügung steht. Eine Einheit von P1 kann in einer Stunde auf der Maschine gefertigt werden, für eine Einheit P2 werden drei Stunden benötigt. Der Gewinn beim Verkauf eines Stückes von P1 beträgt 7,– e, ein Stück P2 kann mit einem Gewinn von 9,– e verkauft werden. Maximal lassen sich pro Monat 100 Stück von P1 und 40 Stück von P2 absetzen. Welche Produktionsmengen von P1 und P2 maximieren den Gewinn des Unternehmens? • options noimplicit: Variablen müssen deklariert werden → vereinfacht Fehlersuche! • options explterm: Befehle müssen mit ; beendet werden • uses "Bibliothek" bindet zusätzliche Bibliotheken ein, z. B. mmxprs für die Optimierung. • ! leitet Kommentare ein 2 Einführendes Beispiel model " Produktion " options noimplicit , explterm ; uses " mmxprs " ; • Restriktionen declarations Kriterium P1 P2 100 1 7 40 3 9 end-declarations Maximalmengen Maschinenzeit: 160 h Gewinn end-model • Variablen-Deklaration • Modellierung declarations x , y: mpvar ; Maschinenzeit: linctr ; Gewinn: linctr ; end-declarations Variablen: Produktionsmengen von P1 bzw. P2 → x, y Maximalmengen: x ≤ 100, y ≤ 40 Maschinenzeit: x + 3y ≤ 160 • Nebenbedingungen und Zielfunktion Zielfunktion: max 7x + 9y Gewinn := 7 *x + 9 *y ; Maschinenzeit := x + 3 *y <= 160; x <= 100; y <= 40; max 7x + 9y x + 3y ≤ 160 • Optimierung und Ausgabe x ≤ 100 maximize ( Gewinn ); y ≤ 40 writeln ( " Gewinn: " , getobjval ); writeln ( " P1: " , getsol ( x )); writeln ( " P2: " , getsol ( y )); Einführendes Beispiel in Mosel • Modell Gesamtprogramm max 7x + 9y model " Produktion " options noimplicit , explterm ; uses " mmxprs " ; x + 3y ≤ 160 x ≤ 100 y ≤ 40 declarations x , y: mpvar ; Maschinenzeit: linctr ; Gewinn: linctr ; • Grundgerüst 3 – is_free: freie Variable, d. h. Wertebereich (−∞; ∞) end-declarations – is_integer: {0, 1, 2, . . .} Gewinn := 7 *x + 9 *y ; Maschinenzeit := x + 3 *y <= 160; x <= 100; y <= 40; ganzzahlige Variable, d. h. Wertebereich – is_binary: 0-1-Variable, d. h. Wertebereich {0, 1} – Selten benötigte Typen auf Kurzreferenz maximize ( Gewinn ); writeln ( " Gewinn: " , getobjval ); writeln ( " P1: " , getsol ( x )); writeln ( " P2: " , getsol ( y )); end-model • Deklaration z. B. mit x is_binary 2.2 Mengen, Felder und Parameter Mengen und Intervalle Wichtige Befehle • Mengen • model – end-model klammert das Modell ein – Deklaration als A, B : set of ...; • uses zur Einbindung von Bibliotheken – Schreibweise: A := {1,2,6,9} – Vereinigung: A + B oder union(i in 1..5) {i*5} • declarations – end-declarations klammert Variablendeklaration ein – Differenz: A - B • maximize bzw. minimize startet die Optimierung – Schnitt: A*B oder inter(i in 1..5) {1,i} • write bzw. writeln zur Ausgabe – Test auf Untermenge: A <= B / Obermenge A >= B • getobjval liefert den Zielfunktionswert • Intervalle – Deklaration als R: range; • getsol() liefert die Lösung – Schreibweise: R := 4..20 2 Mosel für Fortgeschrittene – spezielle Mengen, effizientere Speichernutzung 2.1 Entscheidungsvariable – Operationen wie bei Mengen (auch gemischt), Ergebnis evtl. kein Intervall. Typen von Entscheidungsvariablen Felder, Summen, Schleifen • Entscheidungsvariable sind vom Typ mpvar • Felder • Vorgabe: stetig aus [0; ∞) – Deklaration als F: array(Indexmenge) of ... mit einer Menge oder einem Intervall als Indexmenge • Spezielle Typen von Entscheidungsvariablen: 4 (bi ≥ 0) bzw. das Angebot an Ostereiern (bi < 0). Dabei sei die Gesamtnachfrage gleich dem Gesamtangebot. Zwischen je zwei Bahnhöfen i und j existiert eine Zugverbindung, auf der maximal lij Eier transportiert werden können, der Transport koste dabei cij pro Ei. Bestimmen Sie einen kostenminimalen Transportplan für die Ostereier! – Schreibweise: F := [1,2,3] – Zugriff auf einzelne Elemente: F(2) – Mehrdimensionale Arrays: G: array(Index1, Index2, Index3) of ... – Beispiel: G: array ({ " rot " , " blau " } , 1..3) of integer ; G :: ([ " rot " , " blau " ] , [1 ,2 ,3])[1 , 2 , 3 , 4 , 5 , 6]; G ( " rot " , 2) := 42; Modellierung • Variablen: ergibt [1, 42, 3, 4, 5, 6] xij = Anzahl Eier, die von i nach j transportiert werden • Summen über Felder: sum(i in 1..3) F(i) • Schleifen über Felder: • Kapazitäten: forall ( i in { " rot " , " blau " } , j in 1..3) do writeln ( " G " ,i , " / " , j , " = " , G (i , j )); end-do xij ≤ lij ∀(i, j) ∈ {1, . . . , n} × {1, . . . , n} • Bedarf/Angebot: Parameter n X • Mosel-Parameter sind Konstanten, die in einem eigenen Block deklariert werden: xki − n X xik = bi k=1 k=1 • Zielfunktion parameters n = 10; end-parameters ∀i ∈ {1, . . . , n} min n X n X cij xij i=1 j=1 • Über die Kommandozeile oder die Benutzeroberfläche können andere Werte für die Parameter übergeben werden. Modellierung in Mosel • Modell • Parameter können nur integer, real, string oder boolean sein. min 2.3 Ein Beispiel n X n X cij xij i=1 j=1 Beispiel n X Aufgabe Auf dem Schienennetz der Bahn sollen Ostereier transportiert werden. Es gibt n Bahnhöfe, für jeden Bahnhof sei bi der jeweilige Bedarf an Ostereiern k=1 xki − n X xik = bi 0 ≤ xij ≤ lij 5 ∀i ∈ {1, . . . , n} k=1 ∀(i, j) ∈ {1, . . . , n} × {1, . . . , n} • Parameter • Zielfunktion definieren und optimieren parameters n = 10; ! Anzahl Bahnhöfe end-parameters costs := sum ( i in Stations , j in Stations ) cost (i , j ) * x (i , j ); minimize ( costs ); • Deklarationen • Ausgabe declarations Stations = 1.. n ; cost: array ( Stations , Stations ) of integer ; capacity: array ( Stations , Stations ) of integer ; demand: array ( Stations ) of integer ; writeln ( " Gesamtkosten: " , getobjval ); forall ( i in Stations , j in Stations | getsol ( x (i , j )) <> 0) do writeln ( " Transport von " , i , " nach " , j , " : " , getsol ( x (i , j )) , " Ostereier " ); end-do .. . .. . • Deklarationen Gesamtprogramm model " Ostereier " uses " mmxprs " ; options noimplicit , explterm ; x: array ( Stations , Stations ) of mpvar ; c a p a c i tyConstraints: array ( Stations , Stations ) of linctr ; de ma nd Constraints: array ( Stations ) of linctr ; costs: linctr ; end-declarations parameters n = 10; ! Anzahl Bahnhöfe end-parameters • Initialisieren declarations Stations = 1.. n ; cost: array ( Stations , Stations ) of integer ; capacity: array ( Stations , Stations ) of integer ; demand: array ( Stations ) of integer ; x: array ( Stations , Stations ) of mpvar ; capacityCon s t r a i n t s : array ( Stations , Stations ) of linctr ; demandConst ra int s: array ( Stations ) of linctr ; costs: linctr ; end-declarations ! initialization demand := [5 , 20 , 11 , -15 , -17 , 23 , 2 , 7 , -15 , -21 ]; cost := [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 2 , ... ]; capacity := [0 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , ... ]; • Nebenbedingungen und forall ( i in Stations , j in Stations ) do c ap a c ityConstraints (i , j ) := x (i , j ) <= capacity (i , j ); end-do forall ( i in Stations ) do sum ( k in Stations ) x (k , i ) sum ( k in Stations ) x (i , k ) = demand ( i ); end-do ! initialization demand := [5 , 20 , 11 , -15 , -17 , 23 , 2 , 7 , -15 , -21 ]; cost := [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 2 , ... ]; capacity := [0 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , ... ]; 6 ! capacity constraints forall ( i in Stations , j in Stations ) do c ap a c it y Constraints (i , j ) := x (i , j ) <= capacity (i , j ); end-do ! demand constraints forall ( i in Stations ) do sum ( k in Stations ) x (k , i ) sum ( k in Stations ) x (i , k ) = demand ( i ); end-do initializations from " daten . dat " capacity ; cost as " Transportkosten " ; end-initia l iz a ti o n s Beispiel für Dateneingabe • Die Datei daten.dat: ’ numOfStations ’: 10 ’ demand ’: [5 20 11 -15 -17 23 2 7 -15 -21 ] ! objective costs := sum ( i in Stations , j in Stations ) cost (i , j ) * x (i , j ); ’ Transportkosten ’: [1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 99] minimize ( costs ); writeln ( " Gesamtkosten: " , getobjval ); forall ( i in Stations , j in Stations | getsol ( x (i , j )) <> 0) do write ( " Transport von " , i , " nach " , j , " : " ); writeln ( getsol ( x (i , j )) , " Ostereier " ); end-do end-model ’ capacity ’: [0 3 4 5 6 7 8 9 10 11 3 0 5 6 7 8 9 10 11 12 4 5 0 7 8 9 10 11 12 13 5 6 7 0 9 10 11 12 13 14 6 7 8 9 0 11 12 13 14 15 7 8 9 10 11 0 13 14 15 16 8 9 10 11 12 13 0 15 16 17 9 10 11 12 13 14 15 0 17 18 10 11 12 13 14 15 16 17 0 19 11 12 13 14 15 16 17 18 19 0] 2.4 Daten einlesen und ausgeben Daten Aus- und Eingabe über Dateien Beispiel für Dateneingabe • Daten können aus Textdateien eingelesen und in Textdateien ausgegeben werden. • Die Datei daten.dat: • Vorteil: Trennung von Daten und Programmcode ’ numOfStations ’: 10 ’ demand ’: [5 20 11 -15 -17 23 2 7 -15 -21 ] . . . • Datenausgabe mit initializations to-Block, mit as wird eine neue Bezeichnung vergeben: initializations to " daten . dat " capacity ; cost as " Transportkosten " ; e nd - i ni t ializations • Einlesen der Daten: initializations from " daten . dat " n as " numOfStations " ; capacity ; • Einlesen von Daten mit initializations from-Block: 7 • repeat-Schleife: cost as " Transportkosten " ; demand ; e nd - i ni t ializations repeat writeln ( " Das wird immer ausgeführt " ); until (2 > 5); 2.5 Entscheidungen und Schleifen • In allen Schleifen: Entscheidungen – break bricht die Schleife ab – next springt zur nächsten Iteration der Schleife • if-Befehle: if ( note <= 2.3) then writeln ( " gut bis sehr gut bestanden " ); elif ( note <= 4.0) then writeln ( " bestanden " ); else writeln ( " leider nichts " ); end-if 2.6 Datensätze Datensätze • Kapseln mehrere Variablen • Deklaration als • Konditionale Summen und Schleifen MyRecord = record var: ...; ... end-record r: MyRecord ; ungerade := sum ( i in 1..10 | isodd ( i )) i ; forall ( i in 1..10 , j in 1..10 | isodd ( i ) and j <> i ) do writeln ( " i = " , i , " / j = " , j ); end-do • Zugriff auf einzelne Elemente: r.var • Beispiel: Schleifen declarations Hyperplane = record a: array ( Dimensions ) of real ; b: real ; end-record ; H: Hyperplane ; end-declarations • forall-Schleife: forall ( i in 1..10) do writeln ( " i = " , i ); end-do • while-Schleife: H . a := [1 , 0 , 0 , 0]; H . b := -3 ; writeln ( H . a (1)); while ( i*j < 10) do writeln ( " i*j = " , i*j ); i += 2; end-do 2.7 Unterprogramme 8 • Routinen für die Verwaltung von Schnittebenen Unterprogramme • Callbacks für Eingriffe in den Ablauf des Optimierungsprozesses • Kapselung mehrfach verwendeter, eigenständiger Programmteile • Lokale Deklarationen und rekursive Aufrufe sind möglich Parameter für den Optimizer • Prozeduren: • Auslesen von Parametern: getparam("XPRS_parameter") procedure writeOddNumbers ( numbers: array ( r: range ) of integer ) forall ( i in r | isodd ( numbers ( i ))) do writeln ( numbers ( i ) , " ist ungerade " ); end-do end-procedure • Setzen von Parametern: setparam("XPRS_parameter", value) • Wichtige Parameter: – XPRS_verbose (true/false): Meldungen des Optimizers anzeigen – XPRS_presolve (0/1): Presolver aus- bzw. einschalten. • Funktionen (Wert wird zurückgeliefert – integer, real, boolean oder string): – XPRS_cutstrategy (-1,0,1,2,3): Aggressivität der Schnittgenerierung function isSmallNumber ( r: real ) : boolean if ( r < 0.1) then returned := true ; else returned := false ; end-if end-function – XPRS_heurstrategy (-1,0,1,2,3): Aggressivität der Heuristiken • Außerdem: Einstellung von Cut-Strategie, Heuristiken, Ablauf des Branch & Bound, Simplex-Algorithmus, numerische Toleranzen, Scaling-Strategie etc. Weitere wichtige Kommandos 3 Optimizer-Modul: Grundlagen • getprobstat: aktueller Status des Optimizers: XPRS_UNF: Optimierung noch nicht beendet Grundlagen XPRS_OPT: Optimierung beendet, Optimallösung gefunden • Xpress-Optimizer wird über das Modul mmxprs eingebunden → XPRS_INF: Optimierung beendet, unzulässiges Problem uses "mmxprs" XPRS_UNB: Optimierung beendet, unbeschränktes Problem • Eigene Datentypen: mpvar, linctr und mpproblem. XPRS_OTH: Optimierung beendet, keine Lösung gefunden • Befehle zur Steuerung des Optimizers • isintegral(x: mpvar): Variable ganzzahlig? (numerische Toleranzen!) • Einstellen und Auslesen von Parametern • sethidden(c: linctr, b: boolean): Vorübergehendes Ein-/Ausschalten von Nebenbedingungen • Speichern und Einlesen von LP-Modellen 9 Der Typ mpproblem • Kapselt ein lineares Problem • Besteht aus linearen Nebenbedingungen • Teilt sich Optimierungsvariablen mit allen anderen Problemen • Beispiel: declarations Problem: mpproblem ; x: array (1..2) of mpvar ; end-declarations with Problem do x (1) + x (2) <= 3; minimize ( x (1) + 2 *x (2)); end-do Der Typ mpproblem • Probleme können: – kopiert werden: P2 := P1 – eingefügt werden: P2 += P1 – geleert werden: reset(P1) – anonym verwendet werden: with mpproblem do ... end-do und werden anschließend automatisch gelöscht • Alle Nebenbedingungen gelten nur für ihr Problem! → Achtung: auch Wertebereiche (is_binary, ...) 10