Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
1 result

Target

Select target project
  • jirikalvoda/binary-paint-shop-problem
1 result
Select Git revision
  • master
1 result
Show changes
Commits on Source (4)
import panflute as pf
from formatitko.command_env import parse_string
from formatitko.elements import Slanted
from formatitko.elements import Slanted, FLineMarkup
formatitko_commands={}
def formatitko_command(f):
......@@ -45,6 +45,14 @@ def box(element, context, processor):
'lemma': "Lemma",
'def': "Definice",
}[element.attributes["t"]]
color = {
'fact': "red",
'task': "orange",
'algo': "brown",
'theorem': "green",
'lemma': "lightgreen",
'def': "blue",
}.get(element.attributes["t"], "black")
out = []
intro = []
if 'name' in element.attributes:
......@@ -56,12 +64,12 @@ def box(element, context, processor):
for e in content:
if not out:
if isinstance(e, pf.Para):
out.append(pf.Para(intro, Slanted(*e.content)))
out.append(pf.Para(intro, *e.content))
else:
raise NotImplemented()
else:
out += make_emph(e, wrap_class=Slanted)
return out
out.append(e)
return [FLineMarkup(*out, color=color)]
@formatitko_command
def proof(element, context, processor):
......
......@@ -46,7 +46,7 @@ def intro_graph(algo):
fig.update_layout(showlegend=False)
return fig
def nonzero_coords(n, seeds, max_dim=20):
def nonzero_coords(n, max_dim=20):
data_dir = d/"main_test/sdp/nonzero_coord"
nonzero_coords = [[float(i) for i in open(data_dir/f).read().split()] for f in os.listdir(data_dir) if f.startswith(f"{n}-")]
......@@ -68,7 +68,7 @@ def nonzero_coords(n, seeds, max_dim=20):
)
return fig
def max_coords(n, seeds, max_dim=20):
def max_coords(n, max_dim=20):
data_dir = d/"main_test/sdp/max_coord"
max_coords = [[float(i) for i in open(data_dir/f).read().split()] for f in os.listdir(data_dir) if f.startswith(f"{n}-")]
......
......@@ -85,7 +85,7 @@ Obsah {-}
V této práci představíme binární paint shop problem.
Jedná se o úlohu, kterou neumíme efektivně řešit (protože je $\NP$ úplná)
a ani aproximovat (za předpokladu Unique games conjecture je $c$-aproximace také $\NP$ těžká),
a ani aproximovat (za předpokladu Unique games conjecture je konstantní aproximace také $\NP$ těžká),
takže mimo jiné probíhá aktivní výzkum snažící se najít algoritmus,
který je dobrý v průměrném případě (pro náhodný vstup).
......@@ -202,17 +202,20 @@ Bohužel někdy asi ani s aproximačními algoritmy nevystačíme a proto zavede
pravděpodobnostní relaxaci.
::: {c=box t=def name="Pravděpodobnostní aproximační algoritmus"}
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í alespoň $p$ platí, že $f(\alg(I)) \le g(|I|) \cdot \opt(I)$.
Náhodný algoritmus $\alg$ je pravděpodobnostně $g(n)$-aproximační na minimalizačním problému,
pokud pro každý vstup $I$ algoritmus vždy vrátí přípustné řešení $\alg(I)$, pro které platí, že $\E[f(\alg(I))] \le g(|I|) \cdot \opt(I)$.
:::
Všimněme si, že na konstantě $p$ příliš nezáleží.
Snadným důsledkem definice je, že pro každé $\varepsilon > 1$ pravděpodobnostně $g(n)$-aproximační algoritmus
vrátí s pravděpodobností zdola omezenou konstantou $1- {1\over \varepsilon} \in (0,1)$ řešení s hodnotou nejvýše $\varepsilon \cdot g(n)\cdot \opt(I)$.
Všimněme si, že na hodnotě $1-{1\over \varepsilon}$ příliš nezáleží.
Opakovaným spouštěním algoritmu a pak vybráním nejlepšího
z dodaných řešení zvládneme $p$ libovolně těsně přiblížit k jedné.
z dodaných řešení zvládneme pravděpodobnost libovolně těsně přiblížit k jedné.
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.
Navíc chceme, aby všechna přípustná řešení měla omezenou délku nějakým polynomem v délce vstupu.
Navíc chceme, aby všechna přípustná řešení měla délku omezenou nějakým polynomem v délce vstupu.
Snadno nahlédneme, že za takovýchto podmínek je rozhodovací verze,
jestli je optimum alespoň zadané číslo, v $\NP$.
Pro takovou úlohu většinou hledáme polynomiální aproximační algoritmus.
......@@ -386,7 +389,7 @@ fig.add_vline(x=200*hypoth,
annotation_text="hypotéza", annotation_position="top left",
fillcolor="green")
```
Graf skóre $100$ běhů hvězdičkového rekurzivního řešení pro $n=200$.
Graf skóre $1\,000$ běhů hvězdičkového rekurzivního řešení pro $n=200$.
:::
Autoři algoritmu o něm vyslovili domněnku, že $\delta_{\algo{rsg}} = {1\over 13} \cdot \sqrt{61} - {3\over 13} \doteq 0.370$.
......@@ -438,7 +441,7 @@ V této kapitole si představíme princip semidefinitního programování (dále
z něhož vychází algoritmus na binary paint shop.
Nejprve zavedeme a připomeneme notaci důležitou v této kapitole.
Nechť $\R^{n\times m}$ značí množinu $n$ řádkových $m$ sloupcových matic
Nechť $\R^{n\times m}$ značí množinu $n$-řádkových $m$-sloupcových matic
složených z reálných čísel.
Řádky i sloupce indexujeme od $0$,
tedy matice $A\in \R^{n\times m}$ obsahuje prvky $A_{i,j}$ pro všechna $0\le i < n$ a $0 \le j < m$.
......@@ -529,7 +532,7 @@ programování a proto popsaného mimo jiné v již dříve zmíněném úvodu d
Naivní implementace je, že si pro každý vrchol $u$
vyrobíme proměnnou $x_u$, která může nabývat hodnot $\pm 1$, která bude říkat, do jaké množiny máme vrchol umístit.
Do optimalizační funkce pak zakódujeme graf.
Do účelové funkce pak zakódujeme graf.
Účelová funkce bude
$$ \sum_{uv \in E} h(u,v)\cdot\left(- \frac{x_u x_v}{2} + \frac{1}{2}\right) = \frac{F}{2} + \frac{1}{2} \sum_{uv \in E} -h(u,v)x_u x_v ,$$
kde $F=\sum_{uv\in E} h(u,v)$ značí součet hodnot všech hran.
......@@ -621,7 +624,7 @@ optimalizování $h(u,v)\cdot\left(\frac{1}{2} - \frac{\vec{y_u}^{\rm T}\vec{y_v
::: {#gw-func c=figure}
```asy {c=asy}
import graph;
size(380,180,IgnoreAspect);
size(380,175,IgnoreAspect);
real f(real x) {return 1/2-x/2;}
real g(real x) {return acos(x)/pi;}
draw(graph(f, -1, 1),red,"\vbox{\hbox{Účelová funkce}\vskip 2pt\hbox{$\frac{1 - \vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}}{2}$}}");
......@@ -645,7 +648,7 @@ $$
::: {#gw-frac c=figure}
```asy {c=asy}
import graph;
size(350,180,IgnoreAspect);
size(350,175,IgnoreAspect);
real f(real x) {return acos(x)/pi/(1/2-x/2);}
draw(graph(f, -1, 0.99),red,"$\frac{\frac{\arccos \vecoverrightarrow{y_u}^{\rm\,T} \vecoverrightarrow{y_v}}{\pi}}{\frac{1 - \vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}}{2}}$");
ylimits(0,2, Crop);
......@@ -663,9 +666,9 @@ $$\frac{\arccos x}{\pi} / \frac{1-x}{2} > 0.8785 $$
Označme tuto hodnotu $c = 0.8785$.
Tedy když je optimalizační funkce $h(u,v)r$, tak pravděpodobnost výběru hrany do řezu bude alespoň $cr$.
Tedy když je účelová funkce dané hrany $h(u,v)r$, tak pravděpodobnost výběru hrany do řezu bude alespoň $cr$.
Sečtením přes všechny hrany (z linearity středních hodnot) tedy získáme, že
střední hodnota součtu hran v řešení je $cR$, kde $R = \sum_{uv \in E} h(u,v)\cdot\left(\frac 12
střední hodnota součtu hran v řešení je alespoň $cR$, kde $R = \sum_{uv \in E} h(u,v)\cdot\left(\frac 12
- \frac{\vec{y_u}^{\rm T}\vec{y_v}}2\right)$, tedy jedno z ekvivalentních vyjádření účelové funkce.
Pokud tedy najdeme dobré řešení semidefinitního programu, ve střední hodnotě
......@@ -682,7 +685,7 @@ Přesněji řečeno, pro každé $\varepsilon > 0$ jsme schopní najít řešen
Ovšem musí platit, že všechna přípustná řešení jsou dostatečně malá.
Přesněji řečeno, musí platit, že Frobeniova 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 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}
Goemansův-Williamsonův algoritmus je pravděpodobnostní $0.878$-aproxi\-mační algoritmus.
......@@ -839,8 +842,9 @@ Měření řešení BPS
=================
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 s počtem typů aut
10, 20, 50, 100, 200, 400, 566, 800, 1131, 1600, 2263 a 3200 (jedna z implementací $\algo{sdp}$ -- pomocí sage vyžaduje moc paměti a proto byla spuštěna jen na vstupech do velikosti $566$).
Každý z nich byl následně spuštěn pro různé velikosti, pokaždé na $1\,000$ nezávisle náhodně vybraných vstupech s počtem typů aut
10, 20, 50, 100, 200, 400, 566, 800, 1131, 1600, 2263 a 3200.[^2]
Jedna z implementací $\algo{sdp}$ -- pomocí sage vyžaduje moc paměti a proto byla spuštěna jen na vstupech do velikosti $566$.
Celý test běžel jednovláknově zhruba dva dny a využíval nejvýše 16 GB operační paměti.
U $\algo{sdp}$ řešení bylo použito 10 náhodných řezů a vždy byl vybraný nejlepší z nich.
Gitový repozitář s implementací i naměřenými daty je k dispozici na <https://gitlab.kam.mff.cuni.cz/jirikalvoda/binary-paint-shop-problem>.
......@@ -850,6 +854,9 @@ Pro reprodukovatelnost byly vstupy generovány deterministicky ze seedů.
Se stejným seedem se tedy vždy vygeneruje stejný vstup a ideálně i stejné řešení.
Pro různé algoritmy a velikosti vstupů byly použité různé seedy.
[^2]: Hodnoty, co nejsou hezká čísla jsou zhruba $\sqrt{2}$-násobky předešlé hodnoty,
tedy v polovině mezi okolními hodnotami na logaritmické stupnici.
Praktické řešení semidef. programů
-----------------------------------------
......@@ -930,6 +937,7 @@ 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.
\vfil\eject
### 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,
......@@ -1035,7 +1043,7 @@ Vizualizace jednoho z řešení pro $n=50$, které se vejde do 3D.
::: {#max_coords_400 c=figure floatpage=400}
```python {c=plotly}
from bakalarka import g
fig = g.max_coords(400, range(1, 101))
fig = g.max_coords(400)
```
Maximální hodnota v dané dimenzi pro $n=400$.
:::
......@@ -1043,7 +1051,7 @@ Maximální hodnota v dané dimenzi pro $n=400$.
::: {#nonzero_coords_400 c=figure floatpage=400!}
```python {c=plotly}
from bakalarka import g
fig = g.nonzero_coords(400, range(1, 101))
fig = g.nonzero_coords(400)
```
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=400$.
:::
......@@ -1052,7 +1060,7 @@ Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=400$.
::: {#max_coords_200 c=figure floatpage=200}
```python {c=plotly}
from bakalarka import g
fig = g.max_coords(200, range(1, 101))
fig = g.max_coords(200)
```
Maximální hodnota v dané dimenzi pro $n=200$.
:::
......@@ -1060,7 +1068,7 @@ Maximální hodnota v dané dimenzi pro $n=200$.
::: {#nonzero_coords_200 c=figure floatpage=200!}
```python {c=plotly}
from bakalarka import g
fig = g.nonzero_coords(200, range(1, 101))
fig = g.nonzero_coords(200)
```
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=200$.
:::
......@@ -1068,7 +1076,7 @@ Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=200$.
::: {#max_coords_100 c=figure floatpage=100}
```python {c=plotly}
from bakalarka import g
fig = g.max_coords(100, range(1001, 1101))
fig = g.max_coords(100)
```
Maximální hodnota v dané dimenzi pro $n=100$.
:::
......@@ -1076,7 +1084,7 @@ Maximální hodnota v dané dimenzi pro $n=100$.
::: {#nonzero_coords_100 c=figure floatpage=100!}
```python {c=plotly}
from bakalarka import g
fig = g.nonzero_coords(100, range(1001, 1101))
fig = g.nonzero_coords(100)
```
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=100$.
:::
......@@ -1084,7 +1092,7 @@ Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=100$.
::: {#max_coords_50 c=figure floatpage=50}
```python {c=plotly}
from bakalarka import g
fig = g.max_coords(50, range(2001, 2101))
fig = g.max_coords(50)
```
Maximální hodnota v dané dimenzi pro $n=50$.
:::
......@@ -1092,7 +1100,7 @@ Maximální hodnota v dané dimenzi pro $n=50$.
::: {#nonzero_coords_50 c=figure floatpage=50!}
```python {c=plotly}
from bakalarka import g
fig = g.nonzero_coords(50, range(2001, 2101))
fig = g.nonzero_coords(50)
```
Počet vektorů s danou souřadnicí větší než $0.05$ pro $n=50$.
:::
......@@ -1171,26 +1179,32 @@ from bakalarka import g, data_lib
data = g.load_main_test()
shift_unit = 1.06
fig = go.Figure(data=[go.Box(
x=[i.n for i in d],
x=[i.n * shift for i in d],
y=[i.score/i.n for i in d],
name=name
) for d,name in [
[data.pipelines["semidef_prog(10)"], "sdp – SDPA-C"],
[data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"], "sdp – Sage"],
[data.pipelines["greedy"], "g"],
[data.pipelines["rg"], "rg"],
[data.pipelines["rsg"], "rsg"],
) for d,name, shift in [
[data.pipelines["greedy"], "g", 1/shift_unit],
[data.pipelines["rg"], "rg", shift_unit],
[data.pipelines["rsg"], "rsg", 1/shift_unit],
[data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"], "sdp – Sage", 1/shift_unit],
[data.pipelines["semidef_prog(10)"], "sdp – SDPA-C", shift_unit],
]])
fig.add_hline(y=0.34,
annotation_text="0.34", annotation_position="top left",
fillcolor="green")
fig.update_traces(width=0.05)
fig.update_layout(
xaxis_title="Počet typů aut (logaritmická stupnice)",
yaxis_title="Relativní skóre",
xaxis=dict(showgrid=False, type="log"),
yaxis=dict(range=[0, 1]),
xaxis_title_standoff = 0,
margin_b = 15,
legend=dict(orientation="h"),
)
```
......@@ -1218,19 +1232,19 @@ def gen_alg(pipeline_name, algo_name, name_suffix, floatpage, add_note=False):
pipeline = data.pipelines[pipeline_name]
by_n = data_lib.group_by_n(pipeline)
rows = []
percentils = [10,50,90]
percentils = [5, 25, 50,75, 90]
print_errors = any(i.error or i.data.get("broken", False) for i in pipeline)
for n in sorted(by_n.keys()):
d = by_n[n]
scores = [ i.score/n for i in d]
scores.sort()
l = len(scores)
assert l == 1000
avg = sum(scores) / l
variance = sum((avg-i)**2 for i in scores)/(l-1)
errors = len([None for i in d if i.error or i.data.get("broken", False)])
rows.append(row(
n,
l,
*([errors] if print_errors else []),
avg,
math.sqrt(variance),
......@@ -1239,7 +1253,6 @@ def gen_alg(pipeline_name, algo_name, name_suffix, floatpage, add_note=False):
table = pf.Table(pf.TableBody(*rows), head=pf.TableHead(row(
[pf.Math("n", format='InlineMath')],
"testů",
*(["chyb"] if print_errors else []),
[pf.Math("\\overline{\\delta_{\\algo{"+algo_name+"}}(n)}", format='InlineMath')],
[pf.Math("\\sqrt{\widehat{\\delta_{\\algo{"+algo_name+"}}(n)^2}}", format='InlineMath')],
......@@ -1305,7 +1318,7 @@ fig.update_layout(
)
```
Horní a dolní odhad na 100 náhodných instancí vygenerovaný sage $\algo{spd}$.
Horní a dolní odhad na $1\,000$ náhodných instancí vygenerovaný sage $\algo{spd}$.
:::
Takto vygenerovaný dolní odhad nám bohužel nepřináší žádnou zajímavou informaci o chování na náhodném vstupu.
......