ANALIZA EFEKTYWNO´SCI OPEN-SOURCE’OWYCH SOLWERÓW ...sirius.cs.put.poznan.pl › ~inf75968 ›...

45
Politechnika Pozna´ nska Wydzial Informatyki i Zarz ˛ adzania Instytut Informatyki Praca dyplomowa magisterska ANALIZA EFEKTYWNO ´ SCI OPEN-SOURCE’OWYCH SOLWERÓW PROGRAMOWANIA LINIOWEGO W J ˛ EZYKACH JAVA I C# Tomasz Szymanowski Promotor dr in˙ z. Piotr Zielniewicz Pozna ´ n, 2010

Transcript of ANALIZA EFEKTYWNO´SCI OPEN-SOURCE’OWYCH SOLWERÓW ...sirius.cs.put.poznan.pl › ~inf75968 ›...

  • Politechnika Poznańska

    Wydział Informatyki i Zarządzania

    Instytut Informatyki

    Praca dyplomowa magisterska

    ANALIZA EFEKTYWNOŚCI OPEN-SOURCE’OWYCH SOLWERÓW

    PROGRAMOWANIA LINIOWEGO W JĘZYKACH JAVA I C#

    Tomasz Szymanowski

    Promotor

    dr inż. Piotr Zielniewicz

    Poznań, 2010

  • Tutaj przychodzi karta pracy dyplomowej;

    oryginał wstawiamy do wersji dla archiwum PP, w pozostałych kopiach wstawiamy ksero.

  • Spis treści

    1 Wstęp 1

    2 Cel i zakres pracy 3

    3 Programowanie liniowe 5

    3.1 Historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

    3.2 Podstawy teoretyczne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

    3.3 Inne typy programowania matematycznego . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

    3.4 Złożoność obliczeniowa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

    3.5 Formaty zapisywania problemów programowania liniowego . . . . . . . . . . . . . . . . . 9

    3.6 Przegląd wybranych solwerów . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

    3.6.1 Produkty komercyjne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

    3.6.2 Rozwiązania open-source’owe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

    4 Architektura systemu 15

    4.1 Schemat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

    4.2 Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

    4.3 Widok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

    4.4 Kontroler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

    5 Implementacja projektu 19

    5.1 Implementacja w języku Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

    5.2 Implementacja w języku C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

    6 Eksperyment obliczeniowy 23

    6.1 Wpływ rozmiarów problemu na efektywność . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

    6.1.1 Problemy małe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

    6.1.2 Problemy z przewagą liczby ograniczeń nad liczbą zmiennych . . . . . . . . . . . 25

    6.1.3 Problemy z przewagą liczby zmiennych nad liczbą ograniczeń . . . . . . . . . . . 26

    6.1.4 Problemy duże . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

    6.1.5 Podsumowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

    6.2 Wpływ ograniczeń całkowitoliczbowych na efektywność . . . . . . . . . . . . . . . . . . . . 27

    6.3 Wpływ platformy uruchomieniowej na efektywność . . . . . . . . . . . . . . . . . . . . . . 28

    6.3.1 Wydajność solwerów . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

    6.3.2 Wydajność platform uruchomieniowych . . . . . . . . . . . . . . . . . . . . . . . . . 31

    6.4 Wnioski . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

    7 Podsumowanie 35

    I

  • II

    A Wyniki eksperymentu 37

    Literatura 40

  • Rozdział 1

    Wstęp

    Matematyka to nauka, która wykorzystywana jest w niemal każdej dziedzinie życia. Jednym z jej

    ważnych narzędzi jest programowanie liniowe (PL) – metoda maksymalizowania zysku (bądź minima-

    lizowania strat) przy uwzględnieniu pewnych warunków, zapisanych w postaci równań liniowych.

    Programowanie liniowe narodziło się w czasach II Wojny Światowej, w Związku Socjalistycznych

    Republik Radzieckich oraz w Stanach Zjednoczonych. Pierwsze problemy były formułowane na po-

    trzeby przemysłu drzewnego. Funkcja celu wyrażała produktywność w zakresie wytwarzania sprzętu

    wojennego, zmienne oznaczały ilości dystrybuowanych materiałów, a ograniczenia wyrażały takie re-

    strykcje, jak: maksymalna liczba pojazdów, które mogły transportować surowce, maksymalna liczba

    osób zdolnych do pracy czy liczba dostępnych narzędzi. Inny problem z ówczesnych czasów polegał

    na jak najlepszym przyporządkowaniu 70 osób do 70 stanowisk pracowniczych. Funkcja celu wyra-

    żała produktywność całej grupy pracowników, zmienne oznaczały przypisanie pracownika do pozycji,

    natomiast ograniczenia uwzględniały umiejętności pracowników.

    Pierwotnie używane do celów wojennych, programowanie liniowe znajduje szereg praktycznych

    zastosowań w obecnych czasach. Jedną z ważniejszych dyscyplin, w której jest wykorzystywane bardzo

    obszernie, jest ekonomia. Duże przedsiębiorstwa, dysponując określonymi środkami produkcji i ogra-

    niczeniami związanymi z popytem na ich towar, dążą do maksymalizacji przychodu i minimalizacji

    kosztów. Programowanie liniowe pozwala podejmować decyzje strategiczne dotyczące tego, jaką linię

    produkcyjną wybrać, którego gatunku surowca użyć, z podzespołów którego podwykonawcy skorzy-

    stać, itd. Przykładowy problem można znaleźć w przedsiębiorstwie produkującym zabawki plastikowe.

    Załóżmy, że w firmie rozważa się wprowadzenie trzech nowych typów zabawek. Funkcja celu opisuje

    w takim przypadku roczny zysk firmy ze sprzedaży nowego towaru. Zmienne wyrażają ilości, w jakich

    należy owe zabawki produkować. Ograniczenia wyrażają maksymalny roczny zapas kilku rodzajów

    plastiku, z którego wytwarza się zabawki. Inne ograniczenia biorą pod uwagę limity emisji substancji

    zanieczyszczających środowisko przy przetwarzaniu każdego rodzaju surowca. W postaci ograniczeń

    zapisuje się również maksymalny przewidywany popyt na dany produkt.

    Przemysły: transportowy, energetyczny, telekomunikacyjny i przetwórczy to kolejne gałęzie życia,

    w których istnieją problemy rozwiązywane przy pomocy programowania liniowego. Popularnym pro-

    blemem w transporcie, który można rozwiązywać przy pomocy programowania liniowego, jest pro-

    blem komiwojażera. Zadanie polega na odwiedzeniu sieci miast w jak najkrótszym czasie. Funk-

    cja celu wyraża łączną długość przebytej drogi przez flotę pojazdów, którą należy minimalizować.

    Zmienne wyznaczają trasy dla poszczególnych środków transportu. Ograniczenia uwzględniają mak-

    symalną liczbę pojazdów, możliwe godziny ich pracy, maksymalne prędkości na poszczególnych po-

    łączeniach między miastami itd. W przypadku przemysłu energetycznego problem programowania li-

    niowego można znaleźć w zakładzie elektroenergetycznym. Funkcja celu to koszty eksploatacji bloków

    1

  • Wstęp 2

    energetycznych związane z wytwarzaniem energii, które należy minimalizować. Zmienne wyrażają

    liczby poszczególnych bloków, których należy użyć. Ograniczenia uwzględniają ceny poszczególnych

    bloków energetycznych, koszty ich instalacji, koszty związane z zatrudnieniem specjalistów do obsługi

    bloków, maksymalne ilości energii, które każdy z bloków może wytworzyć czy roczne zapotrzebowa-

    nie na energię w danej lokalizacji. W dziedzinie telekomunikacji, z problemem programowania linio-

    wego można się spotkać w firmie telefonii komórkowej, która chce rozbudować swoją sieć w kolejnych

    lokalizacjach. Problem polega na optymalnym wyborze kolejności miejsc, w których będzie rozbudo-

    wywana odpowiednia infrastruktura. Funkcja celu opisuje zysk firmy po całej inwestycji, który należy

    maksymalizować. Zmienne wyznaczają kolejność lokalizacji, w których będzie rozbudowywana sieć.

    Ograniczenia związane są z pozwoleniami na budowę nadajników, kosztem wykupu gruntów budow-

    lanych, przewidywanym zainteresowaniem siecią w danych lokalizacjach, istniejącą konkurencją itd.

    Jeżeli chodzi o przemysł przetwórczy, problemy programowania liniowego można znaleźć w zakładzie

    produkującym soki owocowe, który chce stworzyć nową linię produkcyjną. Funkcja celu wyraża prze-

    widywany zysk, który przeniesie linia. Zmienne określają typ linii, którą należy wybrać, aby zysk był

    maksymalny. Ograniczenia uwzględniają ceny poszczególnych części linii, koszty owoców, z których

    będzie powstawał sok, przewidywany popyt na sok danego typu czy liczbę pracowników potrzebnych

    do produkcji danego napoju.

    Nauki techniczne korzystają z programowania liniowego w inżynierii. Przykładem problemu z tej

    dziedziny jest projektowanie autostrady. Funkcja celu wyraża koszty związane z budową drogi, które

    należy minimalizować. Zmienne wyznaczają przebieg trasy, użyte surowce oraz zatrudnionych pod-

    wykonawców. Ograniczenia wiążą się kosztami przygotowania terenu (uwarunkowania geograficzne),

    ceną dostępnych surowców, kosztem robocizny, czasem budowy itd. W informatyce programowanie

    liniowe znalazło zastosowanie m.in. w rozwiązywaniu problemów sieci przepływowych (ang. Flow ne-

    twork). Przykładowy problem tej dziedziny polega na minimalizacji kosztów transportu towarów mię-

    dzy dostawcami i odbiorcami. Funkcja celu wyraża sumaryczny koszt transportu. Zmienne wyrażają

    przepływ towarów między dostawcami i odbiorcami. Ograniczenia wymuszają równość pomiędzy po-

    dażą i popytem na dany towar. Inna dziedzina informatyki, w której używa się programowania li-

    niowego, to grafika komputerowa, a dokładniej – algorytmy wykrywania kolizji (ang. Collision detec-

    tion). Główne zadanie takich algorytmów polega na sprawdzeniu, czy dwa obiekty kolidują ze sobą,

    a więc czy posiadają co najmniej jeden punkt wspólny. Funkcję celu można więc przedstawić jako

    wartość jednej ze współrzędnych szukanego punktu wspólnego, którą należy maksymalizować bądź

    minimalizować. Zmienne to współrzędne szukanego punktu. Natomiast ograniczenia to opis każ-

    dego z badanych obiektów w postaci nierówności. Obiekt w przestrzeni można bowiem opisać jako

    przecięcie się pewnych półprzestrzeni. Każde otrzymane rozwiązanie dopuszczalne takiego problemu

    oznacza, że obiekty kolidują, natomiast brak rozwiązania mówi, iż obiekty nie nachodzą na siebie.

    Opisane powyżej przykłady problemów optymalizacji są przedmiotem zainteresowania badań ope-

    racyjnych, gdzie programowanie liniowe wykorzystywane jest jako jedna z głównych metod.

    Jak pokazują przytoczone przykłady problemów, spektrum zastosowań programowania liniowego

    jest bardzo duże. Wraz z pojawieniem się pierwszych zastosowań dla programowania liniowego i po-

    wstaniem pierwszych problemów, zrodziła się potrzeba ich efektywnego rozwiązywania. Powstał

    szereg narzędzi dedykowanych tej dziedzinie matematyki, zwanych solwerami (ang. solver). Solwer

    ma najczęściej postać samodzielnego oprogramowania lub biblioteki.

    Na rynku istnieje sporo rozwiązań komercyjnych, np.: CPLEX firmy ILOG Inc., GAMS firmy GAMS

    Development Corporation, solwery oprogramowania MATLAB (The MathWorks) czy narzędzie wbu-

    dowane programu MS Excel (Solver). Równie obszerną część stanowią projekty otwarte (licencje typu

    Open-Source). Do najpopularniejszych należą: GLPK (Free Software Foundation), LP Solve (Free So-

    ftware Foundation) czy CLP (COIN-OR Foundation).

  • Rozdział 2

    Cel i zakres pracy

    Pojawianie się kolejnych rozwiązań na rynku solwerów przyczyniło się do powstania pewnych pro-

    blemów. Oto dwa najważniejsze z nich, które zostaną podjęte w niniejszej pracy:

    • Nie istnieje spójny i jednolity standard, który narzucałby określone reguły autorom tworzą-

    cym oprogramowanie do rozwiązywania problemów programowania liniowego. Co prawda po-

    wstały standardy dotyczące danych wejściowych, o których warto wspomnieć. Są to formaty pli-

    ków, w których zapisywane są problemy PL, takie jak MPS (Mathematical Programming System)

    czy LP (CPLEX LP Format). Niemniej, istnieje problem braku podobnego standardu dla zestawu

    funkcji, które powinien posiadać solwer. Programiści zwykle implementowali własne struktury

    danych, metody definiowania problemów czy sposoby odczytywania wyników. W efekcie, inter-

    fejsy solwerów różnią się między sobą, co utrudnia ich porównanie i zamienne stosowanie.

    • Solwery pisane były w różnych językach programowania, najczęściej niskopoziomowych

    (np. C, C++). Wynika to z faktu, iż zastosowanie języków niskopoziomowych pozwoliło na więk-

    szą kontrolę i lepsze wykorzystanie zasobów maszyn obliczeniowych. Innymi słowy, implemen-

    tacja solwera w C czy C++ pozwoliła na maksymalne wykorzystanie mocy obliczeniowej ówcze-

    snych komputerów i uzyskanie wysokiej efektywności obliczeń. Warto także dodać, że języki

    C czy C++ było w ówczesnych czasach bardzo popularne. Obecnie, kiedy równie dużym zain-

    teresowaniem cieszą się języki wysokiego poziomu, takie jak Java czy C#, pojawił się problem

    w korzystaniu z tak zaimplementowanych narzędzi.

    Niniejsza praca magisterska jest próbą rozwiązania powyższych problemów. Dokładniej, cel pracy

    można podzielić na kilka zadań:

    • Zaprojektowanie interfejsu i zaimplementowanie (w językach Java oraz C#) natywnych bibliotek

    pośredniczących dla wybranych, open-source’owych solwerów PL. Do uruchamiania kodu na-

    tywnego w językach wysokiego poziomu należy wykorzystać mechanizmy Java Native Interface

    (JNI) oraz Platform Invocation Services (P/Invoke).

    • Opracowanie krótkich programów testujących, umożliwiających prezentowanie wyników dzia-

    łania solwerów oraz rezultatów testów wydajnościowych.

    • Przeprowadzenie testów efektywnościowych solwerów w różnych środowiskach programistycz-

    nych.

    • Sporządzenie dokumentacji dla stworzonych bibliotek pośredniczących w celu możliwości

    ich późniejszego wykorzystania dla innych zastosowań.

    3

  • Cel i zakres pracy 4

    W kolejnym rozdziale zostaną przedstawione podstawy teoretyczne związane z programowaniem

    liniowym. Następnie pojawi się przegląd dostępnych obecnie open-source’owych solwerów PL, z na-

    ciskiem na solwery wykorzystane w niniejszej pracy. Dalej opisana zostanie architektura i szczegóły

    implementacji zaproponowanego projektu. Wreszcie, przedstawione zostaną wyniki testów wydajno-

    ściowych. Na końcu opisane zostaną możliwe zastosowania stworzonej aplikacji oraz przedstawione

    zostanie krótkie podsumowanie.

  • Rozdział 3

    Programowanie liniowe

    3.1 Historia

    Początki programowania liniowego wiążą się z potrzebą znalezienia modelu matematycznego,

    który pozwoliłby tak planować wydatki, aby zmniejszyć koszty utrzymania własnej armii i jednocze-

    śnie zwiększyć straty u wroga w czasie II Wojny Światowej.

    Za osobę, która dała podwaliny programowaniu liniowemu, uznawany jest Leonid Kantorowicz,

    radziecki matematyk i ekonomista, który pracował dla rządu swojego kraju. Jednym z zadań, które mu

    powierzono, była optymalizacja produkcji ekwipunku wojennego ze sklejki. Dokładniej, miał on za-

    proponować technikę dystrybucji surowców w celu zwiększenia produkcji, przy uwzględnieniu pew-

    nych limitów. Kantorowicz zauważył, że problem można zinterpretować matematycznie jako zada-

    nie maksymalizacji funkcji liniowej, która posiada pewne ograniczenia. Co więcej, radziecki nauko-

    wiec dostrzegł, że podobnie można podejść do wielu innych problemów ekonomicznych, takich jak:

    optymalne użycie siły roboczej, jak najlepsze wykorzystanie powierzchni uprawnych, rozsądna eks-

    ploatacja materiałów i bogactw czy efektywne korzystanie ze środków transportu. W 1939 roku wy-

    dał pracę Matematyczne metody organizacji i planowania w przedsiębiorstwie (ang. Mathematical Me-

    thods of Organizing and Planning Production), w której sformułował technikę matematyczną zwaną

    programowaniem liniowym. Metody opracowane przez Kantorowicza wykorzystywały m.in. idee pro-

    gramowania dynamicznego [Kan].

    Zagadnienie programowania liniowego znacznie rozwinął amerykański matematyk George Dant-

    zig. W czasie II Wojny Światowej był on ekspertem od spraw planowania w Pentagonie. W 1946 roku

    zajął się zadaniem zautomatyzowania procesu planowania w programie rozmieszczania zasobów, ope-

    racji logistycznych oraz planowania szkoleń. Dokładniej, jego celem było przyspieszenie wykonywa-

    nych obliczeń. W 1947 roku wypracował metodę sympleksów (ang. Simplex algorithm), popularny

    algorytm rozwiązywania problemów programowania liniowego. Jeden z pierwszych problemów, zde-

    finiowany przez Dantziga, polegał na jak najlepszym przyporządkowaniu 70 osób do 70 stanowisk pra-

    cowniczych. Analiza wszystkich permutacji byłaby procesem bardzo czasochłonnym, natomiast algo-

    rytm sympleksów pozwolił na znaczne ograniczenie liczby możliwych rozwiązań optymalnych, które

    należało sprawdzić [Fre94].

    Kolejną z osób, o której warto wspomnieć w kontekście programowania liniowego, jest John

    von Neumann, amerykański naukowiec pochodzenia węgierskiego. W 1947 roku wprowadził on dual-

    ność do programowania liniowego. Poza tym, Neumann zauważył związek między programowaniem

    liniowym i teorią gier [Dan91].

    5

  • 3.2. Podstawy teoretyczne 6

    3.2 Podstawy teoretyczne

    Programowanie liniowe to matematyczna metoda rozwiązywania problemów, dla których należy

    uzyskać jak najlepszy rezultat, przy uwzględnieniu pewnych ograniczeń. Jest ona jedną z podklas pro-

    gramowania matematycznego.

    Formalnie, problem programowania matematycznego można zapisać następująco:

    mi nx

    f (x) (3.1)

    hi (x) = 0 i = 1..rg j (x) ≤ 0 j = 1..nxD ≤ x ≤ xG

    (3.2)

    Problem składa się z funkcji celu (3.1) oraz ograniczeń (3.2) równościowych, nierównościowych i ogra-

    niczeń zakresu. Funkcja celu to główne kryterium, według którego ocenia się jakość rozwiązania, na-

    tomiast ograniczenia to warunki, jakie to rozwiązanie musi spełniać. Cechą charakterystyczną progra-

    mowania liniowego jest fakt, iż funkcja celu i ograniczenia mają postać równań liniowych. Zmienne

    funkcji celu noszą nazwę zmiennych decyzyjnych.

    Problem programowania liniowego można również zapisać w postaci macierzowej, która

    składa się z: funkcji celu (3.3), macierzy ograniczeń (3.4) oraz ograniczeń zakresu (3.5).

    c1x1 + c2x2 + . . .+ cn xn = z (3.3)

    a11x1 +a12x2 + . . .+a1n xn ≤ b1a21x1 +a22x2 + . . .+a2n xn ≤ b2

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    am1x1 +am2x2 + . . .+amn xn ≤ bm

    (3.4)

    xD ≤ x1 ≤ xGxD ≤ x2 ≤ xG. . . . . . . . . . . . . . . . . . . . . .

    xD ≤ xn ≤ xG(3.5)

    Ze względu na fakt, że zmienne decyzyjne (x1, x2, . . . , xn) odpowiadają kolumnom w macierzy ograni-

    czeń, często nazywane są kolumnami (ang. column). Podobnie, ograniczenia nazywane są wierszami

    (ang. row), ponieważ odpowiadają wierszom w macierzy ograniczeń. Wartości liczbowe ograniczeń

    (ang. right hand side) zostały oznaczone jako: b1,b2, . . . ,bm . Współczynniki równania funkcji celu z

    (ang. objective coefficients) to c1,c2, . . . ,cn , a współczynniki poszczególnych ograniczeń (ang. constra-

    int coefficients) to a11, a12, . . . , amn .

    Szczególną odmianą programowania liniowego jest programowanie całkowitoliczbowe (ang. Inte-

    ger Programming), w którym wszystkie zmienne decyzyjne posiadają ograniczenie na całkowitoliczbo-

    wość. Wyróżnia się także programowanie całkowitoliczbowe mieszane (ang. Mixed Integer Program-

    ming), w którym jedynie część zmiennych jest ograniczona całkowitoliczbowo. Dodatkowo można

    zawęzić dziedzinę zmiennych całkowitych do liczb {0, 1}. Wówczas problem nazywany jest binarnym.

    Wyróżnia się programowanie binarne (zero-jedynkowe), jako szczególny przypadek programowania

    całkowitoliczbowego.

  • 3.3. Inne typy programowania matematycznego 7

    3.3 Inne typy programowania matematycznego

    Programowanie matematyczne zawiera szerszą grupę rodzajów programowania, np.: programowa-

    nie ilorazowe, celowe czy minimaksowe/maksyminowe, które, dzięki odpowiednim przekształceniom

    matematycznym, można doprowadzić do postaci liniowej i rozwiązywać solwerami PL.

    W programowaniu ilorazowym funkcja celu ma postać ułamka, w którym licznik wyraża sumę zy-

    sków, a mianownik sumę strat uzyskaną dla danego problemu. Problem programowania ilorazowego

    w postaci macierzowej przedstawia poniższy wzór (3.6), gdzie x to wektor zmiennych, c i d to wektory

    współczynników funkcji celu, c0 i d0 to wyrazy wolne, A jest macierzą współczynników ograniczeń,

    natomiast b to wektor wartości liczbowych ograniczeń.

    mi nx

    cT x+c0d T x+d0

    Ax = b

    x ≥ 0

    (3.6)

    Problem w postaci ilorazowej można przekształcić do problemu PL dzięki transformacji Charnes’a

    i Cooper’a [Mis]. Poniżej (3.7) przedstawiono problem ilorazowy w postaci problemu programowania

    liniowego.

    mi nx

    cT u + c0u0

    Au −bu0 = 0d T u +d0u0 = 1

    u ≥ 0u0 ≥ 0

    (3.7)

    Zmienne u oraz u0 to nowe zmienne, opisane poniżej (3.8).

    u = xd T x+d0

    u0 = 1d T x+d0 (3.8)

    Istnieje również możliwość rozwiązywania problemów programowania celowego poprzez sprowa-

    dzenie problemu do postaci problemu PL. Programowanie celowe polega na tym, że szuka się takich

    wartości zmiennych, by wartości pewnych zdefiniowanych wielomianów dążyły do, z góry zdefiniowa-

    nych, wartości. Poniżej przedstawiono problem programowania celowego w postaci macierzowej (3.9).

    Wektory współczynników oznaczone zostały jako ci , natomiast di to skalarne współczynniki zwane

    celami. A jest macierzą współczynników ograniczeń, natomiast b to wektor wartości liczbowych ogra-

    niczeń. Dla każdego i dąży się, by bezwzględna różnica między wartością wielomianu i danym celem

  • 3.3. Inne typy programowania matematycznego 8

    wynosiła zero. Dlatego, w programowaniu celowym funkcja celu jest zawsze minimalizowana.

    mi nx

    ∑Si=1

    ∣∣cTi x −di ∣∣Ax = b

    x ≥ 0

    (3.9)

    Sposób na linearyzację problemów programowania celowego również przedstawili Abraham Char-

    nes i Wiliam W. Cooper [Mis]. Przekształconą postać problemu przedstawia poniższy wzór (3.10).

    mi ny,z

    ∑Si=1(yi + zi )

    ∀i cTi x −di = yi − zi∀i yi · zi = 0

    Ax = bx ≥ 0y ≥ 0z ≥ 0

    (3.10)

    Zmienne y oraz z to wielkości wprowadzone przez autorów, które opisują każdą różnicę wielo-

    mianu i celu w postaci różnicy tych dwóch zmiennych. Powyższy problem nie mógłby zostać uznany

    za problem programowania liniowego, ponieważ ograniczenia zawierają iloczyn. Jednak owo ograni-

    czenie z mnożeniem można usunąć z problemu, a rozwiązanie optymalne nadal będzie identyczne.

    Jest to cecha tzw. programowania wypukłego.

    Przy pomocy programowania liniowego można rozwiązywać też problemy programowania maksy-

    minowego/minimaksowego, które znajdują zastosowanie w teorii decyzji. Przykładowy problem mak-

    syminowy może polegać na wyborze decyzji, której odpowiada największy spośród najmniejszych zy-

    sków dla każdej z decyzji. Rozwiązanie takiego problemu zapewnia najmniejsze straty, a jednocześnie

    maksymalizuje zysk. Problem maksyminowy można zapisać następująco (3.11):

    maxx

    { mi ni=1..m

    (qi (x)−ai )}(3.11)

    Przez qi oznaczono funkcję, która jest oceną decyzji x dla i-tego kryterium. Preferowane wartości

    dla każdego kryterium oznaczono przez ai .

    Po przekształceniu do postaci liniowej problem wygląda następująco (3.12) [Gra]:

    maxx

    z

    ∀i=1..m z ≥ qi (x)−ai(3.12)

    Przez z oznaczono dodatkową, nieograniczoną zmienną, która reprezentuje maksymalną różnicę

    między każdym z kryteriów a wartością preferowaną.

    Przekształcenie problemu minimaksowego do postaci PL przebiega analogicznie.

  • 3.4. Złożoność obliczeniowa 9

    3.4 Złożoność obliczeniowa

    Popularnym algorytmem do rozwiązywania problemów programowania liniowego bez ograniczeń

    na całkowitoliczbowość zmiennych decyzyjnych jest metoda sympleksów, która działa w sposób ite-

    racyjny. Przez długi czas nie umiano stwierdzić, czy algorytm ten należy do klasy problemów o zło-

    żoności wielomianowej. W 1972 roku, amerykańscy badacze V. Klee i G.J. Minty znaleźli rodzinę pro-

    blemów, dla których algorytm ma złożoność wykładniczą [Gre97]. Niemniej, dla większości proble-

    mów praktycznych algorytm Simplex działa w czasie wielomianowym. Powstały jednak algorytmy,

    które pozwalają zaliczyć problem programowania liniowego (bez ograniczeń na całkowitoliczbowość

    zmiennych decyzyjnych) do klasy problemów łatwych P. Pierwszy z nich zaproponował Leonid Kha-

    chiyan [MJA97], matematyk pochodzenia ormiańskiego. Algorytm jest odporny na przypadki Klee’go

    i Minty’ego i posiada złożoność obliczeniową określoną wzorem:

    O(n4L) (3.13)

    gdzie n to liczba zmiennych decyzyjnych, natomiast L – liczba bitów koniecznych do zapisania

    problemu.

    Jeśli chodzi problemy całkowitoliczbowe, to zaliczane są one do klasy problemów NP-trudnych.

    Wersja decyzyjna problemu programowania zero-jedynkowego została umieszczona na liście

    21 NP-zupełnych problemów Karpa [Kar72].

    3.5 Formaty zapisywania problemów programowania liniowego

    Rozwijające się solwery spowodowały powstanie standardów odnośnie zapisywania problemów

    programowania liniowego w plikach tekstowych. Warto wspomnieć o dwóch najpopularniejszych –

    MPS (wykorzystywany w niniejszej pracy) oraz LP.

    Format MPS (ang. Mathematical Programming System) to stosunkowo stary standard, stworzony

    w firmie IBM [mps]. Problem zapisywany jest w nim w orientacji kolumnowej, a nie w postaci równań.

    Wiąże się to z przeszłością formatu, ponieważ był on projektowany z uwzględnieniem potrzeby zapisu

    na kartach perforowanych. Problem podzielony jest na tzw. sekcje. Poniżej opisano każdą z nich:

    • NAME – Definiuje nazwę problemu, która powinna zaczynać się w piętnastej kolumnie doku-

    mentu.

    • ROWS – Specyfikuje nazwy ograniczeń. Przed nazwą każdego ograniczenia powinna znaleźć się

    litera (w drugiej lub trzeciej kolumnie), która oznacza typ ograniczenia: równościowe (E), mniej-

    sze bądź równe (L), większe bądź równe (G) lub brak ograniczenia (N). Pierwsze ograniczenie

    wyraża nazwę funkcji celu.

    • COLUMNS – Specyfikuje nazwy kolumn oraz współczynniki w macierzy ograniczeń.

    • RHS – Sekcja przeznaczona jest dla prawych stron ograniczeń.

    • BOUNDS – Sekcja opcjonalna, w której podaje się zakres wartości zmiennych. Tożsame warunki

    można podać w sekcji ROWS.

    • RANGES – Opcjonalne miejsce dla nierówności podwójnych.

    • ENDATA – Sekcja kończąca definicję problemu.

  • 3.5. Formaty zapisywania problemów programowania liniowego 10

    Przykładowy problem, na którym zostanie pokazany format MPS, przedstawiono poniżej (3.14).

    maxx,y

    3x +2y

    2x + y ≤ 3000y ≤ 2000

    10x +5y ≥ 9000

    (3.14)

    Ten sam problem, zapisany w formacie MPS wygląda następująco:

    NAME EXAMPLE

    ROWS

    N R0000000

    L ROW1

    L ROW2

    G ROW3

    COLUMNS

    X R0000000 3 ROW3 10

    X ROW1 2

    Y R0000000 2 ROW3 5

    Y ROW2 1 ROW1 1

    RHS

    RHS1 ROW1 3000 ROW2 2000

    RHS1 ROW3 9000

    ENDATA

    Co ciekawe, opisywany standard nie uwzględnia kierunku optymalizacji (maksymaliza-

    cja/minimalizacja). Co więcej, standard nie określa domyślnego kierunku optymalizacji. O wyborze

    maksymalizacja/minimalizacja decyduje więc domyślne ustawienie solwera bądź jawne wskazanie

    programisty. Warto również wspomnieć o ograniczeniach formatu MPS. Długość każdej nazwy

    (zmiennej, ograniczenia itp.) nie może przekraczać ośmiu znaków. Inne ograniczenie wiąże się

    z liczbą dwunastu znaków, na których można zapisywać liczby. W ten sposób ograniczona jest np.

    precyzja przy zapisywaniu liczb zmiennoprzecinkowych.

    Drugi popularny format dla problemów programowania liniowego to CPLEX LP, stworzony

    przez firmę ILOG wraz z solwerem CPLEX. Charakteryzuje się on bardziej intuicyjnym sposo-

    bem definiowania problemów niż MPS. Nie posiada ograniczeń, którymi cechował się stary format

    MPS. Przede wszystkim, CPLEX LP pozwala na definiowanie kierunku optymalizacji (maksymaliza-

    cja/minimalizacja). Poza tym, ograniczenie liczby znaków dla nazw wynosi 255. Poniżej przedsta-

    wiono najważniejsze części problemu w formacie CPLEX LP:

    • Część definiująca kierunek optymalizacji i funkcję celu, rozpoczynająca się jednym ze słów klu-

    czowych: MINIMIZE, MAXIMIZE, MINIMUM, MAXIMUM, MIN, MAX.

    • Część ograniczeń, definiowana jednym ze słów kluczowych: SUBJECT TO, SUCH THAT, S.T., ST.,

    ST.

    • Opcjonalna część zakresu zmiennych, którą rozpoczyna słowo BOUNDS lub BOUND.

  • 3.6. Przegląd wybranych solwerów 11

    • Część kończąca definicję problemu – słowo kluczowe END.

    Poniżej przedstawiono przykład problemu w formacie CPLEX LP, na podstawie tego samego pro-

    blemu PL, którym posłużono się w przypadku formatu MPS.

    Maximize

    OBJ: + 3 X + 2 Y

    Subject To

    ROW1: + Y + 2 X

  • 3.6. Przegląd wybranych solwerów 12

    przydatnych informacji. Plik podzielony jest na części, które zawierają informacje o problemie, prze-

    biegu obliczeń, rozwiązaniu, ewentualnych błędach itd. Do rozwiązywania problemów programowa-

    nia liniowego oprogramowanie wykorzystuje zestaw solwerów, m.in.: BARON, CONOPT, CPLEX, DI-

    COPT, GUROBI, SNOPT, XPRESS [gam]. GAMS jest produktem komercyjnym, wszystkie wersje, w tym

    akademicka, są płatne.

    RYSUNEK 3.1: Środowisko GAMS.

    Za konkurenta środowiska GAMS można uznać propozycję firmy Tomlab Optimization Inc. –

    TOMLAB. Jest to platforma do modelowania i rozwiązywania problemów optymalizacji w MATLA-

    BIE. Przykładowe okienko środowiska przedstawia rysunek 3.2. Jeśli chodzi o format danych wej-

    ściowych, środowisko definiuje własny standard o nazwie TOMLAB. Nie ma wsparcia dla formatów

    MPS czy LP. Wyniki dla rozwiązywanego problemu umieszczane są w odpowiedniej strukturze środo-

    wiska MATLAB. Są to informacje o czasie obliczeń, użytym solwerze, statusie rozwiązania, wartościach

    zmiennych, wartości optymalnej itd. Podobnie jak GAMS, środowisko udostępnia interfejs dla stan-

    dardu AMPL. Solwery dostępne w TOMLABIE to m.in.: CONOPT, CPLEX, GUROBI, KNITRO, SNOPT

    [tom]. Wszystkie wersje środowiska są płatne, także akademicka. Dla celów testowych producent udo-

    stępnia 21-dniową wersję próbną.

    Jak widać, powyższe dwa rozwiązania to środowiska, które posiadają cechy aplikacji stanowiącej

    jedno z zadań niniejszej pracy magisterskiej.

    Na koniec, ze względu na popularność, warto wspomnieć o narzędziu Solver, które jest częścią

    programu Microsoft Excel. Algorytmy zastosowane w solwerze zostały opracowane przez Johna Wat-

  • 3.6. Przegląd wybranych solwerów 13

    RYSUNEK 3.2: Okno konfiguracyjne środowiska TOMLAB.

    sona i Dana Fylstrę z firmy Frontline Systems (metody do rozwiązywania problemów liniowych, w tym

    całkowitych) oraz Leona Lasdona z uniwersytetu University of Texas w Austin i Allana Warena z uni-

    wersytetu Cleveland State University (metody do rozwiązywania problemów nieliniowych) [sol]. Sol-

    wer pozwala rozwiązywać problemy zapisane w arkuszu kalkulacyjnym. Jego zaletą jest prostota, dla-

    tego nadaje się on do małych, prostych problemów. Trudno jednak porównywać owo narzędzie z in-

    nymi rozwiązaniami komercyjnymi.

    3.6.2 Rozwiązania open-source’owe

    Solwery udostępniane na zasadach licencji Open-Source stanowią nie mniej ważną część rynku

    solwerów. Poniżej przedstawiono rozwiązania wykorzystane w niniejszej pracy magisterskiej:

    • Pierwszy z nich, GLPK (GNU Linear Programming Kit), to pakiet rozwijany od 2000 roku

    przez Rosjanina Andrew Makhorina z Moskiewskiego Instytutu Lotniczego (Wydział Informatyki

    Stosowanej). Jest to oprogramowanie przeznaczone do rozwiązywania dużych problemów pro-

    gramowania liniowego, w tym całkowitoliczbowego. Napisane zostało w języku ANSI C i ma po-

    stać biblioteki. Pakiet wspiera język GNU MathProg, będący częścią AMPL.

    O popularności solwera może świadczyć fakt, iż powstał szereg interfejsów pozwalających wy-

    korzystywać GLPK w różnych językach programowania (np.: C, Delphi, Java, Lisp, Perl, Python),

    w różnych systemach operacyjnych (Linux, Mac OS, Windows) czy w środowisku MATLAB.

    Dzięki wysokiej wydajności, solwer zyskał uznanie w oczach profesjonalistów. Na przykład,

    jest wykorzystywany w komercyjnym oprogramowaniu MPL Modeling System firmy Maximal

    Software Inc., razem z takimi solwerami jak: CPLEX, GUROBI czy XPRESS [mpl]. GLPK

  • 3.6. Przegląd wybranych solwerów 14

    jest także zawarty w darmowym systemie modelowania i rozwiązywania problemów optyma-

    lizacji – YALMIP [yal].

    Solwer udostępniony jest na zasadzie licencji Free Software Foundation [glp].

    • LPSolve to kolejna propozycja solwera open-source’owego. Początki projektu wiążą się z Uniwer-

    sytetem Technicznym w Eindhoven i osobą Michela Berkelaara, który rozpoczął pracę nad opro-

    gramowaniem. Jeroen Dirks znacząco rozwinął solwer, dzięki czemu, po wersji 1.5, powstała

    wersja 2.0. Aktualna wersja (5.5) to wynik pracy wielu współpracowników, głównie Holendrów.

    Poza napisaną w ANSI C biblioteką, LPSolve to także interfejs okienkowy umożliwiający wy-

    godną pracę z solwerem, w tym: formułowanie własnych problemów, rozwiązywanie problemów

    zapisanych w plikach czy oglądanie wyników.

    Solwer w postaci biblioteki może być uruchamiany w wielu językach programowania (np.: .NET,

    C, Delphi, Java, VB), w różnych systemach operacyjnych (np.: Linux, Windows). Powstały spe-

    cjalne dodatki pozwalające na pracę z LPSolve w takich środowiskach jak: Excel, MATLAB,

    Octave, O-Matrix czy Scilab.

    Podobnie jak GLPK, LPSolve cieszy się renomą wśród profesjonalistów. Jest jednym z solwerów

    wcześniej wspomnianych środowisk MPL Modeling System i YALMIP.

    LPSolve to oprogramowanie darmowe, wydane pod licencją LGPL – GNU Lesser General Public

    License [lps].

    • Ostatnim z solwerów wybranych do analizy jest QSopt. Narzędzie stworzone zostało na ame-

    rykańskim uniwersytecie w Georgii (Georgia Institute of Technology), przez Davida Applegate’a,

    Williama Cooka, Sanjeeba Dasha i Monikę Mevenkamp. QSopt, napisany w ANSI C, ma postać

    biblioteki i pozwala rozwiązywać problemy programowania liniowego. Jednak, w przeciwień-

    stwie do dwóch poprzednich solwerów, nie obsługuje problemów całkowitoliczbowych. Solwer

    posiada również interfejs okienkowy, dzięki któremu można tworzyć nowe problemy PL, wczy-

    tywać je z pliku, rozwiązywać i oglądać wyniki.

    QSopt może być uruchamiany w systemach Linux, Mac OS, Solaris i Windows. Jeśli chodzi o ję-

    zyki programowania, inne niż ANSI C, to istnieje wersja biblioteki pozwalająca korzystać z sol-

    wera w języku Java.

    Podobnie jak dwa pierwsze solwery, QSopt został wykorzystany w środowisku YALMIP. Warto

    wspomnieć, że solwer został wykorzystany w projekcie Concorde (projekt uczelniany na uni-

    wersytecie w Georgii). Było to przedsięwzięcie, które miało służyć do rozwiązywania problemów

    komiwojażera [qso].

    Solwer jest darmowy, jednak, jako projekt uczelniany, nie jest objęty żadnym typem licencji.

  • Rozdział 4

    Architektura systemu

    4.1 Schemat

    Projekt systemu, który stanowiłby środowisko uruchomieniowe dla solwerów, był następnym kro-

    kiem niniejszej pracy. Stworzony system miał służyć do uruchamiania i porównywania solwerów, czę-

    sto napisanych w językach natywnych. Dokładniej, miał umożliwiać przekazywanie i odczytywanie

    parametrów i danych dla solwerów w jednolity sposób oraz pozwalać na proste uruchamianie me-

    tod rozwiązujących. Innymi słowy, należało zaprojektować pewną warstwę abstrakcji, która pozwala-

    łaby zarządzać solwerami bez znajomości ich konkretnej implementacji. Poglądowy schemat systemu,

    który wyraża powyższe założenia, przedstawia rysunek 4.1.

    Aplikacja

    Solwer #1

    Solwer #2

    Solwer #3

    Warstwa

    pośrednia

    RYSUNEK 4.1: Poglądowy schemat systemu.

    Przy projekcie środowiska oparto się na wzorcu Model-Widok-Kontroler (MVC). Rysunek 4.2 przed-

    stawia szczegółowy schemat systemu. W dalszej części rozdziału opisany został każdy z modułów

    MVC.

    4.2 Model

    Model to najobszerniejszy i najbardziej złożony moduł aplikacji. Warstwa modelu realizuje bez-

    pośrednio pierwszy z celów niniejszej pracy – projekt interfejsu i implementację natywnych bibliotek

    pośredniczących dla solwerów.

    Podstawowy element modułu to interfejs, na który składają się:

    • Solver – klasa abstrakcyjna, główna klasa solwera. Odpowiada ona przede wszystkim za rozwią-

    zywanie problemów. Pozwala także wczytywać problemy z pliku.

    • Problem – klasa abstrakcyjna, odpowiadająca problemowi, który ma być rozwiązany

    przez dany solwer. Pozwala modyfikować problem, np. dodawać/usuwać ograniczenia, modyfi-

    kować funkcję celu itd.

    15

  • 4.2. Model 16

    Solver

    Problem

    Parameters

    Solution

    PrzykladowySolwer rozszerza Solver

    PrzykladowyProblem rozszerza Problem

    PrzykladoweParametry rozszerza Parameters

    Kod

    przykładowego

    solwera

    (biblioteka DLL)

    SolversLoader

    GUI

    TaskManager

    Task

    TaskSolution

    Interfejs

    Kod natywny

    Implementacja interfejsu

    Kontroler

    aplikacji

    Interfejs

    użytkownika

    Plug-in

    MODEL

    WIDOK

    KONTROLER

    RYSUNEK 4.2: Architektura systemu (MVC).

    • Parameters – klasa abstrakcyjna, która pozwala ustawiać parametry, z jakimi solwer pracuje,

    np. ograniczenie czasowe czy ograniczenie liczby iteracji.

    • Solution – klasa, w której przechowywane są informacje o rozwiązanym problemie, ta-

    kie jak wartość rozwiązania czy czas obliczeń.

    Każdy solwer dodawany jest do aplikacji w postaci wtyczki (plugin-a). Tworząc wtyczkę,

    która jest odpowiedzialna za bezpośrednią komunikację z natywnym kodem solwera, należy zaim-

    plementować opisany powyżej interfejs. Tak stworzoną wtyczkę należy umieścić w folderze wtyczek

    programu głównego. Klasą, która jest odpowiedzialna za ładowanie wtyczek i udostępnianie solwerów

    jest SolversLoader.

  • 4.3. Widok 17

    RYSUNEK 4.3: Główne okno programu (wersja napisana w języku Java).

    4.3 Widok

    Moduł widoku to okienko (zob. rysunek 4.3), które pozwala użytkownikowi testować dostępne sol-

    wery. Możliwe jest dodawanie plików z problemami oraz ustawianie liczby powtórzeń (wartość mó-

    wiąca, ile razy dany problem ma być rozwiązany przez każdy z solwerów). Problem, wraz z liczbą

    powtórzeń, tworzy zadanie (ang. Task). Tak skonstruowane zadania można organizować w listy i za-

    pisywać/odczytywać je do/z pliku (taki zestaw zadań będzie nazywany testem w dalszej części pracy).

    Aplikacja przechowuje informacje o pięciu ostatnio używanych listach. Przed uruchomieniem te-

    stu można zaznaczyć/odznaczyć dostępne solwery oraz ustawić parametry z jakimi mają pracować.

    Po uruchomieniu testu, większość funkcji jest dla użytkownika niedostępna. Dostępny jest jedynie

    przycisk zatrzymujący aktualny test. Należy zwrócić uwagę, że zatrzymanie testu następuje po zakoń-

    czeniu rozwiązywania aktualnego powtórzenia problemu. Po zakończeniu obliczeń, aplikacja wyświe-

    tla kartę rozwiązania dla każdego z problemów. Zawiera ona informacje o powodzeniu obliczeń, wy-

    nikach i czasie pracy (w formie tabeli i wykresu słupkowego). Dla liczby powtórzeń większej niż jeden,

    obliczane są również podstawowe statystyki – wartość średnia i odchylenie standardowe.

    4.4 Kontroler

    Trzeci z modułów, kontroler, zarządza komunikacją między widokiem i modelem. W uproszczeniu,

    kontroler otrzymuje żądania od widoku, zleca ich wykonanie solwerom z warstwy modelu, nadzo-

  • 4.4. Kontroler 18

    ruje obliczenia, otrzymuje wyniki oraz przekazuje je do warstwy widoku. Dokładniej, kontroler składa

    się z kilku elementów:

    • TaskManager – główna klasa kontrolera, która zarządza zadaniami. Klasa udostępnia funkcjo-

    nalności opisane powyżej.

    • Task – klasa zadania, w której znajdują się informacje o problemie (nazwa, ścieżka do pliku)

    oraz liczba powtórzeń.

    • TaskSolution – klasa z informacjami o rozwiązanym zadaniu. Poza przechowywaniem rozwią-

    zań problemu na poszczególnych solwerach, klasa odpowiedzialna jest za obliczanie statystyk

    czasów wykonywania (wartości średniej i odchylenia standardowego).

  • Rozdział 5

    Implementacja projektu

    W celu zaimplementowania projektu, którego architektura została opisana w poprzednim roz-

    dziale, należało rozwiązać kilka problemów. Poniżej zostały opisane szczegóły implementacyjne.

    5.1 Implementacja w języku Java

    Wersja dla języka Java napisana została w środowisku Eclipse 3.4.0. Do implementacji bibliotek po-

    średniczących wykorzystano środowisko Microsoft Visual Studio 2008. Poniżej opisano implementację

    warstw modelu, widoku i kontrolera:

    • Najważniejszym zagadnieniem związanym z warstwą modelu jest uruchamianie kodu natyw-

    nego solwera w języku wysokopoziomowym – Java. Realizację tego zadania umożliwił frame-

    work Java Native Interface (JNI). Dzięki niemu, kod napisany w Javie, wykonywany w wirtualnej

    maszynie Javy, może wywoływać kod aplikacji lub biblioteki napisanej w języku natywnym. JNI

    działa także w kierunku odwrotnym, tzn. pozwala wywoływać kod wysokopoziomowy Javy z po-

    ziomu języka natywnego. Na przykład, w metodzie języka C++ można zlecić maszynie wirtualnej

    Javy stworzenie nowego obiektu.

    Proces stworzenia wtyczki dla danego solwera, przy wykorzystaniu JNI, można przedstawić

    w kilku krokach:

    – Najpierw należy stworzyć nowy projekt.

    – Następnie należy stworzyć w Javie klasę z zestawem metod natywnych. Metody takiego

    typu deklaruje się przy pomocy słowa kluczowego native. Co ważne, nie definiuje się ciała

    metody – umieszcza się tylko sygnaturę.

    – Trzeci krok to wykorzystanie narzędzia javah, które dołączane jest do każdego JDK

    (Java Development Kit) i umieszczane w folderze \bin.

    Przy jego pomocy należy wygenerować plik nagłówkowy C++, na podstawie klasy stworzo-

    nej w poprzednim kroku.

    – Kolejny etap to implementacja metod, których deklaracje stworzone zostały przez javah.

    Należy zwrócić uwagę na fakt, że zasoby alokowane na poziomie języka C++ muszą być

    również zwalniane na tym poziomie, ponieważ nie zajmuje się tym Garbage Collector –

    narzędzie Javy do zwalniania nieużytków.

    – Po implementacji, kod natywny należy wyeksportować do postaci biblioteki DLL.

    – Ostatni etap związany z JNI to załadowanie biblioteki w Javie. Należy to zrobić przy po-

    mocy metody:

    19

  • 5.1. Implementacja w języku Java 20

    System.loadLibrary(ścieżka-do-biblioteki);

    Należy pamiętać, że poza stworzoną biblioteką pośredniczącą, należy również załadować

    bibliotekę z kodem samego solwera.

    – Następnie, mając dostęp do metod natywnych, należy zaimplementować interfejs wtyczki

    (klasy abstrakcyjne Solver, Problem i Parameters, zawarte w bibliotece solver-model.jar).

    Tak stworzoną wtyczkę należy wyeksportować do pliku JAR. Należy jednak pominąć biblio-

    teki DLL przy eksporcie.

    – Ostatni krok to umieszczenie wtyczki w folderze Solvers. Najpierw należy stworzyć podfol-

    der dla solwera, a w nim umieścić stworzony wcześniej plik JAR. Wraz z plikiem JAR, należy

    umieścić plik konfiguracyjny o nazwie solver.properties, który zawiera dwie informacje:

    * nazwę pliku JAR, zapisaną w następującej postaci: jar-file-name=przykladowy-

    solwer.jar

    * nazwę kwalifikowaną klasy głównej solwera, która rozszerza klasę Solver, w sposób na-

    stępujący: solver-class-name=nazwa.pakietu.przyklad.PrzykladowySolver

    Odpowiadające biblioteki DLL solwerów należy umieścić w folderze lib aplikacji. Nie jest

    możliwe umieszczanie bibliotek w plikach JAR, razem z klasami wtyczki. Wówczas bowiem

    system Windows nie byłby w stanie załadować biblioteki. Stąd konieczność umieszczania

    bibliotek DLL w osobnym folderze, w oryginalnej postaci.

    Tworzenie wtyczek dla solwerów badanych w niniejszej pracy wiązało się z problemem, który

    polegał na tym, iż nie zawsze solwer posiadał dokładny odpowiednik dla metody interfejsu. Wy-

    jaśniono to na przykładzie metody:

    setBounds(int columnNumber, Double lowerBound, Double upperBound)

    Metodę, która służy do określenia dopuszczalnego przedziału dla danej zmiennej (kolumny),

    należało zaimplementować dla każdego solwera w nieco inny sposób:

    – W przypadku solwera GLPK należało wykorzystać następującą metodę:

    void glp_set_col_bnds(glp_prob *lp, int j, int type, double lb, double ub)

    Typ przedziału dla kolumny o numerze j określa się poprzez parametr type. Wyróżnia się

    pięć typów: ograniczenie obustronne, ograniczenie dolne, ograniczenie górne, ogranicze-

    nie równościowe oraz brak ograniczenia. W zależności od typu ograniczenia należy podać

    wartość dolnego (lb) i/lub górnego (ub) ograniczenia. Parametr lp, wskaźnik do problemu,

    nie jest ważny w bieżących rozważaniach. Biorąc pod uwagę aktualny zestaw parametrów

    metody wysokopoziomowej (możliwe wartości null parametrów lowerBound i upperBo-

    und) należało wywołać odpowiednio metodę niskopoziomową.

    – Przy implementacji solwera LPSolve należało użyć metody następującej:

    unsigned char set_bounds(lprec *lp, int column, double lower, double upper)

    Poza parametrem lp, który jest wskaźnikiem do problemu, pozostałe mają analogiczne zna-

    czenie do parametrów z metody wysokopoziomowej. Jedyna różnica dotyczy definiowania

    braku ograniczenia dolnego/górnego. W takiej sytuacji, wartość null należy w wywołaniu

    zastąpić domyślną wartością nieskończoności (dokładniej – bardzo dużą liczbą całkowitą,

    definiowaną przez LPSolve).

  • 5.2. Implementacja w języku C# 21

    – Jeśli chodzi o solwer QSopt, wykorzystano metodę:

    int QSchange_bound (QSprob p, int indx, char lu, double bound)

    Chcąc ustawić ograniczenie zmiennej o numerze columnNumber, wartość indx należało

    podać jako columnNumber - 1, ponieważ w przypadku tego solwera numeracja kolumn

    i wierszy rozpoczyna się od zera. Poza tym, funkcja pozwala ustawić jednocześnie tylko

    jedną stronę ograniczenia. Parametr lu definiuje ograniczenie dolne(’L’)/górne(’U’), na-

    tomiast bound – jego wartość. Chcąc uzyskać zamierzony cel, należy wywołać powyższą

    metodę dwa razy.

    Podobny problem do opisanego powyżej polegał na tym, że nie zawsze solwer obsługiwał

    daną funkcjonalność. Na przykład, solwer QSopt nie obsługuje programowania całkowi-

    toliczbowego, dlatego niektórych metod do manipulacji parametrami nie dało się zaimple-

    mentować. W celu poinformowania użytkownika o takich sytuacjach, wprowadzono metodę

    getListOfUnsupportedParameters do klasy Solver. Zwraca ona listę parametrów nieobsługiwa-

    nych przez dany solwer.

    • Warstwa widoku stanowiła najmniej wymagający element implementacji. Okienka aplikacji za-

    implementowane zostały z wykorzystaniem technologii AWT, Swing oraz JGoodies. Komunikaty

    od warstwy kontrolera odbierane są poprzez mechanizm zdarzeń (ang. Event Listener).

    • Implementacja kontrolera wymagała rozwiązania kilku problemów. Przede wszystkim należało

    zapewnić sposób na uruchamianie zadań na solwerach w osobnym wątku. Taki wymóg wynika

    przede wszystkim z konieczności oddzielenia wątku widoku od wątku rozwiązującego zadania.

    Pozostawienie wszystkich obowiązków jednemu wątkowi głównemu skutkowałoby bowiem bra-

    kiem odpowiedzi okienka na akcje użytkownika w czasie obliczeń na solwerach. Wydzielenie

    osobnego wątku dla obliczeń umożliwiła klasa SwingWorker z wcześniej wspomnianego pakietu

    Swing (javax.swing). Dzięki zastosowanemu rozwiązaniu możliwe jest zażądanie przerwania

    rozwiązywania zadań w trakcie obliczeń. Dokładniej, rozwiązywanie jest przerywane, kiedy po-

    wtórzenie zadania obliczane w danym momencie zostaje zakończone.

    Innym zagadnieniem, o którym warto wspomnieć w ramach implementacji kontrolera, był pro-

    blem przechowywania listy zadań. W tym celu został stworzony format pliku .tsl. Dane do pliku

    są zapisywane z wykorzystaniem mechanizmu serializacji i odczytywane poprzez deserializację.

    Klasę, w której przechowywane są informacje o zadaniu – Task, należało wyposażyć w imple-

    mentację interfejsu Serializable (pakiet java.io), aby obsługiwała mechanizm serializacji. Dodat-

    kowo, kontroler został wyposażony w funkcję zapamiętywania pięciu ostatnio otwieranych list

    zadań. Realizację takiej funkcjonalności umożliwiła klasa Preferences z pakietu java.util.prefs.

    5.2 Implementacja w języku C#

    Aplikacja w języku C# stworzona została w środowisku Microsoft Visual Studio 2008. Model, widok

    i kontroler zostały zaimplementowane w sposób następujący:

    • Przy implementacji warstwy modelu należało rozwiązać problem dostępu do kodu niskopozio-

    mowego. Z pomocą przyszedł mechanizm P/Invoke (Platform Invocation Services), który po-

    zwala w kodzie zarządzanym wywoływać funkcje natywne.

    Zastosowanie P/Invoke w praktyce okazało się prostsze, aniżeli korzystanie z JNI. Poniżej przed-

    stawiono etapy tworzenia wtyczki w języku C#:

  • 5.2. Implementacja w języku C# 22

    – Najpierw należy utworzyć nowy projekt dla wtyczki.

    – Następnie należy stworzyć klasę z deklaracjami metod natywnych. Każdą z nich należy po-

    przedzić słowami kluczowymi static extern. Przed każdą z metod należy również umieścić

    atrybut DllImport wraz ze ścieżką do biblioteki DLL, która zawiera implementację danej

    metody. Przykładowa deklaracja wygląda następująco:

    [DllImport(ścieżka-do-biblioteki)]

    static extern void metoda(double parametr);

    Stworzenie zestawu metod w sposób opisany powyżej to jedyny krok dotyczący mechani-

    zmu P/Invoke. Jak widać, korzystanie z kodu natywnego w języku C# jest prostsze niż w ję-

    zyku Java. Jednym z udogodnień, które umożliwia tak prostą konstrukcję, jest istnienie

    struktury IntPtr (przestrzeń nazw System). Służy ona do reprezentowania wskaźników.

    Poza tym, język C# umożliwia przekazywanie referencji do metod dzięki słowu kluczowemu

    ref.

    – Następny krok to implementacja interfejsu solwera, czyli klas Solver, Problem oraz Parame-

    ters, które znajdują się w bibliotece klas Model. Podobnie jak w przypadku Javy, pamiętać

    należy o zwalnianiu zasobów alokowanych przez metody natywne.

    – Kolejny etap to eksport zaimplementowanego solwera do biblioteki klas DLL, tzw. podze-

    społu (ang. Assembly).

    – Ostatnią rzeczą, którą należy wykonać, jest umieszczenie stworzonej wtyczki w folderze So-

    lvers. Oryginalną bibliotekę solwera należy umieścić w folderze Lib, który jest podfolderem

    katalogu wtyczek Solvers.

    • Tak jak w języku Java, tak w C# warstwa widoku była najprostszą częścią implementacji. Okienka

    aplikacji zostały stworzone w edytorze GUI środowiska Visual Studio, przy wykorzystaniu API

    Windows Forms.

    • Napisanie kontrolera wymagało rozwiązania takich samych problemów jak w przypadku Javy.

    Uruchamianie zadań w osobnym wątku umożliwiła klasa BackgroundWorker z przestrzeni nazw

    System.ComponentModel.

    Zapisywanie list zadań do pliku zrealizowano poprzez wprowadzenie formatu pliku .tcs. Dane

    zapisywane są do pliku poprzez serializację, a odczytywane przez deserializację. Realizując me-

    chanizm serializacji, należało w klasie zadania (Task) zaimplementować interfejs ISerializable

    z przestrzeni nazw System.Runtime.Serialization.

  • Rozdział 6

    Eksperyment obliczeniowy

    Stworzenie aplikacji do uruchamiania solwerów pozwoliło przeprowadzić testy wydaj-

    nościowe. Testy zostały stworzone w oparciu o instancje problemów, które pochodzą

    z dwóch źródeł. Problemy liniowe bez ograniczeń na całkowitoliczbowość zostały pobrane

    ze strony departamentu informatyki brytyjskiego organu administracyjnego Science and Tech-

    nology Facilities Council (http://www.numerical.rl.ac.uk/cute/netlib.html). Problemy całko-

    witoliczbowe pobrano ze strony amerykańskiego uniwersytetu Carnegie Mellon University

    (http://www.andrew.cmu.edu/user/anureets/mpsInstances/OrLib_CWLP/mpsInstances/).

    Testy zostały podzielone na kilka grup, tak aby sprawdzić wpływ różnych czynników na efektyw-

    ność. Problemy były rozwiązywane na trzech solwerach open-source’owych, opisanych w poprzednich

    rozdziałach. Dodatkowo, stworzono wtyczkę z solwerem komercyjnym CPLEX, aby móc porównać

    solwery darmowe z rozwiązaniem profesjonalnym. Niestety, wersja studencka solwera pozwala roz-

    wiązywać problemy posiadające maksymalnie 300 zmiennych i 300 ograniczeń, dlatego solwer CPLEX

    nie mógł być wykorzystany do rozwiązywania większych problemów.

    Testy podzielone zostały na trzy kategorie, badające wpływ różnych czynników na efektywność.

    Pierwsze dwa zestawy uruchamiano w aplikacji napisanej w języku Java, natomiast trzeci – na obu

    platformach. Poniżej opisany został każdy zestaw testów oraz jego wyniki.

    6.1 Wpływ rozmiarów problemu na efektywność

    Pierwsza kategoria testów miała zbadać wpływ rozmiarów problemu na wydajność solwerów. Do-

    kładniej, testy różniły się liczbą zmiennych (kolumn) i ograniczeń (wierszy).

    6.1.1 Problemy małe

    Najpierw zbadano problemy niewielkich rozmiarów, tzn. takich, dla których liczba zmiennych

    i ograniczeń nie przekraczała 200.

    Przy mierzeniu czasów obliczeń dla problemów tej kategorii pojawił się problem z dokładnością

    metod mierzących czas. Teoretycznie, mierzenie czasu, zarówno w Javie jak i w C#, wykonywane

    jest z dokładnością do milisekund. Jednak, dla małych problemów, których rozwiązywanie trwało

    kilkanaście milisekund, wystąpiła pewna anomalia. Przykładowe czasy zmierzone dla problemu KB2

    przedstawia tabela 6.1. Jak widać, różnice między czasami są rzędu kilkunastu milisekund, a nie po-

    jedynczych milisekund, jak należałoby się spodziewać. Występują również wartości zerowe, co jest

    wynikiem niepoprawnym. Statystyki przeprowadzone dla tak małych problemów charakteryzowa-

    łyby się nienaturalnie dużym odchyleniem standardowym. Dlatego, w ramach jednego powtórzenia

    należało rozwiązywać problem kilkudziesięciokrotnie i brać pod uwagę sumę czasów rozwiązywa-

    23

  • 6.1. Wpływ rozmiarów problemu na efektywność 24

    nia problemu. Taki zabieg pozwolił bardziej wiarygodnie porównać czasy pracy solwerów. Ponadto,

    ze względu na krótkie czasy obliczeń, powtórzeń takich przeprowadzono stosunkowo dużo. Poniżej

    przedstawiony został zestaw testów dla problemów małych:

    Powtórzenie CPLEX GLPK LPSolve QSopt1 15 0 31 162 0 0 0 153 16 16 16 04 0 0 0 165 15 0 0 06 0 0 0 07 0 0 0 08 16 15 15 159 0 0 16 1610 0 16 0 0

    TABLICA 6.1: Czasy rozwiązywania problemu KB2 (platforma Java).

    • Problem KB2, 41 zmiennych i 44 ograniczenia, 100 x 50 powtórzeń,

    • Problem SC50A, 48 zmiennych i 51 ograniczeń, 100 x 50 powtórzeń,

    • Problem STOCFOR1, 111 zmiennych i 118 ograniczeń, 100 x 50 powtórzeń,

    • Problem SCAGR7, 140 zmiennych i 130 ograniczeń, 100 x 50 powtórzeń,

    • Problem ISRAEL, 175 zmiennych i 142 ograniczenia, 100 x 50 powtórzeń

    Wszystkie solwery znajdowały takie same, optymalne rozwiązania powyższych problemów. Na wy-

    kresie 6.1 przedstawiono średnie czasy obliczeń. Najszybciej problemy rozwiązywał solwer CPLEX,

    jednak solwer GLPK okazał się niewiele gorszy. Słabiej wypadły solwery LPSolve i QSopt.

    0

    200

    400

    600

    800

    1000

    1200

    1400

    1600

    1800

    KB2 SC50A STOCFOR1 SCAGR7 ISRAEL

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Problem

    CPLEX

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.1: Średnie czasy 50-krotnego rozwiązywania problemów małych (platformaJava).

  • 6.1. Wpływ rozmiarów problemu na efektywność 25

    6.1.2 Problemy z przewagą liczby ograniczeń nad liczbą zmiennych

    Drugi zestaw testów zawierał problemy, dla których liczba wierszy (ograniczeń) była większa

    niż liczba kolumn (zmiennych). W wykorzystywanym zbiorze danych niewiele problemów charak-

    teryzowało się taką proporcją w rozmiarze danych. Problem z największą przewagą liczby ograniczeń

    nad liczbą zmiennych posiadał ich 3 razy więcej. Z reguły jednak przewaga ta nie przekraczała 20%

    i występowała w problemach o niewielkich rozmiarach. Najczęściej spotyka się problemy, dla któ-

    rych opisywane proporcje są odwrotne, tzn. więcej jest zmiennych niż ograniczeń. Jednym z powodów

    takiej prawidłowości jest fakt, iż szereg ograniczeń zapisanych przez człowieka daje się często sprowa-

    dzić do mniejszej liczby ograniczeń. W przypadku testów tej kategorii mierzono czasy 50-krotnego

    rozwiązywania problemu. Powód takiego zabiegu jest analogiczny jak w przypadku problemów ma-

    łych. Poniżej przedstawiono problemy drugiego zestawu:

    • Problem SHARE2B, 79 zmiennych i 97 ograniczeń, 100 x 50 powtórzeń,

    • Problem BOEING2, 143 zmienne i 167 ograniczeń, 100 x 50 powtórzeń,

    • Problem AGG, 163 zmienne i 489 ograniczeń, 100 x 50 powtórzeń,

    • Problem AGG2, 302 zmienne i 517 ograniczeń, 100 x 50 powtórzeń

    Wszystkie solwery znajdowały takie same, optymalne rozwiązania powyższych problemów; jedynie

    solwer CPLEX nie rozwiązał problemu AGG2 (zbyt duża liczba zmiennych). Co ciekawe, wersja stu-

    dencka tego solwera pozwoliła rozwiązać problem AGG z 489 ograniczeniami. Wynika to z faktu, iż sol-

    wer, przed właściwym rozwiązywaniem, przeprowadza procedurę upraszczającą problem (ang. Preso-

    lve). W przypadku problemu AGG solwer zmniejszył liczbę ograniczeń o ponad połowę, stąd rozmiar

    problemu nie przekroczył ograniczeń licencji studenckiej (300 zmiennych, 300 ograniczeń).

    Jak widać na wykresie 6.2, najefektywniej problemy rozwiązywał solwer CPLEX. Solwer GLPK oka-

    zał się nieco gorszy. Najdłużej problemy były rozwiązywane przez solwery LPSolve i QSopt. W ogólno-

    ści, wyniki przedstawiają się podobnie jak w przypadku pierwszego zestawu testów. Wiąże się to z po-

    dobnymi rozmiarami problemów badanych w obu przypadkach.

    0

    200

    400

    600

    800

    1000

    1200

    1400

    1600

    1800

    2000

    SHARE2B BOEING2 AGG AGG2

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Problem

    CPLEX

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.2: Średnie czasy 50-krotnego rozwiązywania problemów z przewagą liczby ogra-niczeń nad liczbą zmiennych (platforma Java).

  • 6.1. Wpływ rozmiarów problemu na efektywność 26

    6.1.3 Problemy z przewagą liczby zmiennych nad liczbą ograniczeń

    Następny zestaw testów charakteryzował się większą liczbą zmiennych niż ograniczeń.

    W tym przypadku stosunek był dużo większy niż odpowiadająca proporcja w poprzednim zestawie,

    tzn.: problemy miały od kilkunastu do kilkuset zmiennych więcej niż ograniczeń. W ogólności pro-

    blemy były dużych rozmiarów, a czasy rozwiazywania dłuższe, dlatego liczby powtórzeń dla każdego

    z nich nie przekraczały pięćdziesięciu. Ze względu na duże rozmiary problemów, do testów nie wyko-

    rzystano solwera CPLEX. Poniżej znajduje się lista rozwiązywanych problemów:

    • Problem D6CUBE, 6184 zmiennych i 416 ograniczeń, 50 powtórzeń,

    • Problem TRUSS, 8806 zmiennych i 1001 ograniczeń, 50 powtórzeń,

    • Problem FIT2D, 10500 zmiennych i 26 ograniczeń, 5 powtórzeń

    Wszystkie solwery znajdowały takie same, optymalne rozwiązania powyższych problemów. Czasy

    obliczeń poszczególnych solwerów przedstawiono na wykresie 6.3. Co ciekawe, bardzo dobre czasy

    zanotował solwer LPSolve. Tylko dla problemu TRUSS minimalnie lepszy okazał się solwer GLPK.

    Od obu solwerów znacząco dłużej problemy rozwiązywał QSopt.

    0

    20

    40

    60

    80

    100

    120

    D6CUBE TRUSS FIT2D

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    s]

    Problem

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.3: Średnie czasy działania solwerów dla problemów z przewagą liczby zmien-nych nad liczbą ograniczeń (platforma Java).

    6.1.4 Problemy duże

    Ostatni zestaw testów w badaniu wpływu rozmiarów problemu na efektywność solwerów stano-

    wiły problemy duże. Zarówno liczba zmiennych jak i liczba ograniczeń liczyła od kilku do kilkunastu

    tysięcy. Oto problemy zestawu czwartego:

    • Problem FIT2P, 13525 zmiennych i 3001 ograniczeń, 10 powtórzeń,

    • Problem STOCFOR3, 15695 zmiennych i 16676 ograniczeń, 5 powtórzeń,

    • Problem DFL001, 12230 zmiennych i 6072 ograniczenia, 2 powtórzenia

  • 6.2. Wpływ ograniczeń całkowitoliczbowych na efektywność 27

    Wszystkie solwery znajdowały takie same, optymalne rozwiązania powyższych problemów. Jak wi-

    dać na wykresie 6.4, dla dwóch problemów najszybciej działał solwer LPSolve, a dla jednego GLPK.

    Po raz pierwszy solwer QSopt nie okazał się najgorszy przy rozwiązywaniu problemów dużych – pro-

    blem STOCFOR3 rozwiązywał średnio szybciej niż LPSolve. Dla pozostałych problemów działał jednak

    znacząco wolniej.

    0

    50

    100

    150

    200

    250

    300

    350

    400

    450

    500

    FIT2P STOCFOR3 DFL001

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    s]

    Problem

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.4: Średnie czasy działania solwerów dla problemów dużych (platforma Java).

    6.1.5 Podsumowanie

    Po przeprowadzeniu czterech zestawów testów należy stwierdzić, że wpływ na efektywność działa-

    nia solwerów ma przede wszystkim liczba zmiennych. Liczba ograniczeń nie wpływa w tak znaczący

    sposób jak liczba zmiennych, ponieważ liczbę ograniczeń daje się zredukować poprzez sprowadzanie

    zestawu wielu ograniczeń do pojedynczych wierszy. Jedno ograniczenie może wiązać ze sobą nawet

    wszystkie zmienne.

    W wyniku przeprowadzonych testów należy stwierdzić, że najlepiej z problemami radził sobie sol-

    wer GLPK. Mimo że czasami inne solwery były szybsze, to nie były to znaczące różnice. Efektywność

    solwera GLPK można ocenić jako bardzo dobrą i równą. Solwer LPSolve okazał się również bardzo

    wydajny. Pomimo słabszych wyników dla małych problemów, przy dużych instancjach LPSolve praco-

    wał bardzo efektywnie. Najgorzej wypadł solwer QSopt. Jego efektywność była zwykle gorsza, czasami

    w niewielkim stopniu, lecz równie często w stopniu znacznym.

    6.2 Wpływ ograniczeń całkowitoliczbowych na efektywność

    Celem drugiej kategorii testów było sprawdzenie, jak dodanie ograniczeń na całkowitoliczbowość

    zmiennych wpływa na efektywność solwerów. Testom tej kategorii można było poddać tylko solwery

    GLPK i LPSolve. Solwer QSopt nie obsługuje programowania całkowitoliczbowego, natomiast dla wer-

    sji studenckiej solwera CPLEX rozmiary problemów okazały się za duże.

    Poniżej przedstawiony został zestaw problemów całkowitoliczbowych:

    • Problem cap71, 816 zmiennych (16 całkowitych) i 67 ograniczeń, 20 powtórzeń,

    • Problem cap72, 816 zmiennych (16 całkowitych) i 67 ograniczeń, 20 powtórzeń,

  • 6.3. Wpływ platformy uruchomieniowej na efektywność 28

    • Problem cap73, 816 zmiennych (16 całkowitych) i 67 ograniczeń, 20 powtórzeń,

    • Problem cap74, 816 zmiennych (16 całkowitych) i 67 ograniczeń, 20 powtórzeń,

    • Problem cap81, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 20 powtórzeń,

    • Problem cap82, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 20 powtórzeń,

    • Problem cap83, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 20 powtórzeń,

    • Problem cap84, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 20 powtórzeń,

    • Problem cap91, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 10 powtórzeń,

    • Problem cap92, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 10 powtórzeń,

    • Problem cap93, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 10 powtórzeń,

    • Problem cap94, 1275 zmiennych (25 całkowitych) i 76 ograniczeń, 10 powtórzeń

    Oba solwery znajdowały takie same, optymalne rozwiązania powyższych problemów. Czasy dzia-

    łania solwerów można porównać na wykresach 6.5, 6.6 i 6.7. Jak widać, solwery rozwiązywały zadania

    w podobnych czasach. W większości przypadków szybszy był LPSolve, jednak w każdej czwórce pro-

    blemów (cap7x, cap8x, cap9x) solwer GLPK posiadał jeden najlepszy czas. Podsumowując, oba solwery

    wykazały się dobrą efektywnością w rozwiązywaniu problemów całkowitoliczbowych.

    0

    1

    2

    3

    4

    5

    6

    7

    8

    cap71 cap72 cap73 cap74

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    s]

    Problem

    GLPK

    LPSolve

    RYSUNEK 6.5: Średnie czasy działania solwerów dla problemów całkowitoliczbowych cap7x(platforma Java).

    6.3 Wpływ platformy uruchomieniowej na efektywność

    Ostatnia kategoria testów miała na celu porównać efektywność solwerów uruchamianych w dwóch

    środowiskach – aplikacjach napisanych w językach Java oraz C#. W tym przypadku, poza testami mie-

    rzącymi efektywność samych solwerów, dodatkowo przeprowadzono testy sprawdzające czasy ładowa-

    nia problemu i tworzenia rozwiązania. Takie testy, dzięki wielu wywołaniom metod natywnych (two-

    rzenie problemu, odczytywanie statusu rozwiązania, pobieranie wartości zmiennych itd.) pozwoliły

    porównać efektywność platform uruchomieniowych Java i C#.

  • 6.3. Wpływ platformy uruchomieniowej na efektywność 29

    0

    5

    10

    15

    20

    25

    cap81 cap82 cap83 cap84

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    s]

    Problem

    GLPK

    LPSolve

    RYSUNEK 6.6: Średnie czasy działania solwerów dla problemów całkowitoliczbowych cap8x(platforma Java).

    0

    500

    1000

    1500

    2000

    2500

    cap91 cap92 cap93 cap94

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    s]

    Problem

    GLPK

    LPSolve

    RYSUNEK 6.7: Średnie czasy działania solwerów dla problemów całkowitoliczbowych cap9x(platforma Java).

    6.3.1 Wydajność solwerów

    Pierwszy zestaw testów sprawdzał, jak rodzaj platformy uruchomieniowej wpływa na wydajność

    solwerów. Poniżej przedstawiono listę problemów użytych do tego celu:

    • Problem WOOD1P, 2594 zmienne i 245 ograniczeń, 50 powtórzeń,

    • Problem SHIP12S, 2763 zmienne i 1152 ograniczenia, 50 powtórzeń,

    • Problem PILOT-WE, 2789 zmiennych i 723 ograniczenia, 20 powtórzeń,

    • Problem CYCLE, 2857 zmiennych i 1904 ograniczenia, 20 powtórzeń

    Wyniki przeprowadzonych testów zostały przedstawione na wykresach 6.8, 6.9, 6.10 oraz 6.11.

    Wszystkie solwery znajdowały rozwiązania optymalne. Dla pierwszych dwóch problemów (WOOD1P,

  • 6.3. Wpływ platformy uruchomieniowej na efektywność 30

    SHIP12S) efektywność solwerów nie różni się znacząco między środowiskami Javy i C#, rozwiązania

    również są takie same. Uwagę zwracają natomiast wyniki dla problemów PILOT-WE i CYCLE. Sol-

    wer LPSolve obliczał rozwiązania problemów znacząco dłużej na platformie napisanej w języku Java.

    Taka różnica wynika z faktu, iż w przypadku owych problemów nie istnieje jedna droga uzyskania

    optymalnego rozwiązania. Co więcej, wartości poszczególnych zmiennych dla problemu CYCLE uzy-

    skane przez solwer LPSolve na obu platformach są często różne. Przykładowe różnice zestawiono w ta-

    beli 6.2. Różne działanie tego samego solwera (tej samej biblioteki DLL) na różnych platformach na-

    leży tłumaczyć różnymi rozwiązaniami początkowymi, które generuje solwer na podstawie pewnego

    zestawu wartości losowych. Najprawdopodobniej generator liczb losowych użyty w LPSolve używa

    ziarna inicjującego zależnego od programu, w którym ładowana jest biblioteka solwera. Może to być

    np. nazwa programu. Sprawdzono, że owo ziarno nie zależy od ścieżki określającej lokalizację biblio-

    teki DLL solwera na dysku.

    0

    50

    100

    150

    200

    250

    300

    350

    400

    Java C#

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Platforma

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.8: Średnie czasy działania solwerów w środowiskach Java i C# dla problemuWOOD1P.

    Zmienna Java C#BABDDEDD 6.2545 40.4322BR22F2AE 13.1161 0WROKGRDD 22857.0942 14548.064PNBDRCDD 0 70.5606

    TABLICA 6.2: Różne wartości tych samych zmiennych problemu CYCLE rozwiązanego przezsolwer LPSolve na platformach Java i C#.

    Wartości zmiennych podane zostały z dokładnością do czterech miejsc po przecinku. Należy pod-

    kreślić, że wartości funkcji celu na obu platformach były takie same.

    Analiza wyników dla badanego zestawu pozwoliła stwierdzić, iż solwer LPSolve działa niedetermi-

    nistycznie, ponieważ inaczej rozwiązuje ten sam problem w różnych środowiskach. Z kolei solwery

    GLPK i QSopt działają w ten sam, deterministyczny sposób w Javie i w C#.

    Jeżeli chodzi o wydajność, najlepszy okazał się solwer GLPK. Co ciekawe, bardzo dobre rezultaty

    osiągnął solwer QSopt, który problem CYCLE rozwiązywał szybciej niż GLPK. Badany zestaw testów

    najwolniej rozwiązywał niedeterministyczny solwer LPSolve.

  • 6.3. Wpływ platformy uruchomieniowej na efektywność 31

    0

    50

    100

    150

    200

    250

    300

    350

    400

    450

    500

    Java C#

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Platforma

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.9: Średnie czasy działania solwerów w środowiskach Java i C# dla problemuSHIP12S.

    0

    500

    1000

    1500

    2000

    2500

    3000

    3500

    Java C#

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Platforma

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.10: Średnie czasy działania solwerów w środowiskach Java i C# dla problemuPILOT-WE.

    6.3.2 Wydajność platform uruchomieniowych

    Drugi zestaw testów miał sprawdzić, jak wydajne są platformy uruchomieniowe Java i C#.

    W tym przypadku badano, wyjątkowo, czas ładowania problemu oraz tworzenia rozwiązania, a nie

    czas rozwiązywania zadania. Dokładniej, skupiono się na operacjach tworzenia obiektów problemów,

    wczytywania problemów z pliku, sprawdzania statusu rozwiązania, odczytywania wartości zmiennych

    i funkcji celu. Chodziło bowiem o sprawdzenie wydajności w wywoływaniu metod natywnych przez

    obie badane platformy. Poza tym, dla dwóch pierwszych problemów mierzono czasy 50-krotnego

    ładowania problemu i 50-krotnego tworzenia rozwiązania, ponieważ czasy jednostkowe były bardzo

    małe. Dokładne uzasadnienie takiego podejścia opisane zostało w punkcie 6.1.1. Poniżej przedsta-

    wiono problemy wykorzystane do testów:

    • Problem LOTFI, 154 zmienne i 308 ograniczeń, 100 x 50 powtórzeń,

  • 6.4. Wnioski 32

    0

    2000

    4000

    6000

    8000

    10000

    12000

    Java C#

    Śre

    dn

    i cza

    s p

    racy

    so

    lwe

    ra [

    ms]

    Platforma

    GLPK

    LPSolve

    QSopt

    RYSUNEK 6.11: Średnie czasy działania solwerów w środowiskach Java i C# dla problemuCYCLE.

    • Problem SHIP08S, 779 zmiennych i 2387 ograniczeń, 50 x 50 powtórzeń,

    • Problem D2Q06C, 2172 zmienne i 5167 ograniczeń, 50 powtórzeń

    Wszystkie solwery znajdowały takie same, optymalne rozwiązania powyższych problemów. Porów-

    nanie czasów między językiem Java i C# zostało zaprezentowane na wykresach 6.12, 6.13 i 6.14. Róż-

    nice w czasach pomiędzy poszczególnymi solwerami wynikają głównie z faktu, iż każdy solwer używa

    innego zestawu funkcji do osiągnięcia tego samego efektu. Na przykład, chcąc uzyskać nazwy zmien-

    nych, w solwerze GLPK należy uruchamiać funkcję natywną dla każdej zmiennej z osobna (stąd naj-

    większe czasy tego solwera), natomiast w solwerze QSopt można zrobić to jedną funkcją. Jak pokazują

    wykresy, czasy wywoływania metod natywnych na platformach Java i C# są podobne. Różnice pomię-

    dzy platformą Java i C# zwykle nie przekraczają kilkunastu procent. Można więc wyciągnąć wniosek,

    że mechanizmy Java Native Interface oraz P/Invoke cechują się podobną wydajnością.

    6.4 Wnioski

    W wyniku przeprowadzonych badań stwierdzono, że najefektywniej działał solwer GLPK, szczegól-

    nie dla problemów bez ograniczeń na całkowitoliczbowość. Co ważne, solwer działał równo, tzn. bar-

    dzo rzadko zdarzało się, by któryś problem rozwiązywał zdecydowanie wolniej od pozostałych solwe-

    rów.

    Narzędzie LPSolve okazało się również solidne, jednak często okazywało się gorsze od pozosta-

    łych solwerów dla danego problemu. Należy jednak podkreślić, że solwer bardzo dobrze radził sobie

    z problemami całkowitoliczbowymi.

    Najsłabiej należy ocenić solwer QSopt. Zazwyczaj rozwiązywał problemy najdłużej, a najlepszy

    czas uzyskał tylko raz. Duża wada to brak obsługi problemów całkowitoliczbowych.

    Jeśli chodzi o platformy uruchomieniowe Java i C#, należy stwierdzić, że praktycznie nie różnią się

    one wydajnością w dostępie do kodu natywnego.

  • 6.4. Wnioski 33

    0

    200

    400

    600

    800

    1000

    1200

    1400

    1600

    1800

    Java C#

    Śre

    dn

    i cza

    s 5

    0-kr

    otn

    ego

    ład

    ow

    ania

    p

    rob

    lem

    u i

    50

    -kro

    tne

    go t

    wo

    rze

    nia

    ro

    zwią

    zan

    ia [

    ms]

    Platforma

    GLPK

    LPSolve

    Qsopt

    RYSUNEK 6.12: Średnie czasy 50-krotnego ładowania problemu i 50-krotnego tworzenia roz-wiązania dla problemu LOTFI (platformy Java i C#).

    0

    1000

    2000

    3000

    4000

    5000

    6000

    Java C#

    Śre

    dn

    i cza

    s 5

    0-k

    rotn

    ego

    ład

    ow

    ania

    p

    rob

    lem

    u i

    50

    -kro

    tne

    go t

    wo

    rze

    nia

    ro

    zwią

    zan

    ia [

    ms]

    Platforma

    GLPK

    LPSolve

    Qsopt

    RYSUNEK 6.13: Średnie czasy 50-krotnego ładowania problemu i 50-krotnego tworzenia roz-wiązania dla problemu SHIP08S (platformy Java i C#).

  • 6.4. Wnioski 34

    0

    50

    100

    150

    200

    250

    300

    350

    400

    Java C#

    Śre

    dn

    i cza

    s ła

    do

    wan

    ia p

    rob

    lem

    u i

    two

    rze

    nia

    ro

    zwią

    zan

    ia [

    ms]

    Platforma

    GLPK

    LPSolve

    Qsopt

    RYSUNEK 6.14: Średnie czasy ładowania problemu i tworzenia rozwiązania dla problemuD2Q06C (platformy Java i C#).

  • Rozdział 7

    Podsumowanie

    Celem niniejszej pracy było kilka zadań. Należało zaprojektować biblioteki pośredniczące do uru-

    chamiania natywnych bibliotek solwerów w językach wysokopoziomowych. Należało stworzyć apli-

    kację umożliwiającą testowanie solwerów pod kątem wydajności. Wreszcie, należało przeprowadzić

    testy wydajnościowe wybranych solwerów open-source’owych. Dodatkowo, należało stworzyć doku-

    mentację umożliwiającą późniejsze wykorzystanie stworzonych narzędzi pośredniczących.

    Wszystkie zadania wymienione w poprzednim akapicie udało się zrealizować. Niemniej, w czasie

    pracy pojawiło się kilka trudności, o których warto wspomnieć:

    • Pierwszym napotkanym problemem było wykorzystanie mechanizmów do uruchamiania kodu

    natywnego w językach wysokiego poziomu. Szczególnie opanowanie mechanizmu Java Native

    Interface wymagało wstępnej pracy wejścia, ponieważ kod natywny, który ma zostać wykorzy-

    stany w Javie, musi być odpowiednio przygotowany. W przypadku języka C# mechanizm wywo-

    łań niskopoziomowych okazał się prostszy.

    • Inny problem polegał na pozyskaniu bibliotek DLL solwerów wybranych do testowania. W przy-

    padku solwerów LPSolve i QSopt owe biblioteki były przygotowane przez autorów. Jeśli chodzi

    o solwer GLPK, to należało stworzyć bibliotekę na podstawie źródeł projektu.

    • Kolejny problem wiązał się z pozyskaniem instancji problemów do testowania w wybranym for-

    macie MPS. Należało bowiem znaleźć problemy nietrywialne, tzn. takie, w których liczba zmien-

    nych i ograniczeń jest rzędu setek i tysięcy. Poza tym, pliki problemów należało odpowiednio

    przygotować, tzn. usunąć z nich komentarze, ponieważ były one problemem dla solwera GLPK.

    Wszystkie powyższe problemy udało się rozwiązać, a solwery gruntownie przetestować. Wyniki ba-

    danych solwerów pokazały, że są to bardzo solidne narzędzia, mogące konkurować z profesjonalnymi

    produktami. Szczególnie wyróżnić należy solwery GLPK oraz LPSolve, które efektywnie rozwiązują

    problemy programowania liniowego, w tym całkowitoliczbowego. Ich jakość pokazała, że projekty

    open-source’owe mogą dorównywać rozwiązaniom komercyjnym, co należy uznać za jeden z wnio-

    sków całej pracy.

    Stworzony system jest w pełni funkcjonalnym narzędziem do uruchamiania solwerów. Dzięki sys-

    temowi wtyczek, dodawanie nowych solwerów i usuwanie istniejących jest bardzo proste. Program

    nadaje się do testowania solwerów, wyboru najszybszego dla danego typu problemów programowania

    liniowego oraz używania go do konkretnych zadań. Budowa aplikacji w oparciu o wzorzec projek-

    towy MVC pozwala także wykorzystać moduły testujące jako część innego, większego projektu. Można

    bowiem łatwo odłączyć warstwę interfejsu graficznego i korzystać z pozostałych elementów osobno.

    Elastyczność stworzonego narzędzia to jego bardzo ważna cecha, ponieważ można z niego korzystać

    35

  • Podsumowanie 36

    w innych programach, np.: dołączyć do oprogramowania ekonomicznego rozwiązującego problemy

    giełdowe, programu zarządzającego produkcją w dużym zakładzie przemysłowym czy też wykorzy-

    stać w innej pracy badawczej. Przy okazji pracy nad aplikacją pojawił się kolejny wniosek. Mianowi-

    cie, pomimo różnych technologii stosowanych w programowaniu aplikacji, istnieją skuteczne sposoby

    na współpracę programów napisanych w różnych językach programowania.

    Zaprojektowana i zaimplementowana aplikacja może być dalej rozwijana w kilku kierunkach.

    W istocie, program jest prostym narzędziem testującym. Jednym z usprawnień byłoby dodanie edy-

    tora do tworzenia i edytowania problemów przez użytkownika. Wprowadzenie takiej funkcjonalno-

    ści znacznie ułatwiłby zaimplementowany już zestaw metod do modyfikowania problemu (dodawa-

    nie/usuwanie zmiennych, dodawanie/usuwanie ograniczeń itd.). Inny z możliwych kierunków roz-

    woju to stworzenie wersji internetowej aplikacji. Tak stworzony serwis byłby ogólnodostępnym narzę-

    dziem do rozwiązywania problemów programowania liniowego. Innym ciekawym kierunkiem rozwoju

    aplikacji byłoby wprowadzenie równoległości. W czasie, kiedy procesory wielordzeniowe stały się stan-

    dardem, aplikacja powinna korzystać z wielowątkowości, chociażby przez równoległe rozwiązywanie

    problemów na różnych solwerach. Co ważne, powyższe kierunki rozwoju nie wykluczają się, lecz po-

    zwalają na rozwój i szereg zastosowań stworzonej aplikacji.

  • Dodatek A

    Wyniki eksperymentu

    Wartość średnia [ms] Odchylenie standardowe [ms]Problem CPLEX GLPK LPSolve QSopt CPLEX GLPK LPSolve QSoptKB2 71,57 86,29 103,66 199,74 24,03 17,18 20,42 30,4SC50A 39,38 34,63 92,92 80,00 17,13 16,11 19,77 22,38STOCFOR1 87,77 124,65 326,51 252,79 27,13 35,65 27,63 44,23SCAGR7 150,19 249,99 551,56 557,09 40,18 40,82 63,25 45,35ISRAEL 459,77 689,63 812,41 1555,14 35,02 47,71 48,01 35,80

    TABLICA A.1: Czasy 50-krotnego rozwiązywania problemów małych (platforma Java).

    Wartość średnia [ms] Odchylenie standardowe [ms]Problem CPLEX GLPK LPSolve QSopt CPLEX GLPK LPSolve QSoptSHARE2B 148,12 141,69 382,25 624,41 25,41 26,10 25,36 55,61BOEING2 195,33 367,43 679,13 628,99 25,20 37,68 29,29 33,24AGG 232,31 575,44 897,36 918,83 37,36 46,12 58,50 31,71AGG2 - 677,48 1559,50 1702,34 - 96,82 92,34 46,02

    TABLICA A.2: Czasy 50-krotnego rozwiązywania problemów z przewagą liczby ograniczeńnad liczbą zmiennych (platforma Java).

    Wartość średnia [ms] Odchylenie standardowe [ms]Problem GLPK LPSolve QSopt GLPK LPSolve QSoptD6CUBE 2625,00 1135,64 12394,10 82,73 79,70 193,17TRUSS 4655,02 7156,48 9651,56 345,00 1044,21 718,67FIT2D 19434,40 6247,00 100971,80 1187,81 390,58 3343,49

    TABLICA A.3: Czasy rozwiązywania problemów z przewagą liczby zmiennych nad liczbąograniczeń (platforma Java).

    Wartość średnia [ms] Odchyleni