proto bychom rádi pro danou posloupnost aut provedli co nejméně změn.
proto bychom rádi pro danou posloupnost aut provedli co nejméně změn.
Je známé, že úloha je NP-těžká a za určitých předpokladů dokonce neaproximovatelná,
Je známé, že úloha je NP-těžká a za určitých předpokladů dokonce neaproximovatelná,
proto je na místě zkoumat řešení, co se chovají dobře na náhodném vstupu.
proto je na místě zkoumat řešení, co se chovají dobře na náhodném vstupu.
V této práci je představen nový algoritmus založený na semidefinitním programování, který dle provedených měření pro náhodné vstupy s až 400 typy aut dosahuje výsledků okolo 0.33-násobku počtu typů aut.
V této práci je představen nový algoritmus založený na semidefinitním programování,
O algoritmu jsme dokázali, že pro každý vstup vrátí řešení nejhůře o 0.212 násobek počtu typů aut horší než optimum.
který dle provedených měření pro náhodné vstupy dosahuje výsledků okolo 0.34-násobku počtu typů aut.
Bohužel se mi nepodařilo dokázat žádný netriviální horní odhad na střední hodnotu počtu změn na náhodném vstupu.
O algoritmu jsme dokázali, že pro každý vstup vrátí ve střední hodnotě řešení nejhůře o 0.212 násobek počtu typů aut horší než optimum.
Bohužel se nám nepodařilo dokázat žádný netriviální horní odhad na střední hodnotu počtu změn na náhodném vstupu.
Tuto hodnotu mimo jiné můžeme chápat jako střední hodnotu skóre pro uniformně náhodný vstup délky $n$.
Tuto hodnotu mimo jiné můžeme chápat jako střední hodnotu skóre pro uniformně náhodný vstup délky $n$.
K tomu ještě zavedeme $\delta_{\alg}(\alpha) = \gamma_{\alg}(\alpha)/n_{\alpha}$ a analogicky $\delta_{\alg}(n) = \gamma_{\alg}(n)/n$.
K tomu ještě zavedeme notaci pro relativní skóre $\delta_{\alg}(\alpha) = \gamma_{\alg}(\alpha)/n_{\alpha}$ a analogicky průměrné relativní skóre $\delta_{\alg}(n) = \gamma_{\alg}(n)/n$.
Nakonec označme $\delta_{\alg} = \lim_{n\to\infty} \delta_{\alg}(n) = \lim_{n\to\infty} \gamma_{\alg}(n)/n$.
Nakonec označme $\delta_{\alg} = \lim_{n\to\infty} \delta_{\alg}(n) = \lim_{n\to\infty} \gamma_{\alg}(n)/n$.
Protože limita nemusí vždy existovat, zavedeme ještě limes superior a limes inferior:
Protože limita nemusí vždy existovat, zavedeme ještě limes superior a limes inferior:
$\delta^+_{\alg} = \limsup_{n\to\infty} \delta_{\alg}(n)$ a
$\delta^+_{\alg} = \limsup_{n\to\infty} \delta_{\alg}(n)$ a
Protože ne na všechny problémy známe polynomiální algoritmus, co je schopný je vyřešit,
Protože ne na všechny problémy známe polynomiální algoritmus, co je schopný je vyřešit,
zajímavý výsledek může být, i když se k řešení zvládneme jen v nějakém smysl alespoň přiblížit.
zajímavý výsledek může být, i když se k řešení zvládneme jen v nějakém smyslu alespoň přiblížit.
Na to nejprve musíme říct, co pro nás znamená, že nějaké řešení je několikrát horší než jiné. To nám poskytne obecná definice optimalizačního problému.
Na to nejprve musíme říct, co pro nás znamená, že nějaké řešení je několikrát horší než jiné. To nám poskytne obecná definice optimalizačního problému.
::: {c=box t=def name="Optimalizační problém"}
::: {c=box t=def name="Optimalizační problém"}
...
@@ -172,35 +184,35 @@ přípustných řešení $F(I)$.
...
@@ -172,35 +184,35 @@ přípustných řešení $F(I)$.
Dále existuje účelová funkce $f$, která pro každý vstup a jeho přípustné řešení určuje reálné nezáporné číslo -- jeho hodnotu.
Dále existuje účelová funkce $f$, která pro každý vstup a jeho přípustné řešení určuje reálné nezáporné číslo -- jeho hodnotu.
Pokud se jedná o minimalizační problém, tak pod pojmem _optimum_ daného vstupu (značíme $\opt(I)$) myslíme infimum hodnot účelové funkce přes všechny přípustné řešení, tedy $\inf f[F(i)]$.
Pokud se jedná o minimalizační problém, tak pod pojmem _optimum_ daného vstupu (značíme $\opt(I)$) myslíme infimum hodnot účelové funkce přes všechny přípustné řešení, tedy $\inf f[F(i)]$.
Pro minimalizační problém analogicky použijeme supremum.
Pro maximalizační problém analogicky použijeme supremum.
:::
:::
A nyní již přejdeme k samotným definicím algoritmů blížících se optimu.
A nyní již přejdeme k samotným definicím algoritmů blížících se optimu.
::: {c=box t=def name="Aproximační algoritmus"}
::: {c=box t=def name="Aproximační algoritmus"}
Algoritmus $\alg$ je $g(n)$-aproximační na minimalizačním problému,
Algoritmus $\alg$ je $g(n)$-aproximační na minimalizačním problému,
pokud pro každý vstup $I$ algoritmus vrátí přípustné řešení $\algo(I)$, pro které platí, že $f(\algo(I)) \le g(|I|) \cdot \opt(I)$.
pokud pro každý vstup $I$ algoritmus vrátí přípustné řešení $\alg(I)$, pro které platí, že $f(\alg(I)) \le g(|I|) \cdot \opt(I)$.
:::
:::
Předešlá definice záleží na tom, co považujeme za velikost vstupu, což se pro různé problémy a jejich interpretace může lišit.
Předešlá definice záleží na tom, co považujeme za velikost vstupu, což se pro různé problémy a jejich interpretace může lišit.
Naštěstí nás většinou budou zajímat $c$-aproximace, tedy
Naštěstí nás většinou budou zajímat $c$-aproximace, tedy
aproximace, kde $f(n)$ je konstantní funkce rovna $c$.
aproximace, kde $g(n)$ je konstantní funkce rovna $c$.
U maximalizačních problémů je drobný problém v terminologii,
U maximalizačních problémů je drobný problém v terminologii,
protože není shoda na tom, jestli má platit
protože není shoda na tom, jestli má platit
$f(A(I)) \ge g(|I|) \cdot \opt(I)$
$f(A(I)) \ge g(|I|) \cdot \opt(I)$
nebo
nebo
$f(A(I)) \ge {1\over g(|I|)} \cdot \opt(I)$.
$f(A(I)) \ge {1\over g(|I|)} \cdot \opt(I)$.
Naštěstí podle kontextu jde snadno rozžnout, která definice se používá,
Naštěstí podle kontextu jde snadno rozhodnout, která definice se používá,
protože je potřeba násobit optimum číslem menším rovno jedné (jinak by pro
protože je potřeba násobit optimum číslem menším rovno jedné (jinak by pro
kladné hodnoty účelové funkce nemohl existovat žádný vyhovující algoritmus).
kladné hodnoty účelové funkce nemohl existovat žádný vyhovující algoritmus).
Bohužel někdy asi ani s aproximačními algoritmy nevystačíme a proto zavedeme
Bohužel někdy asi ani s aproximačními algoritmy nevystačíme a proto zavedeme
Algoritmus $\alg$ je pravděpodobnostně $g(n)$-aproximační, existuje konstanta $p \in (0,1)$, taková, že pro každý vstup $I$ algoritmus vrátí přípustně řešení,
Algoritmus $\alg$ je pravděpodobnostně $g(n)$-aproximační, existuje-li konstanta $p \in (0,1)$, taková, že pro každý vstup $I$ algoritmus vrátí přípustně řešení,
pro které s pravděpodobností $\ge p$ platí, že $f(\alg(I)) \le g(|I|) \cdot \opt(I)$.
pro které s pravděpodobností alespoň $p$ platí, že $f(\alg(I)) \le g(|I|) \cdot \opt(I)$.
:::
:::
Všimněme si, že na konstantě $p$ příliš nezáleží.
Všimněme si, že na konstantě $p$ příliš nezáleží.
...
@@ -209,9 +221,9 @@ z dodaných řešení zvládneme $p$ libovolně těsně přiblížit k jedné.
...
@@ -209,9 +221,9 @@ z dodaných řešení zvládneme $p$ libovolně těsně přiblížit k jedné.
Pokud řešíme složitost algoritmů a úloh,
Pokud řešíme složitost algoritmů a úloh,
většinou vyžadujeme, aby účelová funkce i rozhodování přípustnosti řešení byly vyčíslitelné v polynomiálním čase.
většinou vyžadujeme, aby účelová funkce i rozhodování přípustnosti řešení byly vyčíslitelné v polynomiálním čase.
Navíc chceme, aby všechny přípustné řešení měli omezenou délku nějakým polynomem v délce vstupu.
Navíc chceme, aby všechna přípustná řešení měla omezenou délku nějakým polynomem v délce vstupu.
Snadno nahlédneme, že za takovýchto podmínek je rozhodovací verze,
Snadno nahlédneme, že za takovýchto podmínek je rozhodovací verze,
jestli je optimum alespoň zadané číslo v $NP$.
jestli je optimum alespoň zadané číslo, v $NP$.
Pro takovou úlohu většinou hledáme polynomiální aproximační algoritmus.
Pro takovou úlohu většinou hledáme polynomiální aproximační algoritmus.
Binární paint shop vyhovuje všem těchto podmínkám.
Binární paint shop vyhovuje všem těchto podmínkám.
...
@@ -220,11 +232,12 @@ Doposud známé výsledky
...
@@ -220,11 +232,12 @@ Doposud známé výsledky
Bonsmaa, Epping, a Hochstättler [@apx] dokázali, že optimalizační verze BPS je $\APX$-těžký problém,
Bonsmaa, Epping, a Hochstättler [@apx] dokázali, že optimalizační verze BPS je $\APX$-těžký problém,
což je silnější tvrzení, než že rozhodovací verze je $\NP$-těžká.
což je silnější tvrzení, než že rozhodovací verze je $\NP$-těžká.
Za předpokladu, že $\P\neq \NP$, víme že kromě polynomiálního algoritmu na rozhodovací verzi nemůže existovat
Za předpokladu $\P\neq \NP$ víme, že kromě polynomiálního algoritmu na rozhodovací verzi nemůže existovat
ani polynomiální aproximační schéma na optimalizační verzi.
ani polynomiální aproximační schéma na optimalizační verzi.
Snadno nahlédneme, že rozhodovací problém patří do $\NP$. Když nám někdo dá přiřazení barev autům.
Snadno nahlédneme, že rozhodovací problém patří do $\NP$.
V lineárním čase jsme schopni ověřit, že počet změn je dostatečně malý a od každého typu a barvy auta
Když nám někdo dá přiřazení barev autům,
v lineárním čase jsme schopni ověřit, že počet změn je dostatečně malý a od každého typu a barvy auta
je právě jeden výskyt. Tedy rozhodovací problém je $\NP$-úplný.
je právě jeden výskyt. Tedy rozhodovací problém je $\NP$-úplný.
Dále pak Gupta a spol. [@neaprox] ukázali, že za předpokladu Unique games conjecture je NP-těžké i problém libovolně konstantně aproximovat.
Dále pak Gupta a spol. [@neaprox] ukázali, že za předpokladu Unique games conjecture je NP-těžké i problém libovolně konstantně aproximovat.
...
@@ -236,7 +249,7 @@ ovšem aproximovatelnost se na nich chová drasticky jinak.
...
@@ -236,7 +249,7 @@ ovšem aproximovatelnost se na nich chová drasticky jinak.
V momentě, kdy jsou skoro všechny hrany v řezu, tak přidání další hrany do řezu skoro nezmění počet hran v něm. Ovšem odebrání hrany z ne-řezu bude mít velký vliv (procentuálně) na počet hran v ne-řezu.
V momentě, kdy jsou skoro všechny hrany v řezu, tak přidání další hrany do řezu skoro nezmění počet hran v něm. Ovšem odebrání hrany z ne-řezu bude mít velký vliv (procentuálně) na počet hran v ne-řezu.
Vzhledem k předešlým výsledkům je na místě zkoumat různé heuristiky a obecně algoritmy, co
Vzhledem k předešlým výsledkům je na místě zkoumat různé heuristiky a obecně algoritmy, co
jsou nás schopny k řešení alespoň částečně přiblížit.
jsou nás schopny k optimálnímu řešení alespoň částečně přiblížit.
Jedním z takových algoritmů je hladový algoritmus popsaný Andresem and Hoch\-stättlerem [@gr]:
Jedním z takových algoritmů je hladový algoritmus popsaný Andresem and Hoch\-stättlerem [@gr]:
...
@@ -271,7 +284,7 @@ fig.update_layout(
...
@@ -271,7 +284,7 @@ fig.update_layout(
showlegend=False
showlegend=False
)
)
```
```
Graf střední hodnoty skóre hladového řešení $\delta_{\algo g}(n)$ v závislosti na $n$.
Graf střední hodnoty relativního skóre hladového řešení $\delta_{\algo g}(n)$ v závislosti na $n$.
:::
:::
U hladového řešení je tedy střední hodnota skóre přes uniformně náhodný vstup přesně vyčíslená.
U hladového řešení je tedy střední hodnota skóre přes uniformně náhodný vstup přesně vyčíslená.
...
@@ -308,13 +321,13 @@ musí použít aspoň $\Omega(n)$ změn barev.
...
@@ -308,13 +321,13 @@ musí použít aspoň $\Omega(n)$ změn barev.
Nemůže tedy například existovat algoritmus, kterému by stačilo
Nemůže tedy například existovat algoritmus, kterému by stačilo
jen $\O(n / \log n)$ změn.
jen $\O(n / \log n)$ změn.
To také říká, že odhadovat algoritmy pomocí $\lim_{n\to\infty} \gamma_{\alg}/n$,
To také říká, že odhadovat algoritmy pomocí $\lim_{n\to\infty} \gamma_{\alg}/n$,
je alespoň co se týče asymptoticky dostačující, protože vystihuje
je alespoň co se týče asymptotiky dostačující, protože vystihuje
Z řady aut odstraníme první auto a auto stejného typu.
Z řady aut odstraníme první auto v řadě a druhé auto příslušného typu.
Zbytek aut rekurzivně obarvíme a pak do řady přidáme odebranou dvojici tak,
Zbytek aut rekurzivně obarvíme a pak do řady přidáme odebranou dvojici tak,
aby byl počet změn co možná nejmenší možný.
aby byl počet změn co možná nejmenší možný.
:::
:::
...
@@ -323,32 +336,32 @@ Autoři algoritmu, Andres and Hochstättler [@gr], o něm dokázali, že
...
@@ -323,32 +336,32 @@ Autoři algoritmu, Andres and Hochstättler [@gr], o něm dokázali, že
${2\over 5}\ n - {8\over 15} \le \gamma_{\algo{rg}}(n) \le {2\over 5}\ n + {7\over 10}$,
${2\over 5}\ n - {8\over 15} \le \gamma_{\algo{rg}}(n) \le {2\over 5}\ n + {7\over 10}$,
tedy $\delta_{\algo{rg}} = 2/5 = 0.4$.
tedy $\delta_{\algo{rg}} = 2/5 = 0.4$.
Dále se o zlepšení tohoto algoritmu pokusili Hančl a kol.
Dále se o zlepšení tohoto algoritmu pokusili Hančl a kol. [@docw].
Ti si všimli, že ve výstupu rekurzivního řešení se občas stane,
Ti si všimli, že ve výstupu rekurzivního řešení se občas stane,
že přebarvením dvojic typů aut se zlepší skóre. Ukázali, že
že přebarvením dvojic typů aut se zlepší skóre. Ukázali, že
takových dvojic je vždy ve výstupu lineárně mnoho a z toho pak
takových dvojic je vždy ve výstupu lineárně mnoho a z toho pak
ukázali horní odhad $\delta^+ \le 0.4 - \varepsilon < 0.4$ (pro $\varepsilon$ zhruba $2\cdot 10^6$).
ukázali horní odhad $\delta^+ \le 0.4 - \varepsilon < 0.4$ (pro $\varepsilon$ zhruba $2\cdot 10^{-6}$).
Hančl a kol. přišli ještě s dalším způsobem, jak vylepšit rekurzivní hladové řešení.
Hančl a kol. [@docw] přišli ještě s dalším způsobem, jak vylepšit rekurzivní hladové řešení.
Při běhu hladového řešení se občas stane, že některá dvojice aut stejného typu
Při běhu hladového řešení se občas stane, že některá dvojice aut stejného typu
si může prohodit barvy bez změny skóre řešení.
si může prohodit barvy bez změny skóre řešení.
Když přidáváme auto do okolí takovéto dvojice, v některých případech můžeme provést toto prohození
Když přidáváme auto do okolí takovéto dvojice, v některých případech můžeme provést toto prohození
a tím snížit počet nově vytvořených změn barev.
a tím snížit počet nově vytvořených změn barev.
Budeme si tedy bude udržovat některé z takovýchto dvojic
Budeme si tedy udržovat některé z takovýchto dvojic
a pokud se budeme přidávat auto do okolí evidované dvojice, tak, že
a pokud budeme přidávat auto do okolí evidované dvojice tak, že
prohození barev dané dvojice by pomohlo, prohodíme její barvy.
prohození barev dané dvojice by pomohlo, prohodíme její barvy.
Budeme pracovat nad rozšířenou abecedou barev o znak "$*$" reprezentující neurčenou barvu.
Budeme pracovat nad rozšířenou abecedou barev o znak "$*$" reprezentující neurčenou barvu.
Při rekurzi budeme udržovat invariant, že pro každý typ obě auta buď budou označeny $*$ a nebo ani jedno z nich nebude označeno $*$ a pak nutně budou mít různé barvy.
Při rekurzi budeme udržovat invariant, že pro každý typ obě auta buď budou označeny $*$ a nebo ani jedno z nich nebude označeno $*$ a pak nutně budou mít různé barvy.
Navíc bude platit, že $*$ nikdy není na okrajích a nejsou dvě vedle sebe.
Navíc bude platit, že $*$ nikdy není na okrajích a nejsou dvě vedle sebe.
Specificky tedy na $*$ se budeme přebarvovat dvojice aut v momentě, kdy získá všechny sousedy.
Specificky tedy na $*$ budeme přebarvovat dvojici aut v momentě, kdy získá všechny sousedy.
Pro popis algoritmu zavedeme notaci $N(i)$,
Pro popis algoritmu zavedeme notaci $N(i)$,
což bude reprezentovat multimnožinu barev aut sousedících s autem na pozici $0<i<2n-1$,
což bude reprezentovat multimnožinu barev aut sousedících s autem na pozici $0<i<2n-1$,
Znázornění řezu rovinou obsahující $vec{u}$ i $vec{v}$.
Znázornění řezu rovinou obsahující $vec{u}$ i $vec{v}$. TODO
:::
:::
...
@@ -662,13 +673,13 @@ Vrcholům jedné množiny přidělíme vektory $(1,0,\dots,0)$ a druhé $(-1,0,\
...
@@ -662,13 +673,13 @@ Vrcholům jedné množiny přidělíme vektory $(1,0,\dots,0)$ a druhé $(-1,0,\
Bohužel na rozdíl od lineárního programování, u semidefinitního programování nejsme schopní v polynomiálním čase najít optimální řešení.
Bohužel na rozdíl od lineárního programování, u semidefinitního programování nejsme schopní v polynomiálním čase najít optimální řešení.
Naštěstí však platí, že v polynomiálním čase za určitých podmínek jsme schopní se mu libovolně přiblížit.
Naštěstí však platí, že v polynomiálním čase za určitých podmínek jsme schopní se mu libovolně přiblížit.
Přesněji řečeno, pro každé $\varepsilon > 0$ jsme schopní najít řešení s účelovou funkcí nejdále $\varepsilon$ od optima.
Přesněji řečeno, pro každé $\varepsilon > 0$ jsme schopní najít řešení s účelovou funkcí nejdále $\varepsilon$ od optima.
Ovšem musí platit, že všechny přípustná řešení jsou dostatečně málá.
Ovšem musí platit, že všechna přípustná řešení jsou dostatečně malá.
Přesněji řečeno, musí platit, že Fobeniova norma všech přípustných matic je omezena konstantou s polinomiální délkou zápisu.
Přesněji řečeno, musí platit, že Fobeniova norma všech přípustných matic je omezena konstantou s polynomiální délkou zápisu.
Tohoto výsledku jde dosáhnout pomocí elipsoidové metody, která má nejlepší teoretické výsledky. V prakxi se však často používají jiné algoritmy.
Tohoto výsledku jde dosáhnout pomocí elipsoidové metody, která má nejlepší teoretické výsledky. V praxi se však často používají jiné algoritmy.
Všechny naše programy budou splňovat podmínky na řešení, protože každá z hodnot hledané matice nutně bude ležet v intervalu $[-1, -1]$.
Všechny naše programy budou splňovat podmínky na řešení, protože každá z hodnot hledané matice nutně bude ležet v intervalu $[-1, -1]$.
::: {c=box t=theorem}
::: {c=box t=theorem}
Goemans-Williamsonův algoritmus je $0.878$ pravděpodobnostní aproximační algoritmus.
Goemans-Williamsonův algoritmus je pravděpodobnostní $0.878$-aproxi\-mační algoritmus.
:::
:::
::: {c=proof}
::: {c=proof}
Využitím předešlých pozorování a elipsoidové metody získáme:
Využitím předešlých pozorování a elipsoidové metody získáme:
...
@@ -690,10 +701,6 @@ vynutíme $\vec{y_i} = -\vec{y_j}$. Na to nám stačí jediná podmínka -- ří
...
@@ -690,10 +701,6 @@ vynutíme $\vec{y_i} = -\vec{y_j}$. Na to nám stačí jediná podmínka -- ří
Účelovou funkcí pak řekneme, že sousední auta mají preferovat stejnou barvu, tedy jejich body na sféře mají být blízko sebe, což znamená, že skalární součin má být co největší.
Účelovou funkcí pak řekneme, že sousední auta mají preferovat stejnou barvu, tedy jejich body na sféře mají být blízko sebe, což znamená, že skalární součin má být co největší.
Finální program
semidefinitní program v dekomponovaném tvaru tedy vypadá takto:
:::{c=box t=algo name="Řešení pomocí semidefinitního programování" notation="sdp"}
:::{c=box t=algo name="Řešení pomocí semidefinitního programování" notation="sdp"}
\hfil{\penalty-10000}\relax Vyřešíme následující semidefinitní program v dekomponovaném tvaru:
\hfil{\penalty-10000}\relax Vyřešíme následující semidefinitní program v dekomponovaném tvaru:
Nyní tedy pro každou dvojici sousedních aut sčítáme číslo mezi $0$ a $1$, kde $0$ nastane pro stejné vektory
Nyní tedy pro každou dvojici sousedních aut přičítáme číslo mezi $0$ a $1$, kde $0$ nastane pro stejné vektory,
mezi nimiž nikdy nebude změna barvy a $1$ nastane v případě opačných vektorů, mezi nimiž se nutně barva změní.
mezi nimiž nikdy nebude změna barvy a $1$ nastane v případě opačných vektorů, mezi nimiž se nutně barva změní.
Účelovou funkci se snažíme minimalizovat.
Účelovou funkci se snažíme minimalizovat.
...
@@ -717,14 +724,14 @@ mezi nimiž nikdy nebude změna barvy a $1$ nastane v případě opačných vekt
...
@@ -717,14 +724,14 @@ mezi nimiž nikdy nebude změna barvy a $1$ nastane v případě opačných vekt
Optimum semidefinitního programu, který minimalizuje účelovou funkci $\frac{2n-1}2 - \frac12 \sum_{0 \le i < 2n-1} \vec{y_i}^{\rm T} \vec{y_{i+1}}$ je menší rovno počtu změn v optimálním obarvení aut.
Optimum semidefinitního programu, který minimalizuje účelovou funkci $\frac{2n-1}2 - \frac12 \sum_{0 \le i < 2n-1} \vec{y_i}^{\rm T} \vec{y_{i+1}}$ je menší rovno počtu změn v optimálním obarvení aut.
:::
:::
::: {c=proof}
::: {c=proof}
Podíváme se na optimální obarvení aut a každému červenému autu přiřadit vektor $(1,0,\dots,0)$ a modrému $(-1,0,\dots,0)$.
Podíváme se na optimální obarvení aut a každému červenému autu přiřadíme vektor $(1,0,\dots,0)$ a modrému $(-1,0,\dots,0)$.
Toto je validní řešení semidefinitního programu.
Toto je validní řešení semidefinitního programu.
Účelová funkce za každou změnu barev přičte $1$ a zbytek součtu je $0$, takže její hodnota je přesně počet změn barev.
Účelová funkce za každou změnu barev přičte $1$ a zbytek součtu je $0$, takže její hodnota je přesně počet změn barev.
:::
:::
Nyní by se hodilo odhadnout střední hodnotu počtu změn barev stejně jako u maximálního řezu.
Nyní by se hodilo odhadnout střední hodnotu počtu změn barev stejně jako u maximálního řezu.
Jeho důkaz vycházel z pozorování ohledně poměru pravděpodobností výběru a účelové funkce.
Jeho důkaz vycházel z pozorování ohledně poměru pravděpodobností výběru a účelové funkce,
Přesněji řečeno z toho, že tento poměr umíme zdola odhadnout.
přesněji řečeno z toho, že tento poměr umíme zdola odhadnout.
Ovšem v našem případě místo maximalizace děláme minimalizaci účelové funkce a pravděpodobnosti.
Ovšem v našem případě místo maximalizace děláme minimalizaci účelové funkce a pravděpodobnosti.
Tedy abychom mohli tvrdit, že pravděpodobnost je menší než nějaký násobek účelové funkce,
Tedy abychom mohli tvrdit, že pravděpodobnost je menší než nějaký násobek účelové funkce,
potřebovali bychom horní odhad poměru. Ovšem tento poměr je neomezený (viz obrázky [](#gw-func) a [](#gw-frac)).
potřebovali bychom horní odhad poměru. Ovšem tento poměr je neomezený (viz obrázky [](#gw-func) a [](#gw-frac)).
...
@@ -773,9 +780,9 @@ Střední hodnota (vzhledem k náhodným bitům generovaným algoritmem)
...
@@ -773,9 +780,9 @@ Střední hodnota (vzhledem k náhodným bitům generovaným algoritmem)
počtu změn v $\algo{sdp}$ řešení je nejvýše o $0.212n$ horší než optimum.
počtu změn v $\algo{sdp}$ řešení je nejvýše o $0.212n$ horší než optimum.
:::
:::
::: {c=proof}
::: {c=proof}
Když je účelová funkce pro sousední dvojici aut je $r$, tak pravděpodobnost změny barev je nejvýše $d + r$.
Když účelová funkce pro sousední dvojici aut je $r$, tak pravděpodobnost změny barev je nejvýše $d + r$.
Sečtením přes všechny hrany (z linearity středních hodnot) tedy získáme, že
Sečtením přes všechny hrany (z linearity středních hodnot) tedy získáme, že
střední hodnota součtu změn je nejvýše $c(2n-1) + R$, kde $R = \sum_i^{2n-1} \frac{1}{2} - \frac{\vec{y_i}^{\rm T} \vec{y_{i+1}}}{2}$, tedy jedno z ekvivalentních vyjádření účelové funkce.
střední hodnota součtu změn je nejvýše $d(2n-1) + R$, kde $R = \sum_i^{2n-1} \frac{1}{2} - \frac{\vec{y_i}^{\rm T} \vec{y_{i+1}}}{2}$, tedy jedno z ekvivalentních vyjádření účelové funkce.
@@ -811,15 +818,115 @@ proto v implementační části práce využijeme této formy programu.
...
@@ -811,15 +818,115 @@ proto v implementační části práce využijeme této formy programu.
Měření SDP řešení
Měření SDP řešení
=================
=================
Součástí práce je implementace algoritmů řešících Binární paint shop problém.
Každý z nich byl následně spuštěn pro různé velikosti, pokaždé na 100 nezávisle náhodně vybraných vstupech.
Celý test běžel jedno vláknově zhruba dva dny a využíval nejvýše 16 GB operační paměti.
Na následujících stranách jsou zpracovaná různá naměřená data.
Praktické řešení semidef. programů
-----------------------------------------
U triviálních algoritmů byla implementace poměrně přímočará.
Ovšem u řešení semidefinitního programování je většina složitosti algoritmu schovaná právě v řešení semidefinitních programů,
což už svojí složitostí nepatří mezi algoritmy, které bych chtěl (re)implementovat.
Proto je nutné se spolehnout na funkčnost již existujících implementací.
Bohužel kvalita dostupných řešičů semidefinitních programů je poměrně nízká.
### SDPA-C
Jedním z použitých programů byl SDPA-C [@sdpa-web].
Jedná se o knihovnu v C`++`
založenou na metodě vnitřních bodů v primárním a duálním problému. Dle autorů projektu [@sdpa-web]:
::: {.group lang=cs}
> SDPA (SemiDefinite Programming Algorithm) is one of the most efficient and stable software packages for solving SDPs based on the primal-dual interior-point method. It fully exploits the sparsity of given problems.
:::
Pro účely práce byla použita verze SDPA-C ze dne 2023-06-21 (soubor `sdpa-c.7.3.8.src.20180125.tar.gz`).
Kompilace je poměrně komplikovaná.
Jednak vyžaduje velké množství nainstalovaného software (například překladač Fortranu), ale také poskytnutý `Makefile` není plně kompatibilní s moderními verzemi překladačů.
Bohužel i po úspěšné kompilaci stále není vyhráno.
Dodaný program přeložený aktuálním kompilátorem bohužel
nefunguje.
Při spuštění i na malých vstupech program rychle spadne s chybou Segmentation Fault
ve funkci `Newton::compute_bMatgVec_dense_threads_SDP`.
Při následujícím ladění programu bylo zjištěno, že se program zacyklí v dané funkci ve smyčce, při které inkrementuje hodnotu proměnné
`Column_Number` až do doby, kdy přeteče, což už přímo vede k pádu programu.
Pohledem na kód to vypadá, že toto by se nemělo dít.
Program už mnohokrát měl ukončit smyčku a doběhnout na konec funkce, ovšem po přidání dostatečného počtu debugovacích výpisů je
je vidět, že program cyklus opustí, provede příkazy mezi cyklem a koncem funkce a pak se zase objeví uprostřed cyklu.
Problém byl v tom, že funkce vracející `void*`
dle normy [@cpp-norm] bodu 6.6.3
musí skončit pomocí `return` a nemůže jen tak opustit funkci dojitím na její konec.
Pokud se tak stane, jedná se o nedefinované chování,
nikoliv jen o nedefinovanou hodnotu navrácenou z funkce.
Kdyby se jednalo jen o nedefinovanou hodnotu,
tak se nic zásadního neděje. Funkce může vrátit cokoliv, ale to je nám jedno, protože výsledek se ignoruje.
Ovšem jelikož se jedná o nedefinované chování, překladač může program
přeložit tak, že v takovémto případě udělá prakticky cokoliv.
A v tomto případě optimalizace překladače přeložili program tak,
že v případě opuštění funkce se vykonávání programu vrátí zpět dovnitř a pokračuje se v iterování smyčky.
To je důsledkem toho, jakým způsobem se snaží překladač optimalizovat kód.
Překladače nemusí dodržovat naprogramovanou strukturu programu.
Tedy není problém přesunout část za smyčkou do ní na místo, kde se smyčka opouští.
No a když překladač ví, že v korektním programu by se tudy nemělo opouštět funkci, nemusí se překladač obtěžovat umísťováním explicitního návratu z funkce na dané místo.
Naštěstí oprava tohoto problému je přímočará -- stačí do takovýchto funkcí explicitně dopsat `return NULL;`.
Toto bohužel není jediný problém s SDPA-C.
Na některých vstupech knihovna dojde do stavu, kdy nemá semidefinitní matici a výpočet spadne s následující chybou:
Tedy během výpočtu program něco spočte špatně a jako mezivýsledek mu vyjde matice, co není pozitivně semidefinitní, se kterou již dále nejde pokračovat.
Příčinu této chyby se bohužel nepodařilo odhalit, protože na rozdíl od předchozího problému vůbec není jasné, kde začít hledat.
Detekce nesemidefinitnosti matice totiž může být v úplně jiné části kódu, než kde nastane chyba.
Problém se projevuje například na vstupu s $n=5$ a seedem $19$.
Další problém (nebo ten stejný) se projevuje zejména u větších vstupů.
Uprostřed algoritmu se také objeví chybová hláška o nesemidefinitnosti matice.
Následně program udělá mnoho iterací s nekonečnou účelovou funkcí, dokud se nepřekročí maximální počet iterací a pak je vrácena matice
ze samých $\pm \infty$, což dokonce porušuje zadané podmínky.
Toto se stává například na seedu $18$ pro $n = 150$.
Vzhledem k výše uvedeným problémům dává smysl se porozhlédnout po alternativách.
### Sage
SageMath je dle oficiálních stránek [@sagemath] open-source software pro matematické výpočty založený na rozšířené syntaxi pythonu,
takže je poměrně snadné ho využívat i jako programovací jazyk.
Sage mimo jiné obsahuje rozhraní pro řešení semidefinitních programů.
Rozhraní je navrženo tak, aby bylo schopné pracovat s více různými backendy pro řešení semidefinitních programů.
Bohužel z dokumentace [@sage-semidef-doc] nebylo zřejmé, jaké backendy sage podporuje.
Při zavolání `default_sdp_solver("")` se objeví chybová hláška, která tvrdí, že backend by mělo jít nastavit na
`CVXOPT` (což je výchozí hodnota) nebo `Matrix`, případně uživatelem dodanou třídu.
Ovšem při přepnutí na `Matrix` spuštění řešení spadne na `NotImplementedError`, tedy zdá se, že ani tento backend není implementovaný (alespoň ve verzi, co mám k dispozici) a tedy jediná možnost je `CVXOPT`.
Drobným problémem je, že dle dokumentace [@sage-semidef-doc] rozhraní sage vyžaduje formulování vstupu jako primárního semidefinitního programu,
ovšem náš algoritmus je založený na řešení duálního semidefinitního programu.
Mezi nimi však lze snadno přecházet pomocí triviální úpravy, ovšem výsledný kód pak vypadá velice neintuitivně.
Hodnoty skalárních součinů
Hodnoty skalárních součinů
--------------------------
--------------------------
V odhadu střední hodnoty počtu změn barev v řešení jsme využívali toho, že funkce rozdílu pravděpodobnosti řezu a účelové funkce (funkce $f$) je nejvýše $d = 0.1053$.
V odhadu střední hodnoty počtu změn barev v $\algo{sdp}$ řešení jsme využívali toho, že funkce rozdílu pravděpodobnosti řezu a účelové funkce (funkce $f$) je nejvýše $d = 0.1053$.
Ovšem tato funkce nenabývá takto vysokých hodnot zdaleka všude, na polovině definičního oboru je dokonce záporná (viz obrázek [](#sdp-diff)). Kdyby tedy alespoň část optimalizovaných skalárních součinů se vyskytovala mimo oblast, kde $f$ nabývá vysokých hodnot, šel by odhad zlepšit.
Ovšem tato funkce nenabývá takto vysokých hodnot zdaleka všude, na polovině definičního oboru je dokonce záporná (viz obrázek [](#sdp-diff)). Kdyby tedy alespoň část optimalizovaných skalárních součinů se vyskytovala mimo oblast, kde $f$ nabývá vysokých hodnot, šel by odhad zlepšit.
Bohužel dle naměřených dat to vypadá, že hodnoty skalárních součinů se
Bohužel dle naměřených dat to vypadá, že hodnoty skalárních součinů se
koncentrují pouze poblíž maxima $f$. Viz obrázek [](#scalar_products_200).
koncentrují pouze poblíž maxima $f$. Viz obrázek [](#scalar_products_200).
Tento výsledek intuitivně dává smysl, protože jenom tato může semidefinitní
Tento výsledek intuitivně dává smysl, protože jenom takto může semidefinitní
program dosáhnout lepší účelové funkce než počet změn barev v optimálním řešení.
program dosáhnout lepší účelové funkce než počet změn barev v optimálním řešení.
Ovšem náš jediný dolní odhad je hodnota semidefinitního programu a tedy nejsme schopní více přiblížit dolní a horní odhad k sobě.
Ovšem náš jediný dolní odhad je hodnota semidefinitního programu a tedy nejsme schopní více přiblížit dolní a horní odhad k sobě.
Z grafu jde vidět, že skoro nikdy semidefinitní program neumístí sousední body do protilehlých částí. Drobnou výjimku tvoří hodnota skalárního součinu okolo $-1$, kterých semidefinitní program
Z grafu jde vidět, že skoro nikdy semidefinitní program neumístí sousední body do protilehlých částí. Drobnou výjimku tvoří hodnota skalárního součinu okolo $-1$, kterých semidefinitní program
za $100$ bodů vyrobil zhruba $100$.
za $100$ běhů vyrobil zhruba $100$.
K tomuto mohl být donucen existencí dvojic aut stejného typu hned vedle sebe, kdy nemá jinou možnost než mezi nimi mít skalární součin $-1$.
K tomuto mohl být donucen existencí dvojic aut stejného typu hned vedle sebe, kdy nemá jinou možnost než mezi nimi mít skalární součin $-1$.
::: {c=box t=lemma}
::: {c=box t=lemma}
...
@@ -855,15 +962,14 @@ Střední hodnota počtu sousedních aut stejného typu je $1$.
...
@@ -855,15 +962,14 @@ Střední hodnota počtu sousedních aut stejného typu je $1$.
::: {c=proof}
::: {c=proof}
Pro každý typ auta máme ${2n \choose 2} = {2n(2n-1) \over 2} = n (2n-1)$ možných pozic, kde se mohou
Pro každý typ auta máme ${2n \choose 2} = {2n(2n-1) \over 2} = n (2n-1)$ možných pozic, kde se mohou
nacházet a z toho $2n-1$ z nich jsou vedle sebe.
nacházet a z toho $2n-1$ z nich jsou vedle sebe.
Střední hodnota indikátoru souslednosti aut daného typu je tedy ${2n-1 \over n (2n-1)} = n$.
Střední hodnota indikátoru souslednosti aut daného typu je tedy ${2n-1 \over n (2n-1)} = {1\over n}$.
Z linearity střední hodnoty tedy počet sousedících aut stejného typu je $n/n = 1$.
Z linearity střední hodnoty tedy počet sousedících aut stejného typu je $n/n = 1$.
:::
:::
Ve 100 vstupech by tedy sousedních aut stejného typu mělo být ve střední hodnotě zhruba 100, což z části vysvětluje pozorovanou anomalitu.
Ve 100 vstupech by tedy sousedních aut stejného typu mělo být ve střední hodnotě zhruba 100, což z části vysvětluje pozorovanou anomálii.
Není však vyloučeno, že hodnoty skalárního součinu $-1$ nebo blízké program dosáhne i jinak.
Není však vyloučeno, že hodnoty skalárního součinu $-1$ nebo blízké program dosáhne i jinak.
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=50$.
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=50$.
:::
:::
Nevíme o žádné hypotéze, proč program generuje řešení s malou dimenzí ani není jasné, jestli či jak by toho šlo využit pro získání lepšího řešení či dolního odhadu.
Nevíme o žádné hypotéze, proč program generuje řešení s malou dimenzí.
Z naměřených dat však vychází, že průměrná dimenze je mnohem menší než $n$, ovšem nejspíše není omezena žádnou konstantou, tedy s rostoucím $n$ také roste, ale mnohem pomaleji.
Taktéž není jasné, jestli či jak by toho šlo využit pro získání lepšího řešení či dolního odhadu.
Hypotéza zní, že průměrná dimenze se pohybuje okolo $\log_2 n - 3$.
Z naměřených dat zobrazených na obrázcích [](#max_coords_400) až [](#nonzero_coords_50) na následujících stranách však vychází, že průměrná dimenze je mnohem menší než $n$, ovšem nejspíše není omezena žádnou konstantou, tedy s rostoucím $n$ také roste, ale mnohem pomaleji.
Hypotéza zní, že průměrná dimenze leží v $\Theta(\log n)$.
Malá dimenze má ale zásadní výhodu pro vizualizaci řešení, protože trojrozměrný prostor (tedy sféru dimenze 2) si zvládne člověk snadno představit. Z čehož plyne, že zhruba polovinu řešení pro $n=50$ jsme schopni
Malá dimenze má ale zásadní výhodu pro vizualizaci řešení, protože trojrozměrný prostor (tedy sféru dimenze 2) si zvládne člověk snadno představit. Z čehož plyne, že zhruba polovinu řešení pro $n=50$ jsme schopni
zakreslit jen s drobným zkreslením.
zakreslit jen s drobným zkreslením, tak, jak je ukázáno na obrázku [](#sdp-visualize_50).
Vizualizace jednoho z řešení pro $n=50$, které se vejde do 3D.
Vizualizace jednoho z řešení pro $n=50$, které se vejde do 3D.
:::
:::
Praktické řešení semidef. programů
\vfil\supereject
-----------------------------------------
Ve své práci jsem se pokusil implementovat jednotlivé algoritmy řešící Binární paint shop problém.
U triviálních algoritmů byla implementace poměrně přímočará.
Ovšem u řešení semidefinitního programování je většina složitosti algoritmu schovaná právě v řešení semidefinitních programů,
což už svojí složitostí nepatří mezi algoritmy, které bych chtěl (re)implementovat.
Proto je nutné se spolehnout na funkčnost již existujících implementací.
Bohužel kvalita dostupných řešičů semidefinitních programů je poměrně nízká.
### SDPA-C
Jedním z použitých programů byl SDPA-C [@sdpa-web].
Jedná se o knihovnu v C`++`
založenou na metodě vnitřních bodů v primárním a duálním problému. Dle autorů projektu [@sdpa-web]:
::: {.group lang=cs}
> SDPA (SemiDefinite Programming Algorithm) is one of the most efficient and stable software packages for solving SDPs based on the primal-dual interior-point method. It fully exploits the sparsity of given problems.
:::
Stáhl jsem si verzi SDPA-C (soubor `sdpa-c.7.3.8.src.20180125.tar.gz`) ze dne 2023-06-21.
Kompilace je poměrně komplikovaná.
Jednak vyžaduje velké množství nainstalovaného software (například překladač Fortranu), ale také poskytnutý `Makefile` není plně kompatibilní s moderními verzemi překladačů.
Bohužel i po úspěšné kompilaci stále není vyhráno.
Dodaný program přeložený aktuálním kompilátorem bohužel
nefunguje.
Při spuštění i na malých vstupech program rychle spadne s chybou segmentation fail
ve funkci `Newton::compute_bMatgVec_dense_threads_SDP`.
Při následujícím debugování jsem zjistil, že se program zacyklí v dané funkci ve smyčce, při které inkrementuje hodnotu proměnné
`Column_Number` až do doby, kdy přeteče, což už přímo vede k pádu programu.
Pohledem na kód to vypadá, že toto by se nemělo dít.
Program už mnohokrát měl ukončit smyčku a doběhnout na konec funkce, ovšem po přidání dostatečného počtu debugovacích výpisů je
je vidět, že program cyklus opustí, provede příkazy mezi cyklem a koncem funkce a pak se zase objeví uprostřed cyklu.
Problém byl v tom, že funkce vracející `void*`
dle normy [@cpp-norm] bodu 6.6.3
musí skončit pomocí `return` a nemůže jen tak opustit funkci dojitím na její konec.
Pokud se tak stane, jedná se o nedefinované chování,
nikoliv jen o nedefinovanou hodnotu navrácenou z funkce.
Kdyby se jednalo jen o nedefinovanou hodnotu,
tak se nic zásadního neděje. Funkce může vrátit cokoliv, ale to je nám jedno, protože výsledek se ignoruje.
Ovšem jelikož se jedná o nedefinované chování překladač může program
přeložit tak, že v takovémto případě udělá prakticky cokoliv.
A v tomto případě optimalizace překladače přeložili program tak,
že v případě opuštění funkce se vykonávání programu vrátí zpět dovnitř a pokračuje se v iterování smyčky.
To je důsledkem toho, jakým způsobem se snaží překladač optimalizovat kód.
Překladače nemusí dodržovat naprogramovanou strukturu programu.
Tedy není problém přesunout část za smyčkou do ní na místo, kde se smyčka opouští.
No a když překladač ví, že v korektním programu by se tudy nemělo opouštět funkci, nemusí se překladač obtěžovat umísťováním explicitního návratu z funkce na dané místo.
Naštěstí oprava tohoto problému je přímočará -- stačí do takovýchto funkcí explicitně dopsat `return NULL;`.
Toto bohužel není jediný problém s SDPA-C.
Na některých vstupech knihovna dojde do stavu, kdy nemá semidefinitní matici a výpočet spadne s následující chybou:
Tedy během výpočtu program něco spočte špatně a jako mezivýsledek mu vyjde matice, co není semidefinitní, se kterou již dále nejde pokračovat.
Příčinu této chyby se mi bohužel nepodařilo odhalit, protože na rozdíl od předchozího problému vůbec není jasné, kde začít hledat.
Detekce nesemidefinitnosti matice totiž může být v úplně jiné části kódu, než kde nastane chyba.
Problém se projevuje například na vstupu s $n=5$ a seedem $19$.
Další problém (nebo ten stejný) se projevuje zejména u větších vstupů.
Uprostřed algoritmu se také objeví chybová hláška o nesemidefinitnosti matice.
Následně program udělá mnoho iterací s nekonečnou účelovou funkcí dokud se nepřekročí maximální počet iterací a pak je vrácena matice
ze samých $\pm \infty$, což dokonce porušuje zadané podmínky.
Toto se stává například na seedu $18$ pro $n = 150$.
Vzhledem k výše uvedeným problémům jsem se rozhodl přestat využívat SDPA-C a podívat se po alternativách.
### Sage
SageMath je dle oficiálních stránek [@sagemath] open-source software pro matematické výpočty založený na rozšířené syntaxi pythonu,
takže je poměrně snadné ho využívat i jako programovací jazyk.
Sage mimo jiné obsahuje rozhraní pro řešení semidefinitních programů.
Rozhraní je navrženo tak, aby bylo schopné pracovat s více různými backendy pro řešení semidefinitních programů.
Bohužel v dokumentaci se mi nepodařilo pořádně najít, jaké backendy sage podporuje.
Při zavolání `default_sdp_solver("")` se objeví chybová hláška, která tvrdí, že backend by mělo jít nastavit na
`CVXOPT` (což je výchozí hodnota) nebo `Matrix`, případně uživatelem dodanou třídu.
Ovšem při přepnutí na `Matrix` spuštění řešení spadne na `NotImplementedError`, tedy zdá se, že ani tento backend není implementovaný (alespoň ve verzi, co mám k dispozici) a tedy jediná možnost je `CVXOPT`.
Drobným problémem je, že dle dokumentace [@sage-semidef-doc] rozhraní sage vyžaduje formulování vstupu jako primárního semidefinitního programu,
ovšem náš algoritmus je založený na řešení duálního semidefinitního programu.
Mezi nimi však lze snadno přecházet pomocí triviální úpravy, ovšem výsledný kód pak vypadá velice neintuitivně.
## Časová složitost
## Časová složitost
Celý algoritmus má polynomiální časovou složitost, protože
Víme, že existuje implementace algoritmu $\algo{sdp}$ s polynomiální časové složitosti (za použití elipsoidové metody).
vstupem semidefiního programu je polynomiálně mnoho čísel konstantní velikosti.
Použité řešiče však využívají jiných metod řešení semidefinitních programů.
Bohužel ani jeden z nich moc neslibuje, jakou přesně složitost má.
Řešení založená na semidefiním programování nemůže být moc rychlé.
Proto je na místě změřit, jak rychlé řešení jsou.
Již samotný vstup pro řešič semidefinitních programů má kubickou složitost vzhledem k délce vstupu Binárního paint shop problému.
Program totiž obsahuje $n$ podmínek na délku vektoru
a každá z nich se zapisuje pomocí matice $n \times n$.
::: {#tmp2 c=figure}
::: {#tmp c=figure}
```python {c=plotly}
```python {c=plotly}
from bakalarka import g, data_lib
from bakalarka import g, data_lib
...
@@ -1072,73 +1079,69 @@ data = g.load_main_test()
...
@@ -1072,73 +1079,69 @@ data = g.load_main_test()
d = data_lib.group_by_n(data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"])
d = data_lib.group_by_n(data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"])