Appunti sul linguaggio di programmazione MPL Lorenzo Brunetta, Michele D Amico e Ivan Luzzi 1 2 3 Milano, Agosto 2000 1 Dipartimento di Elettronica e Informazione, Politecnico of Milano, Piazza L. da Vinci 32, 20133 Milano, e-mail: brunetta@elet.polimi.it 2Dipartimento di Matematica Pura e Applicata, Università di Padova, Via Belzoni 7, 35131 Padova ITALY, e-mail:damico@math.unipd.it 3Dipartimento di Matematica Pura e Applicata, Università di Padova, Via Belzoni 7, 35131 Padova ITALY, e-mail:iluzzi@libero.it
Indice 1 Introduzione 1 2 Programmazione con il linguaggio MPL 3 2.1 Scrivere e risolvere un Modello con MPL........... 3 2.1.1 La Prima Sessione in MPL............... 3 2.1.2 Utilizzare il Sistema di Aiuto in MPL......... 6 2.2 Caratteristiche generali..................... 6 2.3 Struttura di un programma MPL............... 8 2.3.1 TITLE........................... 8 2.3.2 INDEX.......................... 8 2.3.3 DATA........................... 10 2.3.4 DECISION........................ 11 2.3.5 MACROS......................... 12 2.3.6 MODEL.......................... 12 2.3.7 SUBJECT TO...................... 13 2.3.8 BOUNDS......................... 14 2.3.9 FREE........................... 15 2.3.10 INTEGER e BINARY................. 15 3 Un modello di bilanciamento della produzione 16 3.1 Formulazione del Modello.................... 16 3.2 Vedere e Analizzare la Soluzione................ 17 4 Esempi di risoluzione di problemi classici della Programmazione Lineare 21 4.1 Un problema di assegnamento.................. 21 4.2 Un primo problema dei trasporti................ 23 4.3 Un problema della dieta..................... 25 4.4 Un problema di produzione più complesso........... 28 5 Un Modello di Piani cazione della Produzione a Multi-Periodo 32 5.1 Formulazione del Modello.................... 33 5.2 Risolvere il Modello e Analizzare la Soluzione......... 34 I
6 Un Modello di Piani cazione con più Impianti di Produzione 39 6.1 Formulazione del Modello.................... 40 6.2 Risolvere il Modello e Analizzare la Soluzione......... 41 7 Modelli di Trasporto 44 7.1 Usare la Condizione Where sui Vettori di Variabili...... 44 7.2 Vincoli di Bilanciamento degli Impianti............ 45 7.3 Un Modello di trasporto tra impianti di produzione...... 46 7.4 Formulazione del modello.................... 47 7.5 Risolvere il Modello e Analizzare la Soluzione......... 48 8 Un Modello con Più Macchinari 51 8.1 Formulazione del Modello.................... 52 8.2 Risolvere il Modello e Analizzare la Soluzione......... 53 II
L obiettivo di queste dispensa è quello di fornire una guida in italiano utile per apprendere le idee di base di uno dei software di ottimizzazione più diffusi. Gli argomenti trattati sono di livello elementare e, leggendo queste pagine, chiunque abbia un minimo di dimestichezza con un qualsiasi linguaggio di programmazione dovrebbe poter apprendere velocemente la metodologia necessaria per creare e risolvere problemi di ottimizzazione lineare. Le pagine che seguono hanno quindi uno scopo puramente didattico e speriamo possano essere uno strumento utile a chi si avvicina per la prima volta a questo programma di ottimizzazione. Nel primo capitolo viene spiegata la loso a di base sulla quale si fondano i software detti generatori algebrici di modelli. Nel secondo capitolo viene introdotta la forma di base di un modello in MPL. Si è volutamente data una struttura più rigida di quella effettivamente necessaria per aiutare chi si trova per la prima volta ad affrontare questo metodo di programmazione a individuare i vari passi necessari per tradurre un problema di ottimizzazione in un modello MPL. Nel terzo, quinto, sesto, settimo ed ottavo capitolo sono discussi i modelli contenuti nel Tutorial di MPL. Nel quarto capitolo sono discussi e tradotti in modelli MPL alcuni classici problemi di programmazione lineare. È possibile ottenere la versione studenti del software MPL con licenza d uso semestrale al sito http://www.maximal-usa.com. III
Capitolo 1 Introduzione Ai sostanziali progressi ottenuti nella programmazione matematica dagli anni 50 agli anni 70 non è corrisposto un adeguato utilizzo applicativo degli stumenti matematici sviluppati. Questo è dovuto, in gran parte, alle difficoltà computazionali ed informatiche insite nella stesura del modello, nella raccolta ed organizzazione dei dati, nella programmazione degli algoritmi solutori e nell analisi dei risultati ottenuti. Quindi, gli sforzi ottenuti in campo matematico, risultavano inutilizzabili a livello applicativo poiché l effettivo incremento nella velocità e potenza dei solutori si scontrava con la necessità di creare software che interrogasse questi solutori e che gestisse modelli e dati appartenenti al modo reale. MPL (e in generale i programmi detti generatori algebrici di modelli ) nasce proprio per ovviare a questa difficoltà e si pongono come obiettivi: fornire un linguaggio di programmazione ad alto livello che permetta di descrivere in modo semplice modelli reali anche molto complessi; creare modelli indipendenti dal solutore utilizzato, in modo da poter sfruttare sempre i solutori più potenti presenti sul mercato; descrivere modelli nei quali la struttura logica del problema e i dati utilizzati possano essere considerati come entità diverse. Uno schema formale per la situazione descritta può essere il seguente: utente programma ( es. MPL) solutore ( es.cp LEX) dati database Utilizzando MPL l utente non deve più occuparsi direttamente dell interrogazione del solutore, ma può concentrarsi sulla stesura del modello programmando in un linguaggio di alto livello con il quale può descrivere molto 1
semplicemente problemi reali anche molto complessi. Inoltre il solutore può essere sostituito senza dover modi care la forma con la quale sono descritti i modelli. In ne la possibilità di trattare in modo diverso i dati e struttura logica dei problemi fa si che piccole modi che nei dati o nella struttura del modello siano gestibili senza dover riprogrammare completamente molte righe di codice. In ne, la distinzione dati-modello permette di importare i parametri che caratterizzano l istanza di un problema da un database esterno lasciando libero l utente di occuparsi solo della formulazione logica del modello. Le principali caratteristiche di questi generatori algebrici di modelli sono le seguenti: il linguaggio di programmazione utilizzato è semplice da imparare (è composto da poche parole chiave); i codici che si realizzano sono essi stessi una rappresentazione concisa ed elegante del modello; è possibile inserire commenti o de nizioni linguistiche che rendono sia l input che l output comprensibili anche da non-programmatori ; la maggior parte dei programmi creati sta in un unico documento il che facilita il controllo e la creazione dei modelli; nell output vengono rappresentate, in modo comprensibile, tutte le operazioni svolte dal solutore; i programmi vengono scritti con un semplice editor di testo e sono indipendenti dalla piattaforma sulla quale vengono eseguiti sia il programma, sia il solutore (anche se Windows è la piattaforma su cui è più usato); MPL ha circa 5 anni possiede una piccola libreria di esempi di modelli che comprende sia problemi lineari, che problemi non lineari, e un tutorial. 2
Capitolo 2 Programmazione con il linguaggio MPL MPL è l acronimo di Model Development Environment, cioè Ambiente di Sviluppo di Modelli; si tratta di un linguaggio di programmazione ad alto livello che permette di descrivere in modo semplice e intuitivo dei modelli di Programmazione Lineare. Si possono de nire modelli speci ci con dati assegnati, oppure modelli più generali, che vengono associati di volta in volta con i dati letti da un le esterno. In questo capitolo, dopo una prima introduzione generale, verranno analizzate le diverse sezione che compongono un programma MPL. 2.1 Scrivere e risolvere un Modello con MPL In questa sezione impareremo, attraverso una serie di passi elementari, come scrivere e risolvere un modello con MPL. Alla ne della sezione, saremo in grado di far partire MPL, caricare e risolvere il modello, vedere la soluzione. A questo punto, impareremo come si possano cambiare le opzioni di settaggio di MPL attraverso le nestre di dialogo. Inoltre, nell ultima parte di questa sezione, forniremo una breve descrizione del sistema di aiuto di MPL. 2.1.1 La Prima Sessione in MPL Ci sono quattro semplici passi che devono diventarci familiari se intendiamo risolvere modelli usando MPL: Eseguire l applicazione MPL. Eseguire MPL in Windows 95 è molto semplice. Quando abbiamo installato MPL, il programma di installazione ha creato una voce in Programmi nel menù di Avvio dal nome Mpl for 3
Windows. Per eseguire MPL, se non lo abbiamo ancora fatto, è sufficiente cliccare sul menù Avvio e selezionare Programmi Mpl for Windows Caricare il le del modello nell ambiente MPL. Dopo che si è avviato MPL e ci si trova nell ambiente di sviluppo di modelli, il prossimo passo è quello di caricare il le del modello nell editor di modelli. L applicazione MPL viene fornita con diversi modelli di esempio che sono collocati nella cartella (directory) Mplwin4. I les con i modelli di MPL sono registrati come dei le di testo standard e, tipicamente, hanno l estensione.mpl. Il modello che useremo in questa sezione si chiama Model1.mpl e si trova, insieme a tutti i modelli utilizzati in questo tutorial, in una cartella separata dal nome Tutorial. 1. Scegliamo Open nel menù File per aprire la nestra di dialogo Open. 2. Doppio click sulla cartella dal nome Tutorial per entrare nella cartella dove è conservato il le del modello Model1.mpl. 3. La nestra di dialogo Open ora mostrerà una lista di les di modelli MPL che sono conservati nella cartella Tutorial. Clicchiamo sul le dal nome Model1.mpl per selezionarlo e premete il tasto Open per aprire il le. Alternativamente, possiamo aprirlo semplicemente con un doppio click sul le stesso nella lista. Queste semplici operazioni ci permetteranno di aprire una nuova nestra di editor dei modelli che contiene la formulazione del modello. Risolvere il modello. In questo tutorial usiamo CPLEX300 come risolutore (solver); ma, se siamo in possesso di altri risolutori supportati da MPL, possiamo usarli. Quando eseguiamo MPL per la prima volta dopo averlo installato, MPL proverà automaticamente a localizzare tutti i risolutori che abbiamo a disposizione. Potremo controllare quali risolutori sono stati trovati da MPL andando nel menù Run. Ogni risolutore trovato verrà indicizato nel menù preceduto dalla parola Solve. Se non disponiamo di nessun risolutore verrà visualizzato No Solvers nel menù Run. In questo caso, possiamo consultare il Capitolo 2.4: Setting up Solvers for MPL per trovare le informazioni su come aggiungere risolutori a MPL. D ora in poi assumeremo che CPLEX sia stato installato con successo, oppure che qualsiasi altro risolutore sia stato correttamente installato. Il prossimo passo sarà quello di risolvere il modello che è stato caricato nell editor di modelli. Per risolvere il modello basta seguire i seguenti passi: 1. Scegliere Solve CPLEX nel menù Run per risolvere il modello Model1. 4
2. Durante la risoluzione del modello, viene mostrata la nestra di stato ( Status Window) che ci fornisce informazioni su come progredisce la soluzione. La Status Window fornisce informazioni come il numero di linee lette, il numero delle variabi e dei vincoli del modello, quanta memoria è stata usata. Mentre l ottimizatore risolve il modello, vengono inoltre mostrati il numero delle iterazioni e il valore corrente della funzione obbiettivo. Vedere la soluzione. MPL registra automaticamente la soluzione in un le con lo stesso nome del le del modello, ma con l estensione.sol. Usiamo i seguenti passi per visualizzare il le di soluzione Model1.sol che è stato generato per il modello che abbiamo già risolto. 1. Premiamo il tasto View sul fondo della Status Window che è apparsa sul video durante il processo di risoluzione. Questa operazione aprirà una View Window che contiene il le di soluzione. 2. Possiamo scorrere il le della soluzione semplicemente utilizzando le barre di scorrimento posizionate sulla destra. Notiamo che i dettagli della soluzione includono il valore ottimale della soluzione per la funzione obbiettivo, i valori delle variabili decisionali e dei vincoli. 3. Quando abbiamo nito di vedere il le di soluzione, possiamo chiudere la nestra semplicemente premendo il tasto (X) che si trova nell angolo in alto a destra della nestra stessa. Usare la nestra dell albero delle de nizioni del modello ( Model De nition Window). MPL ci permette inoltre di vedere tutti gli oggetti de niti nella formulazione del modello in una nestra con una struttura ad albero chiamata Model De nitions Windows. Ogni ramo corrisponde ad una sezione del modello. Se la nestra non è visibile, possiamo aprirla scegliendo Model De nitions nel menù View. Normalmente è una buona idea lavorare con MPL mantenendo la Model De nitions Windows sempre aperta. MPL provvederà automaticamente a mantenere aggiornato il contenuto di questa nestra tutte le volte che risolveremo i vostro modello. La Model De nitions Windows provvede ad un facile accesso a differenti parti del modello e ci permette di selezionare e vedere velocemente le parti della soluzione del modello che ci interessano. Per utilizzare l albero basta eseguire: 1. Accertiamoci che la nestra con l albero sia aperta scegliendo De nitions dal menù View. Model 5
2. Sotto l intestazione VARIABLES nella nestra potremo osservare il nome delle variabili in una lista: Produce, Inventory e Sales, che sono le variabili del modello. Davanti all intestazione di ogni sezione, troviamo un quadratino che contiene o un segno più (+) o un segno meno (-). Questo quadratino ci permette di espandere o comprimere velocemente ogni ramo dell albero. 3. Ora selezioniamo la variabile Produce e premiamo il tasto View che si trova sul fondo della nestra. Verrà aperta una nuova View Window con il valore della soluzione per la sola variabile Produce. 2.1.2 Utilizzare il Sistema di Aiuto in MPL MPL offre agli utenti un sistema di aiuto che contiene informazioni utili su come usare questo software. Accesso agli argomenti di aiuto per MPL. Per aprire la nestra principale di aiuto per MPL, andiamo nel menù Help e scegliamo Topics. Ci verrà mostrata una nestra di aiuto, dove potremo selezionare l argomento che ci interessa. La nestra di dialogo degli argomenti di aiuto di MPL ( MPL Help Topics dialog box) contiene tre bottoni; il bottone Contents, il bottone Index e il bottone Find; fornendoci strade differenti per accedere agli aiuti. Il bottone Contents mostra tutti gli argomenti disponibili in una struttura ad albero. Il bottone Index ci permette di accedere alla lista di tutte le parole chiave contenute nel le di aiuto, oppure ci permette di eseguire delle ricerche per speci che parole chiave. Il bottone Find ci permette di ricercare negli argomenti di aiuto parole speci che o frasi nel testo. Il database di ricerca viene costruito automaticamente dal Find Setup Wizard la prima volta che selezionate Find. 2.2 Caratteristiche generali Vengono qui spiegate alcune caratterisctiche generali del linguaggio MPL. Lunghezza delle righe Ogni riga non può superare i 255 caratteri. I rimanenti verranno ignorati da MPL. 6
Commenti In MPL ci sono due tipi di commenti: le parentesi grafe {...} racchiudono commenti in blocco, cioè che possono estendersi anche su più righe { } Commento... il punto esclamativo! termina a ne riga delimita invece l inizio di un commento che x[foods]! dollars of food to be purchased daily Separatori I diversi statement all interno di una sezione (indici, vincoli, etc...) devono essere separati dal carattere ; i = 1..5 ; j = 1..12 ; Gli elementi di una lista di valori devono essere separati dalla virgola, o da un a capo (questo permette di introdurre dei commenti esplicativi accanto ad ogni valore). nutrients = (Calorie,Protein,Calcium,Iron,Vitamin); Required[nutrients] = ( 3! Calories 70! Protein 0.8! Calcium 12! Iron 75 ) ;! Vitamin Range di un insieme Si può esprimere un insieme di valori con l uso di caratteri speciali: [indice = min.. max] [indice > val] oppure selezionare un singolo indice di valore speci cato ["val"] 7
Formule Si possono scrivere espressioni che comprendono le normali operazioni aritmetiche +, -, *, / nonché diverse altre funzioni matematiche: sqr, sqrt, sin, cos, log, ext, power... Esiste inoltre la funzione sommatoria che richiede come primo parametro il nome dell indice su cui iterare e come secondo l espressione da valutare con l indice speci co: Cost = SUM(foods: x); 2.3 Struttura di un programma MPL La struttura di un le MPL è divisa in due parti fondamentali, ognuna delle quali è a sua volta suddivisa in diverse sezioni. La prima parte è di de nizione e comprende: TITLE INDEX DATA DECISION MACRO - Il nome del modello - Gli indici del problema - I dati (scalari, vettori e matrici multidimensionali) - Le variabili del problema - Macro riutilizzabili nelle varie espressioni La seconda parte invece descrive il modello ed è formata dalle seguenti sezioni: MODEL - Descrizione del problema MAX o MIN - Funzione obiettivo SUBJECT TO - Vincoli BOUNDS - Estremi superiori ed inferiori delle variabili FREE - Variabili libere INTEGER - Variabili intere BINARY - Variabili binarie (0/1) END - Fine del modello 2.3.1 TITLE Il titolo del modello non è obbligatiorio, ma è buona norma assegnare un titolo ad ogni problema e terminare la riga con il carattere ; 2.3.2 INDEX Gli indici de niscono il dominio del problema e la sua dimensione. Devono essere dichiarati con il carattere := e possono essere sia numerici che alfabetici. Per i primi è sufficiente speci care i due valori estremi separati dal carattere... 8
month := 1..12; Gli indici alfabetici invece, vengono dichiarati come lista di nomi, separati dalla virgola e racchiusi tra parentesi tonde. Questo secondo tipo è preferibile a primo in quanto rende molto più chiara la lettura di vincoli ed altre espressioni in cui essi vengono usati. nutrients := (Calorie,Protein,Calcium,Iron,Vitamin); Indici alias In MPL è possibile assegnare un nuovo nome ad uno stesso indice senza dover ripeterne la de nizione: i := 1..4; j := i; Indici circolari Per alcuni tipi di indice, in particolare quelli temporali, si ha la necessità di de nire un offset sull indice, ad esempio Stock[month-1]. Se l indice esce dal range assegnato MPL ignora quel particolare valore. Si può invece forzarlo a ripartire dalla parte opposta del range rendendo la lista circolare. Basta aggiungere alla dichiarazione dell indice la parola riservata CIRCULAR. day month := (mon, tue, wed, thu, fri, sat, sun) CIRCULAR; := 1..12 CIRCULAR; Sottoinsiemi di indici Se si vuole dichiarare un nuovo indice composto da un sottoinsieme di valori di un altro basta usare la seguente sintassi: holiday[day] := (sat, sun); summer[month] := (7..9); dove tra parentesi quadre viene speci cato l insieme di origine. Si possono anche eseguire le normali operazioni sugli insiemi: Differenza con il simbolo: - Negazione con il simbolo: NOT Unione con uno dei simboli: +, OR, UNION Intersezione con uno dei simboli: AND, INTERSECTION 9
INDEX plants := (NewYork, Chicago, London, Paris); OpenPlants[plants] := (NewYork, London); EuropePlants[plants] := (London, Paris);! Differenza ClosedPlants[plants] := plants - OpenPlants;! := (Chicago, Paris)! Negazione USPlants[plants] := NOT EuropePlants;! := (NewYork, Chicago)! Unione OpenOrEurope[plants] := OpenPlants OR EuropePlants;! := (NewYork, London, Paris)! Intersezione OpenAndEurope[plants] := OpenPlants AND EuropePlants;! := (London) Indici da le esterno La lista di valori di un indice può anche essere letta da un le esterno in cui i dati sono separati dalla virgola o da uno spazio. Se poi oltre all indice il le contiene anche altri dati, si può speci care quale colonna contiene i valori dell indice. product := INDEXFILE("product.dat", 1); NB: Se i dati sono su un foglio elettronico o un database esterno si può accedere anche direttamente ai dati con opportuni comandi. 2.3.3 DATA In questa sezione vengono speci cati i valori dei dati da usare nel modello. Anche in questo caso essi possono essere dichiarati direttamente o essere caricati da un le esterno, da un foglio elettronico o da un database, con il comando DATAFILE. I dati possono essere scalari, vettori o matrici multidimensionali. Nel caso scalare il valore assegnato risulta una costante per tutta la risoluzione del modello. Il carattere speciale? serve per chiedere conferma all utente del valore prede nito. NumberOfMonths = 12; NumberOfYears = 4?; In questo caso all utente viene chiesto di confermare o modi care il valore prede nito dello scalare NumberOfYears. 10
Per i vettori e le matrici, gli indici sono racchiusi tra parentesi quadre, e separati dalla virgola, mentre i relativi valori, sono racchiusi da parentesi tonde e separati da virgola o a capo. Required[nutrients] = ( 3, 70, 0.8, 12, 5, 2.7, 18, 75 ); A[foods,nutrients] = DATAFILE(nutri.dat)! Nutritive values of foods per dollar expenditure. Nel caso di una matrice, la lista corrisponde alla scansione per riga della matrice stessa. Lo stesso concetto si estende al caso di matrici multidimensionali. I dati possono essere valori semplici o il risultato di espressioni matematiche. A[i] := (2, -4+3, 2*SQR(3)+2, 1/(2+1), last(i)) Se si utilizzano vettori e matrici sparse, si possono inserire solo i valori non nulli; basta sostituire alle normali parentesi tonde quelle quadrate e far precedere ai singoli valori i relativi indici. A[i] := [2: 4.0, 5: 3.0, 6: -4.0] ; ProdCost[plant, machine, product] := [ p1, m11, A1, 73.30, p1, m11, A2, 52.90, p1, m12, A3, 65.40, p1, m13, A3, 47.60, 2.3.4 DECISION p2, m21, A1, 79.00, p2, m21, A3, 66.80, p2, m22, A2, 52.00, p3, m31, A1, 75.80, p3, m31, A3, 50.90, p3, m32, A1, 79.90, p3, m32, A2, 52.10, p4, m41, A1, 82.70, p4, m41, A2, 63.30, p4, m41, A3, 53.80]; Nella sezione DECISION vengono dichiarate le variabili del problema che possono essere scalari, vettori o matrici. Il nome completo della sezione 11
è DECISION VARIABLES, ma MPL accetta indifferentemente uno delle tre seguenti dichiarazioni: DECISION VARIABLES DECISION VARIABLES Nel caso di vettori o matrici, i relativi indici vanno racchiusi tra parentesi quadre e separati dalla virgola. Production[product,month] Condizioni sulle variabili A volte è necessario limitare le variabili al veri carsi di determinate condizioni. La clausola WHERE seguita dalla condizione, permette di selezionare solo alcune variabili. Production[product,month] WHERE (Demand[product,month] > 0); 2.3.5 MACROS Le macro sono una caratteristica importante di MPL. Esse permettono di de nire delle espressioni a cui assegnare un nome speci co da usare poi nella de nizione della funzione obiettivo e dei vincoli. In questo modo si sempli ca il modello rendendolo allo stesso tempo più leggibile. La macro si de nisce con un nome seguito dal simbolo := e dalla sua espressione. Non si possono dichiarare macro indicizzate, ma si possono usare vettori e formule nel calcolo della sua espressione. TotalRevenue := SUM(product,month: price * Sales) ; 2.3.6 MODEL La parola chiave MODEL indica l inizio della descrizione del modello vero e proprio. La funzione obiettivo richiede come primo elemento il senso di ottimizzazione: MAXIMIZE o MINIMIZE, che può essere abbreviato in MAX o MIN; segue il nome, il segno di uguaglianza = ed in ne l espressione da valutare. Se non viene indicato alcun nome MPL assegna per de nizione il nome z alla funzione obietttivo. MAX 3x1 + 5x2 ; MIN Cost = SUM(products,months : InventoryCost) ; 12
2.3.7 SUBJECT TO Questa è la sezione che descrive i vincoli del problema. È buona abitudine assegnare un nome ad ogni vincolo, ed anteporlo alla sua de nizione. Il nome non può contenere spazi vuoti ed altri caratteri riservati e deve essere seguito dal simbolo :. Se non viene dichiarato alcun nome per il vincolo i-esimo MPL assegna il generico nome ci. Un equazione è composta da due membri separati da uno dei seguenti segni di comparazione: minore o uguale < <= uguale = maggiore o uguale > >= I simboli < e > vengono interpretati come minore o uguale e maggiore o uguale. Per comodità e facilità di lettura sia le variabili che le costanti possono essere scritte in entrambi i lati della relazione. Le variabili possono anche essere ripetute nella stessa formula o nei due lati dello stesso vincolo. Ci pensa poi MPL ad accorpare i coefficienti e spostare tutte le variabili a sinistra e a sommare le costanti in un unico valore da porre sul lato destra della relazione. Ogni vincolo può estendersi anche su più righe e deve essere terminato con il simbolo ; per separarlo dal successivo. Ci sono due tipi di vincoli: semplici e vettoriali. Vincoli semplici I vincoli semplici sono quelli de niti senza indici; sono formati dalla combinazione di variabili semplici, vettori costanti ed eventualmente sommatorie su vettori di variabili purchè tutte le variabili del vettore siano utilizzate. 3 (Sb1 + Co1 + So1) = 2 (Sb2 + Co2 + So2) ; Overtime : Over < 50\% * 170 Workforce ; Production : SUM(shifts: Prod[shifts]) < 3750 ; Vincoli vettoriali I vincoli vettoriali sono quelli indicizzati, cioè vincoli che vengono ripetuti cambiando il valore dell indice ogni volta. Si dichiarano aggiungendo il nome dell indice racchiuso tra parentesi quadre subito dopo il nome del vincolo e prima del simbolo :. Ovviamente l indice del vincolo deve essere compatibile con quello dei vettori di variabili usati nella de nizione del vincolo stesso. MPL poi espande questo singolo vincolo in una serie di vincoli semplici basati sui relativi indici. La de nizione del vincolo avviene nello stesso modo dei vincoli semplici. Si costruiscono equazioni composte da variabili, vettori di variabili, sommatorie e costanti. Non occorre dichiarare l indice dopo ogni variabile, purchè questo sia compatibile con l indice principale del vincolo. 13
InventoryBalance[product,period]: Inventory = Inventory[period-1] + Production - Sales ; ProdCap[machine,time] : SUM(tire: ProdRates * Prod) < ProdHours ; Limitare i vincoli vettoriali Si può limitare il range di un indice per un vincolo vettoriale direttamente nella de nizione stessa del vicnolo. InventoryBalance[month=Jan..Nov] :... InventoryBalance[month<=Jun] :... InventoryBalance[month=Dec] :... Oppure si può de nire un vincolo al veri carsi di determinate condizioni InventoryBalance[product,month] WHERE (month > Jan) 2.3.8 BOUNDS In questa sezione vengono de niti i limiti inferiori e superiori alle variabili. La maggior parte dei risolutori LP prevede la possibilità di de nire bounds sulle variabili, migliorando l efficienza della loro gestione interna rispetto alla dichiarazione di vincoli espliciti. La de nizione dei bounds ri ette esatamente quella dei vincoli. Il limite può andare sul lato sinistro o destro della relazione e si possono anche de nire vincoli composti del tipo: limiteinf eriore < variabile < limitesuperiore Come per i vincoli anche i bounds possono essere semplici o vettoriali (indicizzati su uno o più indici). Il limite inferiore è per de nizione 0. x < 4*12 ; z_bounds : 2 < z < 8 ; CloseInv : Inventory[December] = 20000 ; MaxInv[month<=Nov] : Inventory <= 90000 ; Inventory[month=Dec] = 90000 ; Sales <= Demand ; 14
2.3.9 FREE Questa e le due successive sezioni servono per de nire il tipo di variabili usate e possono essere poste in qualsiasi ordine purchè dopo la sezione di dichiarazione dei vincoli (SUBJECT TO). MPL come la maggior parte dei risolutori LP, assume che le variabili decisionali siano positive o nulle. Per permettere ad una variabile semplice o ad un vettore di variabili di assumere anche valori negativi bisogna dichiararli come liberi. FREE Temperature ; Inventory[month] ; 2.3.10 INTEGER e BINARY Allo stesso modo si possono forzare delle variabili ad assumere solamente valori interi; basta anteporre la porola INTEGER alla lista di variabili e/o vettori di variabili. INTEGER Production[month,product] ; Si possono anche forzare alcune variabili ad assumere solamente i valori interi 0 e 1. BINARY ShopOpen ; In entrambi i casi questa forzatura ha effetto soltatno se il pacchetto risolutore supporta questa caratteristica, ossia risolve problemi MIP (Mixed Integer Programming). 15
Capitolo 3 Un modello di bilanciamento della produzione In questo capitolo costruiremo un modello di produzione con tre prodotti che verranno chiamati A1, A2 e A3. Per questi prodotti creeremo un indice e, quindi, de niremo un vettore di variabili che rappresenta la necessità di produzione di ogni prodotto. Data la de nizione di questi nuovi termini, indici e vettori, ora dovremo applicarla a un modello di prova. Il modello di bilanciamento della produzione è un problema di distribuzione della forza produttiva tra i prodotti, con lo scopo di determinare il livello di produzione di ogni singolo prodotto per soddisfare una data domanda. Il prezzo di vendita di ogni prodotto è ssato: $120.00 per A1, $100.00 per A2 e $115.00 per A3. Per ogni prodotto, esiste anche un limite massimo di domanda: 4300 per A1, 4500 per A2 e 5400 per A3. La quota di produzione di un prodotto è misurata in quanti pezzi vengono prodotti ogni giorno. In questo problema abbiamo un totale di 22 giorni di produzione disponibili in un mese. Nella tabella seguente sono indicati la quota e il costo di produzione di ogni prodotto. Produzione A1 A2 A3 Costo di Produzione $73.30 $52.90 $65.40 Quota di Produzione 500 450 550 3.1 Formulazione del Modello Il prossimo passo è quello di prendere il problema appena descritto e formularne un modello per MPL. Qui di seguito riportiamo il codice MPL del modello ( le Planning3.mpl). { Planning3.mpl } 16
TITLE Producton_Planning3; INDEX product := (A1, A2, A3); DATA Price[product] := (120.00, 100.00, 115.00); Demand[product] := (4300, 4500, 5400); ProdCost[product] := (73.30, 52.90, 65.40); ProdRate[product] := (500, 450, 550); ProdDaysAvail := 22; VARIABLES Produce[product] -> Prod; MACROS TotalRevenue := SUM(product: Price * Produce); TotalCost := SUM(product: ProdCost * Produce); MODEL MAX Profit = TotalRevenue - TotalCost; SUBJECT TO ProdCapacity -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; BOUNDS Produce <= Demand; END 3.2 Vedere e Analizzare la Soluzione Dopo aver risolto il modello, MPL crea automaticamente un le che contiene in un formato standard vari elementi della soluzione del modello. Questo le include, tra le altre cose, il valore ottimo della funzione obbiettivo, le attività dei costi ridotti delle variabili, i valori di slack e i costi nascosti ( shadow price ) per i vincoli. Il le della soluzione viene creato con lo stesso nome del le del modello, ma con l estensione.sol. Nel nostro caso il le si chiamerà Planning3.sol. 17
Dopo aver risolto il modello possiamo visualizzare la soluzione in una nestra cliccando il tasto View che si trova sul fondo della nestra di stato. Questa operazione mostrerà in una nestra ( View Window ) i dati che qui riportiamo. MPL Modeling System - Copyright (c) 1988-2000, Maximal Software, Inc. ------------------------------------------------------------------------------- MODEL STATISTICS Problem name: Producton_Planning3 Filename: Planning3.mpl Date: July 8, 2000 Time: 13:53 Parsing time: 0.33 sec Solver: CPLEX 300 Objective value: 544566.636364 Iterations: 3 Solution time: 0.60 sec Constraints: 1 Variables: 3 Nonzeros: 3 Density: 100 % SOLUTION RESULT Optimal solution found MAX Profit = 544566.6364 MACROS Macro Name Values ----------------------------------------------- 18
TotalRevenue 1298181.8182 TotalCost 753615.1818 ----------------------------------------------- DECISION VARIABLES VARIABLE Produce[product] : product Activity Reduced Cost --------------------------------------------- A1 4300.0000 4.3100 A2 1611.8182 0.0000 A3 5400.0000 11.0636 --------------------------------------------- CONSTRAINTS PLAIN CONSTRAINTS END Constraint Name Slack Shadow Price ------------------------------------------------------ ProdCapacity 0.0000 21195.0000 ------------------------------------------------------ La prima parte del le della soluzione contiene diversi dati statistici del modello come il nome del le, la data e l ora in cui il modello è stato risolto, quale risolutore è stato usato, il valore della funzione obbiettivo e la dimensione del modello. La parte successiva contiene i risultati della soluzione. In questa parte possiamo vedere se la soluzione trovata è ottimale, illimitata oppure impossibile. Inoltre viene mostrato il nome e il valore della funzione obbiettivo. Nella sezione MACROS del le della soluzione possiamo avere la lista di tutte le macros del modello de nite insieme al loro 19
valore. Per esempio, nel nostro modello Planning3 le entrate totali sono di 1,298 milioni di dollari, mentre le spese sono circa 754.000 dollari. Questo corrisponde ad un pro tto di circa $545.000, che è il valore della funzione obbiettivo. Nella sezione DECISION VARIABLES possiamo trovare la lista di tutte le variabili del modello: vettore di variabili e variabili semplici. Nel nostro caso abbiamo un solo vettore di variabili produzione (Produce) de nito su l indice prodotti. Potremo osservare che per i prodotti A1 e A3 la soluzione suggerisce di produrre rispettivamente 4300 e 5400 pezzi. Questi valori corrispondono alla domanda per questi prodotti. D altro canto per A2 viene suggerito di produrre non più di 1611 pezzi, questo corrisponde ad una produzione inferiore alla domanda. Chiaramente non abbiamo la possibilità di produrre abbastanza da coprire la domanda di tutti i prodotti e il modello scelto consiglia di scegliere la produzione di A1 e A3 no a coprirne la domanda, sacri cando la produzione di A2. Nella sezione CONSTRAINTS è contenuta la lista dei vincoli compresi nel modello: vettori di vincoli e vincoli semplici. Nel nostro modello abbiamo solo un vincolo di tipo semplice chiamato ProdCapacity. Siccome lo slack del vincolo vale zero risulta che la nostra produzione funziona a pieno regime. I costi nascosti ci informano a quanto ammonterebbe il mancato guadagno se ci trovassimo nella necessità di ridurre i limiti dei vincoli di una unità. Dato che la capacità di produzione è espressa in giorni di produzione, la riduzione dei giorni disponibili per la produzione diminuisce i pro tti di $21.195 al giorno. 20
Capitolo 4 Esempi di risoluzione di problemi classici della Programmazione Lineare In questo capitolo vengono analizzati alcuni problemi classici di Programmazione Lineare e ne viene fornita la descrizione del modello con il linguaggio MPL. 4.1 Un problema di assegnamento Un azienda deve eseguire N lavori diversi avendo a disposizione N persone. Ognuna impiega un determinato tempo a svolgere un certo incarico secondo la tabella riportata sotto. Sapendo che la ditta paga i suoi dipendenti ad un determinato prezzo orario, come devono essere assegnati gli incarichi alle diverse persone per minimizzare il costo complessivo? Lavoro Bianchi Verdi Rossi Ferrari lavoro A 12 15 9 5 lavoro B 13 16 11 6 lavoro C 6 8 5 3 lavoro D 5 7 4 3 Poichè il costo è direttamente proporzionale al tempo impiegato, è sufficiente minimizzare il tempo totale. Introduciamo le variabili binarie xij con il seguente signi cato: x = 1 se il lavoro i-esimo viene assegnato alla j-esima persona ij 0 altrimenti La funzione da minimizzare sarà quindi: tij xij i,j 21
soggetta ai vincoli che ogni persona può svolgere esattamente un lavoro: xij = 1 j = 1.. N i e che ogni lavoro può essere assegnato solamente ad una persona: xij = 1 i = 1.. N j Vediamo ora come si traduce questo modello in MPL. TITLE Problema_di_Assegnamento; INDEX persone = (Bianchi, Verdi, Rossi, Ferrari); incarichi = (lavoroa, lavorob, lavoroc, lavorod); DATA! tempo impiegato dalla persona j a svolgere il lavoro i tempi[persone, incarichi] = ( 12, 15, 9, 5, 13, 16, 11, 6, 6, 8, 5, 3, 5, 7, 4, 3 ); DECISION x[incarichi, persone]! persona che svolge l incarico MODEL MIN tempototale = Sum(persone, incarichi: tempi * x); SUBJECT TO vincolo_incarichi[persone] : Sum(incarichi: x) = 1; vincolo_persone[incarichi] : Sum(persone: x) = 1; BINARY x[persone, incarichi]; END Se si risolve questo modello si avra il seguente assegnamento ottimo: lavoro A ==> Rossi 22
lavoro B lavoro C lavoro D ==> Ferrari ==> Bianchi ==> Verdi di tempo totale minimo = 28. 4.2 Un primo problema dei trasporti Una ditta di trasporto deve trasferire container vuoti dai propri magazzini ai principali porti nazionali. Le disponibilità di container vuoti ai magazzini e le richieste ai porti sono le seguenti: Verona 10 Genova 20 Perugia 12 Venezia 15 Roma 20 Ancona 25 Pescara 24 Napoli 33 Taranto 18 Bari 21 Lamezia 40 I costi di trasporto sono proporzionali al numero di container ed ai chilometri percorsi dai camion, secondo la seguente tabella: Genova Venezia Ancona Napoli Bari Verona 290 115 355 715 810 Perugia 380 340 165 380 610 Roma 505 530 285 220 450 Pescara 655 450 155 240 315 Taranto 1010 840 550 305 95 Lamezia 1072 1097 747 372 333 Si vuole determinare la politica di trasporto di costo complessivo minimo. Cominciamo col de nire le variabili intere xij come segue: xij = numero di container trasferiti dal magazzino ial porto j La funzione obiettivo da minimizzare è il costo complessivo di trasporto: distanzeij xij costo unitario i,j soggetta ai vincoli che ogni magazzino di partenza può spedire tanti container quant è la sua disponibilità: xij dispi i =1.. 6 j 23
e che ogni porto di distinazione deve soddisfare la propria richiesta: x rich j =1.. 5 i ij Vediamo come tradurre questo modello in MPL TITLE Problema_di_Trasporto; j INDEX magazzini = ( Verona, Perugia, Roma, Pescara, Taranto, Lamezia ); porti = ( Genova, Venezia, Ancona, Napoli, Bari ); DATA disponibilita[magazzini] = ( 10, 12, 20, 24, 18, 40 ); richiesta[porti] = ( 20, 15, 25, 33, 21 ); distanze[magazzini, porti] = DATAFILE ("Distanze.dat"); costo_unitario = 300;! costo per chilometro DECISION! numero di containter spediti dal magazzino i al porto j x[magazzini, porti] MODEL! funzione obiettivo MIN costototale = Sum(magazzini, porti: distanze * x * costo_unitario); SUBJECT TO vincolo_disponibilita[magazzini] : Sum(porti: x) <= disponibilita; vincolo_richiesta[porti] : Sum(magazzini: x) >= richiesta; INTEGER x[magazzini, porti]; END Per come abbiamo de niti gli INDEX, magazzini e porti, il le Distanze.dat contiene la tabella dei chilometri percorsi esattamente così come è riportata nella pagina precedente. 24
{ Datafile per TRASPORTI.MPL } Genova Venezia Ancona Napoli Bari \\ Verona 290 115 355 715 810 \\ Perugia 380 340 165 380 610 \\ Roma 505 530 285 220 450 \\ Pescara 655 450 155 240 315 \\ Taranto 1010 840 550 305 95 \\ Lamezia 1072 1097 747 372 333 \\ Risolvendo questo modello con MPL si avrà la seguente con gurazione di trasporto ottimo: Genova Venezia Ancona Napoli Bari Verona - 10 - - - Perugia 7 5 - - - Roma 13-1 6 - Pescara - - 24 - - Taranto - - - - 18 Lamezia - - - 27 3 di costo minimo = 9.045.900. 4.3 Un problema della dieta Una mensa deve piani care gli acquisti di alimenti per la sua attività. Nella formulazione della dieta deve obbedire a requisiti nutrizionali minimi, nonché vincolare le porzioni massime di ogni elemento entro certi limiti. Conoscendo i costi unitari dei vari alimenti, trovare la dieta ottima che minimizzi il costo complessivo rispettando i vincoli imposti. Alimento Costo unitario Quantità massima Pane 2 4 Latte 3 8 Uova 4 3 Carne 19 2 Dolce 20 2 Requisiti nutrizionali minimi Nutrimento Calorie Proteine Calcio requisito 200 cal 50g 700 mg 25
Dalle tabelle dietetiche si ricavano i seguenti contenuti di calorie, proteine e calcio per ogni singola porzione di ciascun alimento. Calorie Proteine Calcio Pane 110 4 2 Latte 160 8 285 Uova 180 13 54 Carne 260 14 80 Dolce 420 4 22 Le variabili di questo problema sono le quantità xi di ogni alimento da inserire nella dieta. La funzione obiettivo da minimizzare è il costo complessivo: costoi xi i Un primo vincolo è quello dell apporto nutrizionale: apportoij richiestej j nutrimenti i alimenti Esiste poi un limite massimo alle quantità dei singoli alimenti. x maxp orz i alimenti i i Questo limite può essere imposto introducendo dei nuovi vincoli oppure, in modo più efficiente, utilizzando degli upper bound. Vediamo come convertire questo modello nel linguaggio MPL. TITLE Dieta_ottima INDEX nutrimenti = (Calorie,Proteine,Calcio) : 10 alimenti = (Pane, Latte, Uova, Carne, Dolce) : 10 DATA richieste[nutrimenti] = ( 2000! Calorie [] 50! Proteine [grammi] 700) ;! Calcio [milligrammi] costo[alimenti] = ( 2! Pane 3! Latte 4! Uova 26
Segue il le Nutrimen.dat. 19! Carne 20 ) ;! Dolce maxporz[alimenti] = ( 4! Pane Massimo numero di porzioni 8! Latte tollerato giornalmente 3! Uova 2! Carne 2 ) ;! Dolce apporto[alimenti,nutrimenti] = DATAFILE(nutrimen.dat)! Contenuti di nutrimenti nei diversi alimenti DECISION x[alimenti] -> ""! porzioni di ogni alimento MODEL MIN Cost = SUM(alimenti: costo*x) ; SUBJECT TO BOUNDS END NutrBal[nutrimenti] : SUM(alimenti: apporto*x) > richieste[nutrimenti]; MaxPorzioni : x < maxporz ; { Datafile per DIETA.MPL }! Valori Nutritivi degli alimenti Calorie Proteine Calcio! (cal) (grams) (migrams) Pane 110 4 2 Latte 160 8 285 Uova 180 13 54 Carne 260 14 80 27
Dolce 420 4 22 Risolvendo questo modello con MPL si avrà la seguente dieta ottima: Alimento quantitá Pane 4 Latte 8 Uova 1.5556 Carne 0 Dolce 0 di costo minimo = 38.222. Come si può notare la soluzione ottima prevede un valore non intero per l alimento Uova. Se vogliamo forzare la soluzione ad assumere solo valori interi basterà aggiungere il seguente vincolo di interezza al modello subito dopo gli altri vincoli: INTEGER x;! variabili intere La nuova soluzione intera risulta essere ora: Alimento quantitá Pane 4 Latte 8 Uova 2 Carne 0 Dolce 0 di costo minimo = 40. Il fatto che la soluzione intera sia semplicemente quella lineare con l unica variabili non intera arrotondata per eccesso è puramente casuale. Spesso la soluzione intera e la sua versione rilassata sono assai diverse. 4.4 Un problema di produzione più complesso Un azienda produce 5 diversi tipi di gomma (PVC1, PVC2, PVC3, Gomma1 e Gomma2) con i seguenti costi e ricavi unitari. Prodotto Costo Ricavo PVC1 30 130 PVC2 45 200 PVC3 20 120 Gomma1 10 115 Gomma2 25 165 28
Sapendo che i costi orari e la disponibilità di ogni macchina è: De niamo per prima cosa le variabili del problema: siano xi le quantità da produrre di ogni prodotto. La funzione obiettivo da massimizzare è il guadagno netto, dato dalla differenza tra ricavi e costi unitari dei singoli prodotti e costi di produzione di ogni macchina in base al loro utilizzo: ( Ricavoi Costoi) xi ( Costo orarioj tempoij x i) / 3600 i j i Vediamo come convertire questo modello nel linguaggio MPL. { Produz.mpl } { Mix ottimale della produzione } TITLE Opt_Mix Produzione; Macchina Costo orario Disponibilità macpvc 90 60 macgomma 100 90 Tagliom 50 130 Pallets 40 70 Avvolgimento 30 40 e che i tempi di lavorazione in ore di ciascuna machina per ottenere un unità di prodotto nita sono i seguenti: macchina / prodotto PVC1 PVC2 PVC3 GOMMA1 GOMMA2 macpvc 2 1 1 0 0 macgomma 0 0 0 3 4 Taglio 1 3 1 1 0.5 Pallets 0.5 1 1 0 0 Avvolgi 0 0 0 0.2 0.1 calcolare la quantità ottimale da produrre per ogni prodotto in modo da massimizzare i guadagni. L unico vincolo è che non si superi la disponibilità di utilizzo di ogni macchina: tempoij x i < Of f ertaj j macchina i INDEX prodotto := (PVC1, PVC2, PVC3, Gomma1, Gomma2); 29
macchina := (macpvc, macgomma, Taglio, Pallets, Avvolgi); DATA Costo_mat[prodotto] = ( 30! PVC1 (in lire) 45! PVC2 20! PVC3 10! GOMMA1 25 ) ;! GOMMA2 Ricavo[prodotto] = ( 130! PVC1 (in lire) 200! PVC2 120! PVC3 115! GOMMA1 165 ) ;! GOMMA2 Offerta[macchina] = 3600 * ( 60! macpvc 90! macgomma 130! Taglio 70! Pallets 40 ) ;! Avvolgimento Costo_orario[macchina] = 1000 * ( 90! macpvc 100! macgomma 50! Taglio 40! Pallets 30 ) ;! Avvolgimento tempo[macchina,prodotto] = DATAFILE(mixprod.dat)! tempi di ciascuna machina per ottenere un unita di prodotto DECISION x[prodotto] -> ""! quantita di prodotti che conviene produrre MACRO Ricavi := SUM(prodotto: Ricavo * x - Costo_mat * x); MODEL TotaleCosto := SUM(prodotto, macchina : Costo_orario * tempo * x / 3600 ); Max Guadagno = Ricavi - TotaleCosto ; SUBJECT TO 30
N[macchina] -> "" : SUM(prodotto: tempo * x) < Offerta[macchina]; END Segue il le mixprod.dat. { Datafile per Produz.MPL }! Tempi di lavorazione in ore di ciascuna machina per ottenere! una unità di prodotto finita PVC1 PVC2 PVC3 GOMMA1 GOMMA2 macpvc 2 1 1 0 0 macgomma 0 0 0 3 4 Taglio 1 3 1 1 0.5 Pallets 0.5 1 1 0 0 Avvolgi 0 0 0 0.2 0.1 La soluzione ottima per la produzione risulta: Prodotto quantità PVC1 105.750 PVC2 110.250 PVC3 0 Gomma1 81.000 Gomma2 0 per un guadagno complessivo di L. 15.388.750. 31
Capitolo 5 Un Modello di Piani cazione della Produzione a Multi-Periodo In questo capitolo creeremo una formulazione di un modello di piani cazione della produzione a multi-periodo. Partiamo dal modello che abbiamo creato nel capitolo 3 facendo le opportune modi che. In questo nuovo problema abbiamo un periodo di quattro mesi da piani care, da Gennaio a Aprile. Per prima cosa dobbiamo creare un nuovo indice che contiene i quattro mesi in esame, quindi aggiornare il resto del modello aggiungendo gli indici ai vettori già de niti. Come nel problema del capitolo 3, il prezzo di vendita per i prodotti è ancora rispettivamente $120.00, $100.00 e $115.00. Ora, oltre ad avere una domanda diversa per ogni prodotto, abbiamo una domanda differenziata per ogni prodotto e ogni mese. Nella tabella seguente riportiamo i valori. Domanda di Produzione Gen Feb Mar Apr A1 4300 4200 6400 5300 A2 4500 5400 6500 7200 A3 5400 6700 7800 8200 Le quote di produzione e i costi di produzione resteranno gli stessi che sono stati forniti nella tabella del capitolo 3 a pag. 16. Inoltre i giorni di produzione disponibili in un mese varieranno ogni mese: 23 giorni a Gennaio, 20 a Febbraio, 23 a Marzo e 22 a Aprile. Inoltre dovremo introdurre un magazzino nel modello; questo comporta l introduzione di un costo di magazzino per ogni prodotto: A1-$3.50/mese, A2-$4.00/mese e A3-$3.00/mese. Ogni prodotto occupa lo stesso spazio, ma lo spazio totale disponibile nel magazzino è di 800 unità. 32
5.1 Formulazione del Modello Il listato seguente è la formulazione del modello di Planning4. Come si può osservare il modello è un estensione di quello presentato nel capitolo 3 a pag. 16. { Planning4.mpl } TITLE Production_Planning4; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); DATA Price[product] := (120.00, 100.00, 115.00); Demand[product, month] := (4300, 4200, 6400, 5300, 4500, 5400, 6500, 7200, 5400, 6700, 7800, 8200); ProdCost[product] := (73.30, 52.90, 65.40); ProdRate[product] := (500, 450, 550); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[product] := (3.50, 4.00, 3.00); InvtCapacity := 800; VARIABLES Produce[product, month] Inventory[product, month] Sales[product, month] -> Prod; -> Invt; -> Sale; MACROS TotalRevenue := SUM(product, month: Price * Sales); TotalProdCost := SUM(product, month: ProdCost * Produce); TotalInvtCost := SUM(product, month: InvtCost * Inventory); TotalCost := TotalProdCost + TotalInvtCost; MODEL MAX Profit = TotalRevenue - TotalCost; SUBJECT TO ProdCapacity[month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; 33
InvtBal[product, month] -> IBal: Produce + Inventory[month-1] = Sales + Inventory; MaxInventory[month] -> MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales <= Demand; END 5.2 Risolvere il Modello e Analizzare la Soluzione Il prossimo passo è quello di risolvere il modello Planning4: basta scegliere la voce Solve CPLEX nel menù Run. Se i dati sono stati inseriti correttamente, MPL mosterà il messaggio che la soluzione ottimale è stata trovata ( Optimal Solution Found). Se si presenta un messaggio di errore in una nestra, indicazione di un errore di sintassi, ricontrolliamo la formulazione del modello che abbiamo inserito confrontandola con quella fornita a pag. 33. Dopo aver risolto il modello, MPL crea automaticamente un le chiamato Planning4.sol nel formato standard contenente la soluzione. Possiamo visualizzare il le della soluzione in una nestra con la pressione del tasto View che si trova sul fondo della nestra di stato. Riportiamo qui il listato completo del le della soluzione: MPL Modeling System - Copyright (c) 1988-2000, Maximal Software, Inc. ----------------------------------------------------------------------------- MODEL STATISTICS Problem name: Production_Planning4 Filename: Planning4.mpl Date: July 8, 2000 Time: 20:13 Parsing time: 0.22 sec Solver: CPLEX 300 Objective value: 2246007.27273 Iterations: 0 Solution time: 0.06 sec 34
Constraints: 20 Variables: 36 Nonzeros: 69 Density: 10 % SOLUTION RESULT Optimal solution found MAX Profit = 2246007.2727 MACROS Macro Name Values ----------------------------------------------- TotalRevenue 5386045.4545 TotalProdCost 3139078.1818 TotalInvtCost 960.0000 TotalCost 3140038.1818 ----------------------------------------------- DECISION VARIABLES VARIABLE Produce[product,month] : product month Activity Reduced Cost ---------------------------------------------------- A1 Jan 4300.0000 0.0000 A1 Feb 4200.0000 0.0000 A1 Mar 4409.0909 0.0000 A1 Apr 3545.4545 0.0000 A2 Jan 1800.0000 0.0000 35
A2 Feb 0.0000-3.6667 A2 Mar 0.0000-4.7889 A2 Apr 0.0000-0.7889 A3 Jan 5720.0000 0.0000 A3 Feb 6380.0000 0.0000 A3 Mar 7800.0000 0.0000 A3 Apr 8200.0000 0.0000 ---------------------------------------------------- VARIABLE Inventory[product,month] : product month Activity Reduced Cost ---------------------------------------------------- A1 Jan 0.0000-0.2000 A1 Feb 0.0000-2.4900 A1 Mar 0.0000-3.5000 A1 Apr 0.0000-123.5000 A2 Jan 0.0000-4.0000 A2 Feb 0.0000-4.0000 A2 Mar 0.0000 0.0000 A2 Apr 0.0000-108.0000 A3 Jan 320.0000 0.0000 A3 Feb 0.0000-2.0818 A3 Mar 0.0000-3.0000 A3 Apr 0.0000-110.8545 ---------------------------------------------------- VARIABLE Sales[product,month] : product month Activity Reduced Cost ---------------------------------------------------- A1 Jan 4300.0000 4.3100 A1 Feb 4200.0000 1.0100 A1 Mar 4409.0909 0.0000 A1 Apr 3545.4545 0.0000 A2 Jan 1800.0000 0.0000 A2 Feb 0.0000 0.0000 A2 Mar 0.0000 0.0000 A2 Apr 0.0000-4.0000 A3 Jan 5400.0000 11.0636 36
A3 Feb 6700.0000 8.0636 A3 Mar 7800.0000 7.1455 A3 Apr 8200.0000 7.1455 ---------------------------------------------------- CONSTRAINTS CONSTRAINT ProdCapacity[month] : month Slack Shadow Price ------------------------------------------- Jan 0.0000 21195.0000 Feb 0.0000 22845.0000 Mar 0.0000 23350.0000 Apr 0.0000 23350.0000 ------------------------------------------- CONSTRAINT InvtBal[product,month] : product month Slack Shadow Price ---------------------------------------------------- A1 Jan 0.0000-115.6900 A1 Feb 0.0000-118.9900 A1 Mar 0.0000-120.0000 A1 Apr 0.0000-120.0000 A2 Jan 0.0000-100.0000 A2 Feb 0.0000-100.0000 A2 Mar 0.0000-100.0000 A2 Apr 0.0000-104.0000 A3 Jan 0.0000-103.9364 A3 Feb 0.0000-106.9364 A3 Mar 0.0000-107.8545 A3 Apr 0.0000-107.8545 ---------------------------------------------------- 37
CONSTRAINT MaxInventory[month] : month Slack Shadow Price ------------------------------------------- Jan 480.0000 0.0000 Feb 800.0000 0.0000 Mar 800.0000 0.0000 Apr 800.0000 0.0000 ------------------------------------------- END Il pro tto ora risulta di 2,2 milioni di dollari che è considerevolmete più alto di quello calcolato per il modello Planning3; questo è naturale dato che ora lavoriamo su un periodo di quattro mesi. Il pro tto è il risultato di un introito pari a 5,4 milioni di dollari e un importo dei costi pari a 3,1 milioni di dollari, la maggior parte dei quali è dato dal costo di produzione, dato che quello che viene conservato in magazzino è poco già a partire da Gennaio. Se si osserva nella soluzione la variabile Produce (produzione) notiamo che abbiamo prodotto A1 e A3 per l intero periodo piani cato, ma non è sempre stato possibile soddisfare la domanda. D altro canto del prodotto A2 sono stati prodotte solo 1800 unità in Gennaio, questo signi ca che non abbiamo la capacità di produrre tutti e tre i prodotti. In Gennaio il modello ha deciso di produrre, oltre alla domanda, 320 unità di A3, con lo scopo di immagazinarli per soddisfare la domanda di Febbraio. 38
Capitolo 6 Un Modello di Piani cazione con più Impianti di Produzione In questo capitolo creeremo una nuovo modello per la piani cazione della produzione che include la presenza di più impianti oltre a considerare periodi differenziati come nel capitolo 5. Quello che dobbiamo decidere è quanto bisogna produrre di ogni prodotto per ogni mese, in ogni impianto, cercando di ottimizare, per ogni mese e per ogni impianto, la vendita e il deposito nel magazzino. In questo nuovo problema avremo quattro differenti impianti p1, p2, p3 e p4. Ognuno di questi impianti produce tutti e tre i prodotti. Creiamo un indice chiamato plants (impianti) che contiene i quattro differenti impianti e quindi aggiorniamo il modello con l aggiunta dell indice ai vettori che ne necessitano. Come nelle sezioni precedenti, il prezzo di vendita per ogni prodotto è rispettivamente $120.00, $100.00 e $150.00. La domanda dei vari prodotti rimane la stessa del capitolo precedente; ci riferiamo alla tabella della domanda del capitolo 5 a pag. 32. Ora abbiamo più di un impianto di produzione e il costo di produzione di ogni prodotto è diverso per ogni impianto. Riportiamo questi dati nella tabella seguente. Costi di Produzione A1 A2 A3 impianto 1 73.30 52.90 65.40 impianto 2 79.00 52.00 66.80 impianto 3 75.80 52.10 50.90 impianto 4 82.70 63.30 53.80 Anche la quota di produzione di ogni prodotto è diversi cata a seconda dell impianto come riportiamo nella seguente tabella. 39
Quota di Produzione A1 A2 A3 impianto 1 500 450 450 impianto 2 550 450 300 impianto 3 450 350 300 impianto 4 550 400 350 6.1 Formulazione del Modello Il listato seguente è la formulazione del modello per Planning5. { Planning5.mpl } TITLE Production_Planning5; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4); DATA Price[product] := (120.00, 100.00, 115.00); Demand[product, month] := DATAFILE("Demand.dat"); ProdCost[plant, product] := DATAFILE("ProdCost.dat"); ProdRate[plant, product] := DATAFILE("ProdRate.dat"); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[product] := (3.50, 4.00, 3.00); InvtCapacity := 800; VARIABLES Produce[plant, product, month] Inventory[product, month] Sales[product, month] -> Prod; -> Invt; -> Sale; MACROS TotalRevenue := SUM(product, month: Price * Sales); TotalProdCost := SUM(plant, product, month: ProdCost * Produce); TotalInvtCost := SUM(product, month: InvtCost * Inventory); TotalCost := TotalProdCost + TotalInvtCost; MODEL MAX Profit = TotalRevenue - TotalCost; 40
SUBJECT TO ProdCapacity[plant, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; InvtBal[product, month] -> IBal: SUM(plant: Produce) + Inventory[month-1] = Sales + Inventory; MaxInventory[month] -> MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales <= Demand; END 6.2 Risolvere il Modello e Analizzare la Soluzione Dato che abbiamo aggiunto diversi indici al modello, il numero delle variabili è cresciuto in maniera considerevole. Tipicamente, quando si lavora con grandi modelli, il creatore del modello preferisce includere solo le variabili diverse da zero. MPL ha diverse opzioni che possono essere impostate tramite il menù Options; mediante queste opzioni possiamo cambiare il comportamento del programma. Una delle possibilità nel menù Options è quella di aprire la nestra di dialogo delle opzioni relative al le della soluzione; in questa nestra di dialogo possiamo scegliere a vostro piacere cosa includere e cosa escludere nel le della soluzione. Per cambiare i valori standard e quindi includere solo i valori diversi da zero nel le della soluzione, è sufficiente fare i seguenti passi: 1. Nel menù Options scegliere Solution File per aprire la nestra di dialogo ( Options Dialog Box). 2. Attivare l opzione Nonzero Values Only cliccando sul quadratino relativo. 3. Chiudere la nestra di dialogo premendo il tasto OK. Dopo aver effettuato il cambiamento che permette di non scrivere i valori nulli, il prossimo passo è quello di risolvere il modello scegliendo Solve CPLEX nel menù Run. Se tutto ha funzionato a dovere MPL mostrerà il messaggio Optimal solution Found. Se si presenta un messaggio di errore in una nestra, indicazione di un errore di sintassi, ricontrolliamo la formulazione del modello che abbiamo inserito confrontandola con quella fornita a pag. 40. 41
Dato che il modello su cui stiamo lavorando sta diventando piuttosto grande ci occuperemo solo di guardare determinate parti della soluzione invece che la soluzione completa. A questo punto, useremo l albero delle de nizioni del modello per visualizzare solo le parti della soluzione alle quali siamo interessati. La nestra delle de nizioni del modello ( Model De nitions Windows) ci permette di vedere tutte le istanze de nite nella formulazione del modello in una struttura ad albero dove ad ogni ramo corrisponde una sezione del modello. Nell uso di MPL è normalmente una buona idea lasciare la nestra con l albero delle de nizioni sempre visibile. MPL provvede all aggiornamento automatico del contenuto della nestra delle de nizioni ogni volta che risolviamo un modello. Per visualizzare la nestra delle de nizioni del modello scegliamo Model De nitions nel menù View. Nella nestra con l albero delle de nizioni possiamo selezionare una qualsiasi delle istanze de nite nel modello per osservarne i valori. Per esempio, per vedere il valore della variabile Produce basta un doppio click sulla scritta Produce nell albero, oppure, alternativamente, selezionando la scritta e premendo il tasto View. Questa operazione visualizzerà in una nestra contenente solo i valori della variabile Produce. VARIABLE Produce[plant,product,month] : plant product month Activity Reduced Cost ----------------------------------------------------------- p1 A1 Jan 4300.0000 0.0000 p1 A1 Feb 4200.0000 0.0000 p1 A1 Mar 6400.0000 0.0000 p1 A1 Apr 5300.0000 0.0000 p2 A2 Jan 4500.0000 0.0000 p2 A2 Feb 5400.0000 0.0000 p2 A2 Mar 6500.0000 0.0000 p2 A2 Apr 7200.0000 0.0000 p3 A3 Jan 5400.0000 0.0000 p3 A3 Feb 6000.0000 0.0000 p3 A3 Mar 6900.0000 0.0000 p3 A3 Apr 6600.0000 0.0000 p4 A3 Feb 700.0000 0.0000 p4 A3 Mar 900.0000 0.0000 p4 A3 Apr 1600.0000 0.0000 ----------------------------------------------------------- Se osserviamo i valori dell attività per la variabile Produce ci accorgere che ora siamo in grado di saturare completamente la domanda per ogni prodotto. Per esempio, l impianto p1 è usato per produrre A1, l impianto p2 è usato per produrre A2, gli impianti p3 e p4 sono usati per produrre A3. 42
Torniamo ora alla nestra con l albero delle de nizioni per visualizzare una nestra contenente i vincoli ProdCapacity; avremo i seguenti valori per la soluzione: CONSTRAINT ProdCapacity[plant,month] : plant month Slack Shadow Price -------------------------------------------------- p1 Jan 14.4000 0.0000 p1 Feb 11.6000 0.0000 p1 Mar 10.2000 0.0000 p1 Apr 11.4000 0.0000 p2 Jan 13.0000 0.0000 p2 Feb 8.0000 0.0000 p2 Mar 8.5556 0.0000 p2 Apr 6.0000 0.0000 p3 Jan 5.0000 0.0000 p4 Jan 23.0000 0.0000 p4 Feb 18.0000 0.0000 p4 Mar 20.4286 0.0000 p4 Apr 17.4286 0.0000 -------------------------------------------------- Questi dati suggeriscono una grossa inefficienza di tutti gli impianti nei vari mesi dovuto ai vincoli di capacità di produzione. Questo può essere interpretato con il fatto che possiamo produrre molto di più di quello che produciamo, ma non è necessario dato che abbiamo già soddisfatto tutta la domanda. Dato che l unità di misura della capacità di produzione è in giorni di produzione, i valori di slack rappresentano quanti giorni al mese gli impianti possono fermare la produzione. 43
Capitolo 7 Modelli di Trasporto Si tratta di modelli nei quali sono previsti dei viaggi tra varie località, spesso tali modelli sono detti di trasporto o di distribuzione. Tipicamente, in un modello del trasporto, si dispone di centri sorgente con determinate disponibilità (risorse) e di destinazioni con determinate richieste, quindi si ha la necessità di far viaggiare i prodotti dalle sorgenti alle destinazioni. In alcuni casi si trattano modelli del trasporto a più livelli. Per esempio, si deve trasportare dagli impianti ai depositi e, successivamente, dai depositi ai centri di vendita. Un altro gruppo di modelli di distribuzione sono i modelli di distribuzione ( Transhipment Models). Questo tipo di modelli si adattano a casi nei quali si hanno diversi centri nei quali sussiste sia la produzione che la vendita. Quindi non ci sono degli speci ci centri di risorse e di destinazione, si può trasportare i prodotti tra tutti i centri. 7.1 Usare la Condizione Where sui Vettori di Variabili Diverse volte, lavorando con vettori multi-dimensionali, ci troveremo a non voler considerare tutti le variabili del vettore dato che alcuni casi possono non essere validi oppure non aver signi cato. Per esempio, in un modello di distribuzione non ha senso trasportare i prodotti da un determinato impianto all impianto stesso. In questi casi possiamo usare la condizione where sulle variabili per rimuovere gli elementi non necessari. Per esempio, nel modello di distribuzione possiamo eliminare la possibilità di trasportare da un impianto in se stesso mediante la seguente dichiarazione: VARIABLES Ship[fromplant,toplant] WHERE(fromplant<>toplant); In questo caso, la condizione (fromplant<>toplant) rimuove tutti gli elementi del vettore dove l impianto sorgente è lo stesso dell impianto di desti- 44
nazione. In alcuni casi le condizioni decisionali non sono basate sul valore dell indice. Queste condizioni possono essere determinate dai vettori di dati del modello. Tipicamente, si ha un vettore assegnato che contiene i costi di trasporto tra gli impianti. Se il trasporto tra due impianti non è possibile, si può assegnare a questo trasporto un valore speciale, come zero, e usarlo per identi carlo. Quindi, possiamo usare i valori dei vettori di dati nella de nizione delle variabili per escludere i percorsi che non hanno senso. Riportiamo un esempio: VARIABLES Ship[fromplant,toplant] WHERE(ShipCost[fromplant,toplant]>0); 7.2 Vincoli di Bilanciamento degli Impianti Quando lavoriamo con un modello di distribuzione abbiamo la necessità di garantire che la quantità di prodotti arrivati da altri impianti sommata alla produzione e a quanto è stato preso dal magazzino, deve essere uguale a quanto è stato trasportato fuori dall impianto sommato a quello che è stato venduto e alla quantità immagazinata. In poche parole, quello che entra nell impianto deve essere uguale a quello che esce. Questo tipo di vincolo è tipicamente chiamato vincolo di bilanciamento dell impianto. Qui riportiamo un semplice esempio di vincolo di bilanciamento dell impianto: PlantBal[plant,product,month] Produce+Inventory[month-1] + SUM(fromplant: Ship[fromplant,toplant:=plant]) = Sales + Inventory + SUM(fromplant: Ship[fromplant:=plant,toplant]); Si noti che questo vincolo è simile al vincolo di bilanciamento del magazzino che è stato introdotto precedentemente. L unica differenza è che ora bisogna considerare che trasportiamo in e da un impianto utilizzando un sommatoria su ogni impianto per la variabile Ship. L assegnamento dell indice toplant:=plant nella prima sommatoria, ci permette di speci care che l impianto di arrivo (toplant) utilizzato deve essere l impianto per il quale stiamo de nendo il vincolo PlantBal. In questa vincolo sono sommati tutti i beni trasportati all impianto al quale il vincolo stesso si riferisce da qualsiasi impianto. In maniera analoga l assegnamento fromplant:=plant nella seconda sommatoria speci ca che l impianto di partenza (fromplant) deve essere l impianto (plant) per il quale stiamo de nendo il vincolo. 45
7.3 Un Modello di trasporto tra impianti di produzione In questa sezione dovremo creare un nuovo modello dove ogni impianto ha una sua domanda interna di prodotti e, inoltre, dispone di un magazzino. Useremo il modello creato nella precedente sezione con le opportune modi che. Dato che ogni impianto può vendere i prodotti, ma adesso la domanda è differenziata in ogni impianto, per ogni prodotto e per ogni mese. Riportiamo qui di seguito la tabella con i dati della domanda: Impianto Prodotto Gen Feb Mar Apr A1 4300 4200 6400 5300 p1 A2 4500 5400 6500 7200 A3 5400 6700 7800 8200 A1 5100 6200 5400 7600 p2 A2 6300 7100 5200 6300 A3 4800 6500 5000 7200 A1 4100 6100 4700 5800 p3 A2 5300 5200 5700 4100 A3 4200 4100 5200 6300 A1 4300 4100 5300 4500 p4 A2 5300 6400 4200 6200 A3 5600 5200 3800 4100 Questi dati sono in tre dimensioni: impianti, prodotti e mesi. Nei modelli di programmazione lineare è abbastanza tipico avere dati multidimensionali, a volte anche di dimensione maggiore di otto. Nella prossima sezione aumenteremo ancora la dimensione dei dati includendo un altra dimensione; inseriremo la dimensione dei macchinari creando un vettore a quattro dimensioni. La capacità del magazzino ora è diversi cata tra i vari impianti. Abbiamo quattro valori, uno per ogni impianto: 800, 400, 500 e 400. Inoltre, dato che abbiamo diversi impianti, ognuno con il suo magazzino, avremo dei costi diversi per ogni magazzino per ogni impianto e per ogni prodotto. I costi del magazzino sono riportati nella seguente tabella: Costi del magazzino A1 A2 A3 p1 $8.50 $7.00 $6.50 p2 $9.80 $9.80 $9.80 p3 $7.50 $7.50 $7.50 p4 $9.30 $8.00 $6.50 In ne, dato che permettiamo il trasporto tra gli impianti dei prodotti, abbiamo dei costi legati a questo trasporto; la tabella seguente riporta il riassunto dei costi di trasporto: 46
Costi di trasporto p1 p2 p3 p4 p1 - $15.00 $21.00 $13.00 p2 $16.00 - $12.00 $12.00 p3 $14.00 $17.00 - $15.00 p4 $21.00 $13.00 $10.00 - Si noti come non sia associato nessun costo quando l impianto di partenza è uguale a quello di destinazione. 7.4 Formulazione del modello Quello che segue è il codice della formulazione del modello Planning6. Come vedremo è una estensione del modello Planning5. { Planning6.mpl } TITLE Production_Planning6; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4); toplant := plant; fromplant := plant; DATA Price[product] := (120.00, 100.00, 115.00); Demand[plant,product,month] := DATAFILE("Demand6.dat"); ProdCost[plant,product] := DATAFILE("ProdCost.dat"); ProdRate[plant,product] := DATAFILE("ProdRate.dat"); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[plant,product] := DATAFILE("InvtCost.dat"); InvtCapacity[plant] := (800, 400, 500, 400); ShipCost[fromplant,toplant] := DATAFILE("ShipCost.dat"); VARIABLES Produce[plant,product,month] -> Prod; Inventory[plant, product,month] -> Invt; Sales[plant, product, month] -> Sale; Ship[product, month, fromplant, toplant] WHERE (fromplant <> toplant); 47
MACROS TotalRevenue := SUM(plant, product, month: Price * Sales); TotalProdCost := SUM(plant, product, month: ProdCost * Produce); TotalInvtCost := SUM(plant, product, month: InvtCost * Inventory); TotalShipCost := SUM(product, month, fromplant, toplant: ShipCost * Ship); TotalCost := TotalProdCost + TotalInvtCost + TotalShipCost; MODEL MAX Profit = TotalRevenue - TotalCost; SUBJECT TO ProdCapacity[plant, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; PlantBal[plant, product, month] -> PBal: Produce + Inventory[month-1] + SUM(fromplant: Ship[fromplant, toplant:=plant]) = Sales + Inventory + SUM(toplant: Ship[fromplant:=plant, toplant]); MaxInventory[plant, month] -> MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales <= Demand; END 7.5 Risolvere il Modello e Analizzare la Soluzione Il prossimo passo è quello di risolvere il modello Planning6 scegliendo Solve CPLEX nel menù Run. Se tutto si è svolto correttamente MPL mostrerà il messaggi Optimal Solution Found. Se viene visualizzato un messaggio di errore ricontrolliamo il codice inserito confrontandolo con quello riportato in precedenza in questa sezione. Ora useremo la nestra con la formulazione del modello ( Model De nition Windows) come nel capitolo 6 per vedere solo le parti della soluzione che ci interessano. Per aprire la nestra con le de- nizioni del modello per il modello Planning6 scegliamo Model De nitions nel menù View. Per vedere i valori della variabile Produce, eseguire un doppio click sulla scritta Produce nell albero o, alternativamente, selezionatela e premiamo 48
il tasto View. Questo mostrerà una nestra contenente solo i valori della variabile Produce. VARIABLE Produce[plant,product,month] : plant product month Activity Reduced Cost ----------------------------------------------------------- p1 A1 Jan 4300.0000 0.0000 p1 A1 Feb 4200.0000 0.0000 p1 A1 Mar 6400.0000 0.0000 p1 A1 Apr 5300.0000 0.0000 p1 A2 Jan 1080.0000 0.0000 p1 A3 Jan 5400.0000 0.0000 p1 A3 Feb 5220.0000 0.0000 p1 A3 Mar 4590.0000 0.0000 p1 A3 Apr 5130.0000 0.0000 p2 A1 Jan 5100.0000 0.0000 p2 A1 Feb 6200.0000 0.0000 p2 A1 Mar 5400.0000 0.0000 p2 A1 Apr 7600.0000 0.0000 p2 A2 Jan 6177.2727 0.0000 p2 A2 Feb 3927.2727 0.0000 p2 A2 Mar 5931.8182 0.0000 p2 A2 Apr 3681.8182 0.0000 p3 A1 Jan 4100.0000 0.0000 p3 A1 Feb 6100.0000 0.0000 p3 A1 Mar 4700.0000 0.0000 p3 A1 Apr 5800.0000 0.0000 p3 A3 Jan 4166.6667 0.0000 p3 A3 Feb 1933.3333 0.0000 p3 A3 Mar 3766.6667 0.0000 p3 A3 Apr 2733.3333 0.0000 p4 A1 Jan 3850.0000 0.0000 p4 A1 Feb 2828.5714 0.0000 p4 A1 Mar 5300.0000 0.0000 p4 A1 Apr 4500.0000 0.0000 p4 A3 Jan 5600.0000 0.0000 p4 A3 Feb 5200.0000 0.0000 p4 A3 Mar 4677.2727 0.0000 p4 A3 Apr 4836.3636 0.0000 ----------------------------------------------------------- Come possiamo vedere la produzione ora è distribuita tra i vari impianti con una gestione più efficiente dei costi. È chiaro che è meglio produrre determinati prodotti in determinati impianti e mettere in conto le spese di 49
spedizione. Per esempio, A1 è prodotto negli impianti p1, p2 e p3, ma non nell impianto p4; A2 è prodotto in p1 e p2, A3 è prodotto in p1, p2 e p4. Se ritorniamo nella nestra con l albero e apriamo una nestra con la variabile Ship avremo i seguenti valori per la soluzione: VARIABLE Ship[product,month,fromplant,toplant] : product month fromplant toplant Activity Reduced Cost ------------------------------------------------------------------------ A2 Mar p2 p4 331.8182 0.0000 A3 Mar p4 p3 877.2727 0.0000 A3 Apr p4 p3 736.3636 0.0000 ------------------------------------------------------------------------ Come possiamo vedere il modello propone di trasportare A2 dall impianto p2 all impianto p4. Analogamente A3 viene trasportato da p4 a p3. Chiaramente perché gli impianti p2 e p4 hanno una capacità extra e un basso costo di produzione e quindi possono essere usati per produrre quello di cui necessitano p4 e p3 rispettivamente. 50
Capitolo 8 Un Modello con Più Macchinari In questo capitolo dovremo aggiornare il precedente modello inserendo dei macchinari distribuiti tra gli impianti. Basterà apportare le opportune modi che al modello creato nel capitolo 7 per aggiornarlo. Dato che ora abbiamo dei macchinari differenti nei vari impianti, i costi e le quote di produzione saranno ora diversi per ogni macchina. La seguente tabella riassume i dati di produzione dove ogni riga corrisponde a una macchina che produce un prodotto in un dato impianto. Impianto Macchinario Prodotto Costo di Produzione Quota di Produzione m11 A1 $73.30 500 p1 m11 A2 $52.90 450 m12 A3 $65.40 550 m13 A3 $47.60 350 m21 A1 $79.00 550 p2 m21 A3 $66.80 450 m22 A2 $52.00 300 m31 A1 $75.80 450 p3 m31 A3 $50.90 300 m32 A1 $79.90 400 m32 A2 $52.10 350 m41 A1 $82.70 550 p4 m41 A2 $63.30 400 m41 A3 $53.80 350 Il processo di valutazione della produzione, quanto dobbiamo produrre di ciascuno prodotto, necessita di tenere in considerazione la presenza di diverse macchine. Perciò, dovremo aggiornare la variabile Produce includendo l indice delle macchine e, quindi, usare la condizione where per escludere gli 51
elementi non necessari; ad esempio: impianto p1, macchina m11 e prodotto A3. 8.1 Formulazione del Modello Il listato che segue è il codice della formulazione di Planning7. { Planning7.mpl } TITLE Production_Planning7; INDEX product := (A1, A2, A3); month := (Jan, Feb, Mar, Apr); plant := (p1, p2, p3, p4); toplant := plant; fromplant := plant; machine := (m11, m12, m13, m21, m22, m31, m32, m41); DATA Price[product] := (120.00, 100.00, 115.00); Demand[plant, product, month] := DATAFILE("Demand6.dat"); ProdCost[plant, machine, product] := SPARSEFILE("Produce.dat", 4); ProdRate[plant, machine, product] := SPARSEFILE("Produce.dat", 5); ProdDaysAvail[month] := (23, 20, 23, 22); InvtCost[plant, product] := DATAFILE("InvtCost.dat"); InvtCapacity[plant] := (800, 400, 500, 400); ShipCost[fromplant, toplant] := DATAFILE("ShipCost.dat"); VARIABLES Produce[plant, machine, product, month] -> Prod WHERE (ProdCost > 0); Inventory[plant, product, month] -> Invt; Sales[plant, product, month] -> Sale; Ship[product, month, fromplant, toplant] WHERE (fromplant <> toplant); MACROS TotalRevenue := SUM(plant, product, month: Price * Sales); TotalProdCost := SUM(plant, machine, product, month: ProdCost * Produce); TotalInvtCost := SUM(plant, product, month: InvtCost * Inventory); TotalShipCost := SUM(product, month, fromplant, toplant: ShipCost * Ship); 52
TotalCost := TotalProdCost + TotalInvtCost + TotalShipCost; MODEL MAX Profit = TotalRevenue - TotalCost; SUBJECT TO ProdCapacity[plant, machine, month] -> PCap: SUM(product: Produce / ProdRate) <= ProdDaysAvail; PlantBal[plant, product, month] -> PBal: SUM(machine: Produce) + Inventory[month-1] + SUM(fromplant: Ship[fromplant, toplant:=plant]) = Sales + Inventory[month] + SUM(toplant: Ship[fromplant:=plant, toplant]); MaxInventory[plant, month] ->MaxI: SUM(product: Inventory) <= InvtCapacity; BOUNDS Sales <= Demand; END 8.2 Risolvere il Modello e Analizzare la Soluzione Il prossimo passo è quello di risolvere il modello scegliendo Solve CPLEX nel menù Run. Se tutto è stato fatto correttamente verrà visualizzato il messaggio Optimal Solution Found. Altrimenti, se viene visualizzato un messaggio di errore, controlliamo il codice che abbiamo scritto confrontandolo con quello riportato in precedenza in questa sezione. Useremo ancora la nestra delle de nizioni del modello ( Model De nitions Model), come nel capitolo 7, per osservare solo le parti della soluzione alle quali siamo interessati. Per aprire la nestra con le de nizioni del modello per il modello Planning7 scegliendo Model De nitions nel menù View. Per vedere il valore della variabile Produce fare un doppio click sulla scritta Produce che si trova nell albero delle de nizioni oppure selezioniamo la scritta e premiamo View. Questa operazioni visualizzeranno una nestra con i valori della soluzione solo per la variabile Produce; riportiamo qui nel seguito questi valori. VARIABLE Produce[plant,machine,product,month] : 53
plant machine product month Activity Reduced Cost -------------------------------------------------------------------- p1 m11 A1 Jan 4300.0000 0.0000 p1 m11 A1 Feb 4200.0000 0.0000 p1 m11 A1 Mar 5487.5000 0.0000 p1 m11 A1 Apr 5300.0000 0.0000 p1 m11 A2 Jan 6480.0000 0.0000 p1 m11 A2 Feb 5220.0000 0.0000 p1 m11 A2 Mar 5411.2500 0.0000 p1 m11 A2 Apr 5130.0000 0.0000 p1 m12 A3 Feb 9049.3506 0.0000 p1 m12 A3 Mar 916.1616 0.0000 p1 m12 A3 Apr 10803.1169 0.0000 p1 m13 A3 Jan 8050.0000 0.0000 p1 m13 A3 Feb 7000.0000 0.0000 p1 m13 A3 Mar 8050.0000 0.0000 p1 m13 A3 Apr 7700.0000 0.0000 p2 m21 A1 Jan 5100.0000 0.0000 p2 m21 A1 Feb 6200.0000 0.0000 p2 m21 A1 Mar 6538.8889 0.0000 p2 m21 A1 Apr 7600.0000 0.0000 p2 m21 A3 Jan 4422.6136 0.0000 p2 m21 A3 Feb 3927.2727 0.0000 p2 m21 A3 Mar 5000.0000 0.0000 p2 m21 A3 Apr 3681.8182 0.0000 p2 m22 A2 Jan 6900.0000 0.0000 p2 m22 A2 Feb 6000.0000 0.0000 p2 m22 A2 Mar 6900.0000 0.0000 p2 m22 A2 Apr 6600.0000 0.0000 p3 m31 A1 Jan 3300.0000 0.0000 p3 m31 A1 Feb 5964.9351 0.0000 p3 m31 A1 Mar 2550.0000 0.0000 p3 m31 A1 Apr 4477.4026 0.0000 p3 m31 A3 Jan 4700.0000 0.0000 p3 m31 A3 Feb 2023.3766 0.0000 p3 m31 A3 Mar 5200.0000 0.0000 p3 m31 A3 Apr 3615.0649 0.0000 p3 m32 A1 Jan 800.0000 0.0000 p3 m32 A1 Feb 135.0649 0.0000 p3 m32 A1 Mar 2150.0000 0.0000 p3 m32 A1 Apr 1322.5974 0.0000 p3 m32 A2 Jan 7350.0000 0.0000 p3 m32 A2 Feb 6881.8182 0.0000 p3 m32 A2 Mar 6168.7500 0.0000 54
p3 m32 A2 Apr 6542.7273 0.0000 p4 m41 A1 Jan 4300.0000 0.0000 p4 m41 A1 Feb 4100.0000 0.0000 p4 m41 A1 Mar 5073.6111 0.0000 p4 m41 A1 Apr 4500.0000 0.0000 p4 m41 A2 Jan 2270.0000 0.0000 p4 m41 A2 Feb 5018.1818 0.0000 p4 m41 A2 Mar 2500.0000 0.0000 p4 m41 A2 Apr 5527.2727 0.0000 p4 m41 A3 Jan 3327.3864 0.0000 p4 m41 A3 Mar 2633.8384 0.0000 -------------------------------------------------------------------- La variabile Produce ora è de nita su quattro indici: impianto, macchinario, prodotto e mese. Per ogni impianto il modello ha deciso quali macchinari sono più efficienti per produrre un prodotto in un particolare impianto. Questa tabella potrà essere usata come per programmare la produzione dell intera azienda. Un altra variabile interessante in questo modello è Inventory (magazzino). Se ritorniamo nell albero delle de nizioni e apriamo una nuova nestra con i valori della soluzione per la variabile Inventory visualizzeremo i seguenti valori: VARIABLE Inventory[plant,product,month] : plant product month Activity Reduced Cost ----------------------------------------------------------- p1 A2 Jan 800.0000 0.0000 p1 A2 Feb 620.0000 0.0000 p2 A2 Jan 400.0000 0.0000 p3 A3 Jan 500.0000 0.0000 p4 A2 Jan 400.0000 0.0000 ----------------------------------------------------------- Possiamo osservare che il modello ha deciso di produrre una eccedenza dei prodotti A2 e A3 in Gennaio per aumentare la disponibilità in Febbraio. La maggior parte degli impianti ora funzionano a pieno regime. Riutilizzare la nestra con l albero delle de nizioni apriamo una nuova nestra con il vincolo ProdCapacity per visualizzare i seguenti valori: CONSTRAINT ProdCapacity[plant,machine,month] : plant machine month Slack Shadow Price ----------------------------------------------------------- p1 m12 Jan 23.0000 0.0000 55
p1 m12 Feb 3.5466 0.0000 p1 m12 Mar 21.3343 0.0000 p1 m12 Apr 2.3580 0.0000 p2 m21 Jan 3.8992 0.0000 ----------------------------------------------------------- Come possiamo vedere l impianti p1 e p2 hanno una capacità di produzione non sfruttata per le machine m12 e m21 rispettivamente. Del resto tutte le altre macchine degli altri impianti lavorano a pieno regime per coprire completamente la domanda. 56