From 6c3a5c29b40f1344db86fcbfd3a779a699cf3507 Mon Sep 17 00:00:00 2001
From: Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
Date: Sat, 4 May 2024 18:13:45 +0200
Subject: [PATCH] =?UTF-8?q?prace:=20Reforma=20obr=C3=A1zk=C5=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 prace/bakalarka/bakalarka.py           |  18 ++-
 prace/bakalarka/formatitko_commands.py |   4 +-
 prace/bakalarka/g.py                   |  11 +-
 prace/bakalarka/index.md               | 165 ++++++++++++++++---------
 prace/bakalarka/ksp.asy                |  56 ---------
 prace/bakalarka/sdp_visualize.py       |  54 +++++++-
 prace/pyproject.toml                   |   1 +
 7 files changed, 177 insertions(+), 132 deletions(-)
 delete mode 100644 prace/bakalarka/ksp.asy

diff --git a/prace/bakalarka/bakalarka.py b/prace/bakalarka/bakalarka.py
index bf93fbb66..9728dfe09 100644
--- a/prace/bakalarka/bakalarka.py
+++ b/prace/bakalarka/bakalarka.py
@@ -85,7 +85,12 @@ def main(build_dir=Path("build_pdf"), link_out=True):
         exec(code, globals)
         fig = globals["fig"]
 
-        fig.update_layout(margin = {'l':0,'r':0,'t':0,'b':0})
+        fig.update_layout(
+            margin = {'l':0,'r':0,'t':0,'b':0},
+            font=dict(
+                size=10,
+            ),
+        )
 
         file = get_name(element, context) + ".pdf"
 
@@ -101,7 +106,7 @@ def main(build_dir=Path("build_pdf"), link_out=True):
         code = element.text
         file = get_name(element, context) + ".pdf"
         r = sage.all.sage_eval("r", cmds=code)
-        r.save(build_dir/file, figsize=3)
+        r.save(str(build_dir/file), figsize=3)
         return img_from_source(element, context, processor, file)
 
     @formatitko_command
@@ -109,11 +114,13 @@ def main(build_dir=Path("build_pdf"), link_out=True):
         code = element.text
         file = get_name(element, context)
         with open(build_dir/(file+".asy"), "w") as f:
-            f.write('include "ksp.asy";\n')
+            f.write('include "jk_web.asy";\n')
             f.write(code)
         env = os.environ.copy()
-        env = os.environ.copy()
-        env["ASYMPTOTE_DIR"]=f".:{d}"
+        d_jk_web = Path("/".join(jk_web.__file__.split("/")[:-1]))
+        env["ASYMPTOTE_DIR"]=f".:{d_jk_web}"
+        d_formatitko = Path("/".join(formatitko.__file__.split("/")[:-1]))
+        env["TEXINPUTS"]=".:"+os.getcwd()+":"+str(d_formatitko/"tex")+":"+env.get("TEXINPUTS", "")
         subprocess.run(["asy", file+".asy", "-f", "pdf", "-tex", "pdftex"], check=True, cwd=build_dir, env=env)
 
         return img_from_source(element, context, processor, file+".pdf")
@@ -220,7 +227,6 @@ def main(build_dir=Path("build_pdf"), link_out=True):
     env = os.environ.copy()
     d_formatitko = Path("/".join(formatitko.__file__.split("/")[:-1]))
     env["TEXINPUTS"]=".:"+os.getcwd()+":"+str(d_formatitko/"tex")+":"+env.get("TEXINPUTS", "")
-    print(env["TEXINPUTS"])
     with open(build_dir/"toc.aux", "w") as f:
         pass
 
diff --git a/prace/bakalarka/formatitko_commands.py b/prace/bakalarka/formatitko_commands.py
index 54742264f..26cb10b86 100644
--- a/prace/bakalarka/formatitko_commands.py
+++ b/prace/bakalarka/formatitko_commands.py
@@ -84,8 +84,8 @@ def cmt(element, content, processor):
 def figure(element, content, processor):
     data = element.content[:-1]
     caption = element.content[-1].content
-    x = processor.transform([pf.Figure(*data, caption=pf.Caption(pf.Plain(*caption)), identifier=element.identifier, attributes=element.attributes)])
-    x[0].content = remove_div_para_to_plain(*x[0].content)
+    fig = pf.Figure(*data, caption=pf.Caption(pf.Plain(*caption)), identifier=element.identifier, attributes=element.attributes)
+    x = processor.transform([fig])
     return x
 
 def copy(e, *content):
diff --git a/prace/bakalarka/g.py b/prace/bakalarka/g.py
index e7909a105..236a59f0d 100644
--- a/prace/bakalarka/g.py
+++ b/prace/bakalarka/g.py
@@ -33,16 +33,17 @@ def draw_algo_graph(fig, data, algo, n, val_getter=lambda x:x.score, name=None,
     for i in d:
         y[val_getter(i)] += 1
     fig.add_trace(go.Histogram(x=[val_getter(i) for i in d], name=name or algo_to_name[algo], xbins=dict(size=1), marker=dict(color=color or algo_to_color[algo])))
-    fig.add_trace(go.Box(x=[val_getter(i) for i in d], name=name or algo_to_name[algo], showlegend=False, marker=dict(color=color or algo_to_color[algo]), boxmean=True), row=2, col=1)
+    fig.add_trace(go.Box(x=[val_getter(i) for i in d], name="", showlegend=False, marker=dict(color=color or algo_to_color[algo]), boxmean=True), row=2, col=1)
     fig.update_xaxes(range=[0, 2*n])
     # xaxis=np.arange(0, 2*n)
-    fig.update_layout(yaxis_title="Počet řešení")
-    fig.update_xaxes(title_text="Skóre", row=2, col=1)
+    fig.update_layout(yaxis_title="Počet řešení.")
+    fig.update_xaxes(title_text="Skóre.", row=2, col=1)
 
 
 def intro_graph(algo):
     fig = plotly.subplots.make_subplots(rows=2, cols=1, row_heights=[0.8, 0.2], shared_xaxes=True, vertical_spacing = 0.05)
     draw_algo_graph(fig, load_main_test(), algo, 200)
+    fig.update_layout(showlegend=False)
     return fig
 
 def nonzero_coords(n, seeds, max_dim=20):
@@ -59,6 +60,8 @@ def nonzero_coords(n, seeds, max_dim=20):
         )])
 
     fig.update_layout(
+        xaxis_title="Index souřadnice.",
+        yaxis_title="Počet bodů s hodnotou dané souřadnice ≥ 0.05.",
         xaxis=dict(showgrid=False),
         yaxis=dict(range=[0, n]),
         showlegend=False
@@ -79,6 +82,8 @@ def max_coords(n, seeds, max_dim=20):
         )])
 
     fig.update_layout(
+        xaxis_title="Index souřadnice.",
+        yaxis_title="Max. hodnota dané souřadnice všech bodů řešení.",
         xaxis=dict(showgrid=False),
         yaxis=dict(range=[0, 1]),
         showlegend=False
diff --git a/prace/bakalarka/index.md b/prace/bakalarka/index.md
index 3b1aae778..3c2b84534 100755
--- a/prace/bakalarka/index.md
+++ b/prace/bakalarka/index.md
@@ -44,19 +44,17 @@ ft:
             We proved that for each input this algorithm returns a solution with expected deviation from optimum of at most 0.212 times the number of car types.
 ---
 ``` {c=cmt}
-Obrázek koule
-
 Klikaci věci + generování obsahu formátítkem
 Algoritmy velké písmena
-Popisky obrázků
-Popisky os
 Obsah bez změn velikostí
 barevné schéma grafu
-font grafů
-Velikost poznámky pod čárou
 
-složitost je n^c pro jaké c?
 Absolutní číslování ve struktuře
+
+Vizualizace: více pohledů a správné pořadí hran.
+Jednotná TeXlib pro asymptote
+
+Velikost \vec
 ```
 
 ::: {only=html}
@@ -73,15 +71,8 @@ $\def\progline#1#2#3{\hbox{#1}\hskip 1cm\relax #2 \hskip 1cm\relax #3}
 \protected\def\P{{\rm P}}
 \protected\def\NP{{\rm NP}}
 \protected\def\APX{{\rm APX}}
-\protected\def\vec#1{\overrightarrow{#1\,}}
+\protected\def\vec#1{\vecoverrightarrow{#1}}
 \let\rightarrowfillreal\rightarrowfill
-\catcode`@=11
-\def\overrightarrow#1{\vbox{\m@th\ialign{##\crcr
-      \rightarrowfill\crcr\noalign{\kern-\p@\kern 0.09em\nointerlineskip}
-      $\hfil\displaystyle{#1}\hfil$\crcr}}}
-\def\rightarrowfill{$\settextsize{5}\m@th\smash-\mkern-7mu%
-  \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill
-  \settextsize{5}\mkern-7mu\mathord\rightarrow$}
 \protected\def\opt{{\rm opt}}
 \protected\def\algo#1{{\bf #1}}
 \protected\def\alg{\algo{alg}}
@@ -287,13 +278,15 @@ fig = go.Figure(data=[go.Scatter(
     mode = 'markers',
     )])
 fig.update_layout(
-    xaxis_title="n",
+    xaxis_title="Počet typů aut.",
+    yaxis_title="Relativní skóre.",
     xaxis=dict(range=[0,None]),
     yaxis=dict(range=[0, 2]),
-    showlegend=False
+    showlegend=False,
 )
 ```
-Graf střední hodnoty relativního skóre hladového řešení $\delta_{\algo g}(n)$ v závislosti na $n$.
+
+Graf střední hodnoty rel. 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á.
@@ -636,9 +629,17 @@ U každé hrany $uv$ optimalizujeme $-h(u,v) \vec{y_u}^{\rm T}\vec{y_v}$, což j
 optimalizování $h(u,v)\cdot\left(\frac{1}{2} - \frac{\vec{y_u}^{\rm T}\vec{y_v}}{2}\right)$.
 
 ::: {#gw-func c=figure}
-```python {c=sage}
-x = var('x')
-r = plot([1/2 - x/2, arccos(x)/pi], xmin=-1, xmax=1)
+```asy {c=asy}
+import graph;
+size(380,180,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{Pravděpodobnost řezu}\vskip 2pt\hbox{$\frac{1 - \vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}}{2}$}}");
+real g(real x) {return acos(x)/pi;}
+draw(graph(g, -1, 1),blue,"\vbox{\hbox{Účelová funkce}\vskip 2pt\hbox{$\frac{\arccos \vecoverrightarrow{y_u}^{\rm\,T} \vecoverrightarrow{y_v}}{\pi}$}}");
+xaxis("$\vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}$",BottomTop,LeftTicks);
+yaxis(LeftRight,RightTicks);
+add(legend(linelength=15pt),point(E),10E);
 ```
 Graf funkcí pravděpodobnosti řezu a účelové funkce.
 :::
@@ -652,9 +653,15 @@ $$
 $$
 
 ::: {#gw-frac c=figure}
-```python {c=sage}
-x = var('x')
-r = plot(arccos(x)/pi / (1/2 - x/2), xmin=-1, xmax=1, ymin=0, ymax=5)
+```asy {c=asy}
+import graph;
+size(350,180,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);
+xaxis("$\vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}$",BottomTop,LeftTicks, xmax=1);
+yaxis(LeftRight,RightTicks);
+add(legend(linelength=15pt),point(E),10E);
 ```
 Graf poměru pravděpodobnosti řezu a účelové funkce.
 :::
@@ -753,9 +760,15 @@ postupu by nás ani neměla zaskočit, protože v případě, že by šlo uděla
 Místo toho se podíváme na rozdíl pravděpodobnosti a účelové funkce.
 
 ::: {#sdp-diff c=figure}
-```python {c=sage}
-x = var('x')
-r = plot(arccos(x)/pi - (1/2 - x/2), xmin=-1, xmax=1)
+```asy {c=asy}
+import graph;
+size(400,180,IgnoreAspect);
+real f(real x) {return acos(x)/pi - (1/2-x/2);}
+draw(graph(f, -1, 1),red,"$\frac{\arccos \vecoverrightarrow{u}^{\rm\,T} \vecoverrightarrow{v}}{\pi} - \frac{1 - \vecoverrightarrow{y_u}^{\rm T}\vecoverrightarrow{y_v}}{2}$");
+ylimits(-0.2,0.2, Crop);
+xaxis("$\vecoverrightarrow{x_i}^{\rm T}\vecoverrightarrow{y_i}$",BottomTop,LeftTicks, xmax=1);
+yaxis(LeftRight,RightTicks);
+add(legend(linelength=15pt),point(E),10E);
 ```
 Graf rozdílu pravděpodobnosti řezu a účelové funkce.
 :::
@@ -771,9 +784,17 @@ Zderivováním získáme:
 $$f'(x) = - \frac{1}{\pi \sqrt{-x^2 +1}} + \frac12 ,$$
 
 ::: {#sdp-derivative c=figure}
-```python {c=sage}
-x = var('x')
-r = plot(derivative(arccos(x)/pi - (1/2 - x/2)), xmin=-1, xmax=1, ymin=-1, ymax=0.3)
+```asy {c=asy}
+import graph;
+size(350,180,IgnoreAspect);
+real f(real x) {return -1/(pi*sqrt(-x*x+1))+ 1/2;}
+real g(real x) {return 0;}
+draw(graph(f, -0.99, 0.99),red,"$f'(x)$");
+draw(graph(g, -1, 1));
+ylimits(-0.5,0.25, Crop);
+xaxis("$\vecoverrightarrow{x_i}^{\rm T}\vecoverrightarrow{y_i}$",BottomTop,LeftTicks, xmin=-1, xmax=1);
+yaxis(LeftRight,RightTicks);
+add(legend(linelength=15pt),point(E),10E);
 ```
 Derivace funkce $f$.
 :::
@@ -962,14 +983,15 @@ d = pathlib.Path("/".join(__file__.split("/")[:-1]))
 
 fig = plotly.subplots.make_subplots(rows=2, cols=1, row_heights=[0.8, 0.5], shared_xaxes=True, vertical_spacing = 0.05)
 scalar_products = sum(([float(i) for i in open(d/f"semidef_prog_scalar_products/n200_seed{i:04}").read().split()] for i in range(1,101)), start=[])
-fig.add_trace(go.Histogram(x=scalar_products, name="Hodnoty skalárních součinů", xbins=dict(size=0.01), marker=dict(color="blue")))
+fig.add_trace(go.Histogram(x=scalar_products, name="Naměřený počet výskytů", xbins=dict(size=0.01), marker=dict(color="blue")))
 x_vals = [i/500 for i in range(-500, 500+1)]
 fig.add_trace(go.Scatter(x=x_vals, y=[math.acos(x)/math.pi - (1/2 - x/2) for x in x_vals], mode='lines', name="Rozdíl pravděpodobnosti řezu a účelové funkce"), row=2, col=1)
 fig.update_xaxes(range=[-1, 1])
 fig.update_layout(
-    legend=dict(
-        orientation="h",
-        ),
+    xaxis2_title="Hodnota skalárního součinu vektorů sousedních aut.",
+    xaxis2_title_standoff = 0,
+    margin_b = 15,
+    legend=dict(orientation="h"),
 )
 ```
 Distribuce skalárních součinů $100$ běhů algoritmu pro $n=200$.
@@ -1003,6 +1025,27 @@ všechny vektory v něm mají několik prvních souřadnic velké hodnoty
 a ve zbylých souřadnic mají hodnoty blízké nule.
 Tedy kdybychom vektory promítli na méně dimenzionální sféru (vzali místo nich nejbližší bod na ní), tak se účelová funkce moc nezmění.
 
+
+Nevíme o žádné hypotéze, proč program generuje řešení s malou dimenzí.
+Taktéž není jasné, jestli či jak by toho šlo využit pro získání lepšího řešení či dolního odhadu.
+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
+zakreslit jen s drobným zkreslením, tak, jak je ukázáno na obrázku [](#sdp-visualize_50).
+
+::: {#sdp-visualize_50 c=figure}
+```python {.run}
+from bakalarka import sdp_visualize
+return processor.transform([pf.CodeBlock(sdp_visualize.visualize("semidef_prog_n50_seed2"), attributes=dict(c="asy"))])
+```
+
+Světlá barva bodu značí bod na zadní straně sféry.
+Černé čáry spojují sousední auta a šedé jsou jim středově simetrické (protože dvojice aut, jejichž druhá auta stejného typu jsou vedle sebe, je také přitahována k sobě).
+
+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
@@ -1068,23 +1111,6 @@ fig = g.nonzero_coords(50, range(2001, 2101))
 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í.
-Taktéž není jasné, jestli či jak by toho šlo využit pro získání lepšího řešení či dolního odhadu.
-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
-zakreslit jen s drobným zkreslením, tak, jak je ukázáno na obrázku [](#sdp-visualize_50).
-
-::: {#sdp-visualize_50 c=figure}
-```python {.run}
-from bakalarka import sdp_visualize
-return processor.transform([pf.CodeBlock(sdp_visualize.visualize("semidef_prog_n50_seed2"), attributes=dict(c="asy"))])
-```
-
-Vizualizace jednoho z řešení pro $n=50$, které se vejde do 3D.
-:::
-
 \vfil\supereject
 
 ## Časová složitost algoritmu $\algo{sdp}$
@@ -1100,21 +1126,36 @@ Během výpočtu na stoji neběželo nic kromě výpočtu a základních funkcí
 
 ::: {#tmp2 c=figure}
 ```python {c=plotly}
+from math import log, exp
 from bakalarka import g, data_lib
+from sklearn.linear_model import LinearRegression
+
+def gen(d, name, minim, color, color_line):
+    pred_x = list(range(minim, max(i.n for i in d)+1))
+    regr = LinearRegression()
+    regr_res = regr.fit([[log(i.n)] for i in d if i.resources_cpu_time_s and i.n>minim], [log(i.resources_cpu_time_s) for i in d if i.resources_cpu_time_s and i.n>minim])
+    predict = [exp(i) for i in regr.predict([[log(i)] for i in pred_x])]
+    return [
+        go.Box(
+            x=[i.n for i in d],
+            y=[i.resources_cpu_time_s for i in d],
+            name=name,
+            marker_color=color,
+        ),
+        go.Scatter(x=pred_x, y=predict, mode="lines", marker_color=color_line, name=f"n^{regr.coef_[0]:.3} * {exp(regr.intercept_):.1g}".replace("-0", "-")),
+    ]
+
 
 data = g.load_main_test()
-d = data_lib.group_by_n(data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"])
 
-fig = go.Figure(data=[go.Box(
-    x=[i.n for i in d],
-    y=[i.resources_cpu_time_s for i in d],
-    name=name
-    ) for d,name in [
-        [data.pipelines["semidef_prog(10)"], "SDPA-C"],
-        [data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"], "Sage"],
-    ]])
+fig = go.Figure(data=[
+    *gen(data.pipelines["semidef_prog(10)"], "SDPA-C", 400, "blue", "lightblue"),
+    *gen(data.pipelines["semidef_prog_sage.sage(10, CVXOPT)"], "Sage", 100, "red", "pink"),
+    ])
 
 fig.update_layout(
+    xaxis_title="Počet typů aut (logaritmická stupnice)",
+    yaxis_title="Čas běhu v sekundách (logaritmická stupnice)",
     xaxis=dict(showgrid=False, type="log"),
     yaxis=dict(type="log"),
 )
@@ -1158,6 +1199,8 @@ fig.add_hline(y=0.34,
               annotation_text="0.34", annotation_position="top left",
               fillcolor="green")
 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]),
 )
@@ -1213,6 +1256,8 @@ fig.add_hline(y=0.214,
               annotation_text="0.214", annotation_position="top left",
               fillcolor="green")
 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]),
 )
diff --git a/prace/bakalarka/ksp.asy b/prace/bakalarka/ksp.asy
deleted file mode 100644
index 16f634b22..000000000
--- a/prace/bakalarka/ksp.asy
+++ /dev/null
@@ -1,56 +0,0 @@
-import settings;
-outformat="pdf";
-
-settings.texcommand = "luatex";
-texpreamble("
-\input luatex85.sty
-\input ucwmac2.tex
-\input ltluatex.tex
-\ucwmodule{luaofs}
-\ucwmodule{verb}
-\ucwmodule{link}
-\clickablefalse
-
-% Fonty
-\ofsdeclarefamily [Pagella] {%
-   \loadtextfam qplr;%
-                qplb;%
-                qpli;%
-                qplbi;;%
-}
-
-\def\MSfeat#1{:mode=node;script=latn;+tlig}
-\registertfm qplr      -      file:texgyrepagella-regular.otf\MSfeat{}
-\registertfm qplb      -      file:texgyrepagella-bold.otf\MSfeat{}
-\registertfm qpli      -      file:texgyrepagella-italic.otf\MSfeat{}
-\registertfm qplbi     -      file:texgyrepagella-bolditalic.otf\MSfeat{}
-
-\settextsize{12}
-
-\uselanguage{czech}\frenchspacing\lefthyphenmin=2\righthyphenmin=2{}
-
-\input glyphtounicode.tex
-\pdfgentounicode=1
-");
-
-defaultpen(fontcommand("\relax"));
-
-pen ksp_pen[] = {
-	linewidth(0.15pt),
-	linewidth(0.4pt),
-	linewidth(0.8pt),
-	linewidth(1.2pt),
-};
-
-pen ksp_thin_pen = ksp_pen[0];
-pen ksp_normal_pen = ksp_pen[1];
-pen ksp_thick_pen = ksp_pen[2];
-pen ksp_fat_pen = ksp_pen[3];
-
-defaultpen(ksp_normal_pen);
-
-pen ksp_short_dashed = linetype(new real[] { 1, 4 });
-pen ksp_mid_dashed = linetype(new real[] { 4, 4 });
-pen ksp_fine_dotted = ksp_thin_pen + 0.3mm + linetype(new real[] { 0, 2.5 });
-pen ksp_gray = gray(0.6);
-pen ksp_area_gray = gray(0.75);
diff --git a/prace/bakalarka/sdp_visualize.py b/prace/bakalarka/sdp_visualize.py
index 39024c1dc..500eed45e 100644
--- a/prace/bakalarka/sdp_visualize.py
+++ b/prace/bakalarka/sdp_visualize.py
@@ -1,20 +1,44 @@
 import pathlib
-import numpy as np
 d = pathlib.Path("/".join(__file__.split("/")[:-1]))
 
+from scipy.spatial.transform import Rotation
+import numpy as np
+from numpy.linalg import norm
+
+
+
+
 def visualize(filename):
     def car_place(i, sgn=1):
         coord = [x*sign[i]*sgn for x in cholesky[inp[i]]]
         return place(coord)
     def place(coord):
-        coord = np.array(coord)
+        coord = np.array(coord[:3])
+        coord = coord / norm(coord)
+        return rot.apply(coord)
+    def format_place(coord):
         return f"({coord[0]}, {coord[1]})"
+
+    def f_car_place(*args, **kwargs):
+        return format_place(car_place(*args, **kwargs))
+
+    def is_front_car(i):
+        return car_place(i)[2] > 0
+
+
+    def car_color(i):
+        return "blue" if sum(a*b*sign[i] for a,b in zip(cholesky[inp[i]], cut)) > 0 else "red"
+
     with open(d/filename) as f:
         data = f.read().split("\n")
         inp = list(map(int, data[0].split()))
         n = len(inp)//2
         cholesky = [list(map(float, i.split())) for i in data[1:n+1]]
         cut = list(map(float, data[n+2].split())) 
+    rot = Rotation.align_vectors([(1,0,0)], [cut])[0]
+    print(rot)
+    print(cut)
+    print(rot.apply(cut))
 
     for i in cholesky:
         assert sum(x**2 for x in i) > 0.99, f"Dimension higher then input ({sum(x**2 for x in i)})"
@@ -25,8 +49,28 @@ def visualize(filename):
         type_sum[inp[i]] += i
     sign = [-1 if type_sum[inp[i]] > 2*i else 1 for i in range(2*n)]
     out.append("unitsize(80);")
+    out.append("""
+    path edge_path(pair a, pair b)
+{
+    path pa = a--b;
+	real x = intersections(pa,circle(a, 0.017))[0][0];
+	real y = intersections(pa,circle(b, 0.017)).pop()[0];
+	return subpath(pa,x,y);
+}
+   """)
+
+    for i in range(2*n):
+        if not is_front_car(i):
+            out.append(f"filldraw(circle({f_car_place(i)},0.015), light{car_color(i)});")
+    for i in range(2*n-1):
+        out.append(f"draw(edge_path({f_car_place(i, -1)}, {f_car_place(i+1, -1)}), gray(0.8));")
     for i in range(2*n-1):
-        out.append(f"draw({car_place(i)} -- {car_place(i+1)});")
-        out.append(f"draw({car_place(i, -1)} -- {car_place(i+1, -1)}, green);")
-    out.append(f"filldraw(circle({place(cut)},0.01));")
+        out.append(f"draw(edge_path({f_car_place(i)}, {f_car_place(i+1)}));")
+    out.append(f"draw(circle((0,0),1));")
+    out.append(f"draw((0,-1.1)-- (0, 1.1));")
+    for i in range(2*n):
+        if is_front_car(i):
+            out.append(f"filldraw(circle({f_car_place(i)},0.015), {car_color(i)});")
+    # out.append(f"filldraw(circle({format_place(place(cut))},0.01));")
+
     return "\n".join(out)
diff --git a/prace/pyproject.toml b/prace/pyproject.toml
index 68a311b16..9d882d4da 100644
--- a/prace/pyproject.toml
+++ b/prace/pyproject.toml
@@ -22,6 +22,7 @@ dependencies = [
   "kaleido",
   "mathjax",
   "scipy",
+  "scikit-learn",
 ]
 
 [project.urls]
-- 
GitLab