Просмотр исходного кода

Merge branch 'convGAN' of fyrr/LoGAN into master

fyrr 4 лет назад
Родитель
Сommit
d5d0278184

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 .ipynb_checkpoints
 __pycache__
 *.swp
+temp

Разница между файлами не показана из-за своего большого размера
+ 32 - 4
SpeedTest.ipynb


Разница между файлами не показана из-за своего большого размера
+ 128 - 94
SpeedTestNNSearch.ipynb


BIN
data_input/kaggle_creditcard.csv.gz


BIN
documentation/NNSearch_idea.pdf


+ 1147 - 0
documentation/NNSearch_idea.svg

@@ -0,0 +1,1147 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="195mm"
+   height="220mm"
+   viewBox="0 0 195 220"
+   version="1.1"
+   id="svg5"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   sodipodi:docname="NNSearch_idea.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview7"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     inkscape:zoom="1.8116734"
+     inkscape:cx="254.73686"
+     inkscape:cy="599.72177"
+     inkscape:window-width="1920"
+     inkscape:window-height="1018"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g458579"
+     width="195mm"
+     height="220mm" />
+  <defs
+     id="defs2">
+    <marker
+       style="overflow:visible"
+       id="Arrow2Mend"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Mend"
+       inkscape:isstock="true">
+      <path
+         transform="scale(-0.6)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:0.625;stroke-linejoin:round"
+         id="path23958" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="Arrow1Mend"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path23940" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="Arrow2Mend-3"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Mend"
+       inkscape:isstock="true">
+      <path
+         transform="scale(-0.6)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:0.625;stroke-linejoin:round"
+         id="path23958-6" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker24392"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow2Mend"
+       inkscape:isstock="true">
+      <path
+         transform="scale(-0.6)"
+         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+         style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:0.625;stroke-linejoin:round"
+         id="path24390" />
+    </marker>
+  </defs>
+  <g
+     inkscape:label="Ebene 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g459190"
+       transform="translate(0,-0.10398126)">
+      <g
+         id="g458884"
+         transform="translate(0,-2.720077)">
+        <rect
+           style="fill:#f9f9f9;stroke:none;stroke-width:0.176389;stroke-linecap:round"
+           id="rect273306"
+           width="185"
+           height="50"
+           x="5"
+           y="60.324059"
+           rx="4.99999"
+           ry="4.99999" />
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:4.23333px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="10.150718"
+           y="99.844391"
+           id="text145084"><tspan
+             sodipodi:role="line"
+             style="stroke-width:0.264583px"
+             x="10.150718"
+             y="99.844391"
+             id="tspan179526"><tspan
+   style="fill:#ff0000"
+   id="tspan268889">d = ‖p - q‖</tspan> ≥ <tspan
+   style="fill:#ffcc00"
+   id="tspan270277">‖p - q&quot;'‖ = ‖ ( d</tspan><tspan
+   style="font-size:65%;baseline-shift:sub;fill:#ffcc00"
+   id="tspan145484">1</tspan><tspan
+   style="fill:#ffcc00"
+   id="tspan270765"> , d</tspan><tspan
+   style="font-size:65%;baseline-shift:sub;fill:#ffcc00"
+   id="tspan153346">2</tspan><tspan
+   style="fill:#ffcc00"
+   id="tspan270767"> ) ‖ = δ (p, q) </tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.44118"
+           y="67.583862"
+           id="text50477"><tspan
+             sodipodi:role="line"
+             style="stroke-width:0.264583px"
+             x="146.44118"
+             y="67.583862"
+             id="tspan55125">x' := x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan73980">s</tspan> + &lt;x - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan75654">s </tspan>, v&gt; v </tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.2639"
+           y="77.529205"
+           id="text111148"><tspan
+             sodipodi:role="line"
+             id="tspan111146"
+             style="fill:#44aa00;stroke-width:0.264583px"
+             x="146.2639"
+             y="77.529205">k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan126974">x</tspan> := ‖x' - x<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan111502">s</tspan>‖</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.2639"
+           y="72.565132"
+           id="text115110"><tspan
+             sodipodi:role="line"
+             id="tspan115108"
+             style="fill:#808080;stroke-width:0.264583px"
+             x="146.2639"
+             y="72.565132">h<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#808080"
+   id="tspan115104">x</tspan> := x' - x</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.34328"
+           y="82.601753"
+           id="text134110"><tspan
+             sodipodi:role="line"
+             id="tspan134108"
+             style="fill:#44aa00;stroke-width:0.264583px"
+             x="146.34328"
+             y="82.601753">d<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan136512">1</tspan> := |k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan135366">p</tspan> - k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan135364">q</tspan>| = ‖p' - q'‖</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.34328"
+           y="87.978584"
+           id="text140366"><tspan
+             sodipodi:role="line"
+             id="tspan140364"
+             style="letter-spacing:-0.00730218px;fill:#44aa00;stroke-width:0.264583px"
+             x="146.34328"
+             y="87.978584">d<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan140358">2</tspan> := | ‖h<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan140360">p</tspan>‖ - ‖h<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00"
+   id="tspan140362">q</tspan>‖ |</tspan></text>
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#ffd42a;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 38.404516,70.325379 65.102413,80.38255"
+           id="path239973"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 38.404516,70.325379 65.125783,92.06575"
+           id="path31780"
+           sodipodi:nodetypes="cc" />
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="37.558197"
+           y="67.729576"
+           id="text24638"><tspan
+             sodipodi:role="line"
+             id="tspan24636"
+             style="stroke-width:0.264583px"
+             x="37.558197"
+             y="67.729576">p</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="116.64616"
+           y="86.965141"
+           id="text24644"><tspan
+             sodipodi:role="line"
+             id="tspan24642"
+             style="stroke-width:0.264583px"
+             x="116.64616"
+             y="86.965141">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan220047">s</tspan> = x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan220045">s</tspan>'</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="64.129318"
+           y="95.293549"
+           id="text24664"><tspan
+             sodipodi:role="line"
+             id="tspan24662"
+             style="stroke-width:0.264583px"
+             x="64.129318"
+             y="95.293549">q</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="7.752389"
+           y="86.042145"
+           id="text24678"><tspan
+             sodipodi:role="line"
+             id="tspan24676"
+             style="stroke-width:0.264583px"
+             x="7.752389"
+             y="86.042145">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan24674">e</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="44.990501"
+           y="80.058746"
+           id="text34061"><tspan
+             sodipodi:role="line"
+             id="tspan34059"
+             style="fill:#ff0000;stroke-width:0.264583px"
+             x="44.990501"
+             y="80.058746">d</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="53.346146"
+           y="68.25972"
+           id="text37519"><tspan
+             sodipodi:role="line"
+             id="tspan37517"
+             style="fill:#44aa00;stroke:none;stroke-width:0.264583px"
+             x="53.346146"
+             y="68.25972">d<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00;stroke:none"
+   id="tspan42471">1</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="67.904221"
+           y="75.44104"
+           id="text38905"><tspan
+             sodipodi:role="line"
+             id="tspan38903"
+             style="fill:#44aa00;stroke:none;stroke-width:0.264583px"
+             x="67.904221"
+             y="75.44104">d<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00;stroke:none"
+   id="tspan41319">2</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="84.838432"
+           y="81.691422"
+           id="text66530"><tspan
+             sodipodi:role="line"
+             id="tspan66528"
+             style="fill:#44aa00;stroke:none;stroke-width:0.264583px"
+             x="84.838432"
+             y="81.691422">k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00;stroke:none"
+   id="tspan66526">p</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#44aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="85.314674"
+           y="91.029129"
+           id="text67848"><tspan
+             sodipodi:role="line"
+             id="tspan67846"
+             style="fill:#44aa00;stroke:none;stroke-width:0.264583px"
+             x="85.314674"
+             y="91.029129">k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#44aa00;stroke:none"
+   id="tspan67844">q</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="37.20937"
+           y="89.072098"
+           id="text207721"><tspan
+             sodipodi:role="line"
+             id="tspan207719"
+             style="stroke-width:0.264583px"
+             x="37.20937"
+             y="89.072098">p'</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="62.230095"
+           y="88.541138"
+           id="text209149"><tspan
+             sodipodi:role="line"
+             id="tspan209147"
+             style="stroke-width:0.264583px"
+             x="62.230095"
+             y="88.541138">q'</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="116.3935"
+           y="70.772057"
+           id="text211913"><tspan
+             sodipodi:role="line"
+             id="tspan211911"
+             style="fill:#0000ff;stroke-width:0.264583px"
+             x="116.3935"
+             y="70.772057">x<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#0000ff"
+   id="tspan217251">s</tspan>&quot; = x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan226318">s</tspan> + h<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan226316">p</tspan></tspan></text>
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 11.945115,85.55773 H 112.97312"
+           id="path225232" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 11.945115,70.357389 H 112.97312"
+           id="path225347" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#cccccc;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 38.404516,85.49214 V 70.325379"
+           id="path227445" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 65.125783,85.33972 V 70.325379"
+           id="path227560"
+           sodipodi:nodetypes="cc" />
+        <path
+           style="fill:#00ff00;fill-rule:evenodd;stroke:#44aa00;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+           d="M 112.97312,87.14524 H 65.802242"
+           id="path228033" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#cccccc;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="M 65.125783,92.06575 V 85.33972"
+           id="path228151"
+           sodipodi:nodetypes="cc" />
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="66.68692"
+           y="91.588608"
+           id="text228274"><tspan
+             sodipodi:role="line"
+             id="tspan228272"
+             style="fill:#808080;stroke-width:0.264583px"
+             x="66.68692"
+             y="91.588608">h<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan228270">q</tspan></tspan></text>
+        <path
+           style="fill:#00ff00;fill-rule:evenodd;stroke:#44aa00;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+           d="M 112.97312,83.44104 H 38.623106"
+           id="path228276"
+           sodipodi:nodetypes="cc" />
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="64.454575"
+           y="67.705566"
+           id="text228497"><tspan
+             sodipodi:role="line"
+             id="tspan228495"
+             style="letter-spacing:0.0147526px;fill:#0000ff;stroke-width:0.264583px"
+             x="64.454575"
+             y="67.705566">q&quot; = x<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#0000ff"
+   id="tspan236807">s</tspan>&quot; + k<tspan
+   style="font-size:65%;baseline-shift:sub;fill:#0000ff"
+   id="tspan236805">q</tspan> ∙ v</tspan></text>
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#44aa00;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+           d="M 38.404516,69.267039 H 65.125781"
+           id="path237116" />
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#44aa00;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+           d="M 66.528221,70.026019 V 79.91624"
+           id="path237512"
+           sodipodi:nodetypes="cc" />
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="66.991364"
+           y="81.524216"
+           id="text239063"><tspan
+             sodipodi:role="line"
+             id="tspan239061"
+             style="fill:#ffcc00;stroke-width:0.264583px"
+             x="66.991364"
+             y="81.524216">q&quot;'</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="54.274467"
+           y="75.811852"
+           id="text240095"><tspan
+             sodipodi:role="line"
+             id="tspan240093"
+             style="fill:#ffcc00;stroke-width:0.264583px"
+             x="54.274467"
+             y="75.811852">d&quot;'</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="34.481274"
+           y="78.815315"
+           id="text242320"><tspan
+             sodipodi:role="line"
+             id="tspan242318"
+             style="fill:#808080;stroke-width:0.264583px"
+             x="34.481274"
+             y="78.815315">h<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan242316">p</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:4.23333px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="10.150718"
+           y="104.01147"
+           id="text253205"><tspan
+             sodipodi:role="line"
+             style="stroke-width:0.264583px"
+             x="10.150718"
+             y="104.01147"
+             id="tspan265120">q&quot;' is in the layer through p, created by the vectors v and h<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan254055">p</tspan> , on the line q'q&quot; with the distance ‖h<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan262944">q</tspan>‖ from the line x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan262948">s</tspan>x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan262946">e</tspan>.</tspan></text>
+      </g>
+      <g
+         id="g458936">
+        <rect
+           style="fill:#f9f9f9;stroke:none;stroke-width:0.176389;stroke-linecap:round"
+           id="rect273188"
+           width="185"
+           height="50"
+           x="5"
+           y="5.2079625"
+           rx="4.99999"
+           ry="4.99999" />
+        <g
+           id="g224928"
+           transform="translate(-6.0027343,-5.3651201)">
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="69.447754"
+             y="22.780071"
+             id="text1485"><tspan
+               sodipodi:role="line"
+               id="tspan1483"
+               style="stroke-width:0.264583px"
+               x="69.447754"
+               y="22.780071">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan18535">0</tspan></tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="127.36237"
+             y="55.605019"
+             id="text3997"><tspan
+               sodipodi:role="line"
+               id="tspan3995"
+               style="stroke-width:0.264583px"
+               x="127.36237"
+               y="55.605019">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan16159">s</tspan></tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="98.235268"
+             y="52.902023"
+             id="text4947"><tspan
+               sodipodi:role="line"
+               id="tspan4945"
+               style="stroke-width:0.264583px"
+               x="98.235268"
+               y="52.902023">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="112.82182"
+             y="39.900944"
+             id="text6095"><tspan
+               sodipodi:role="line"
+               id="tspan6093"
+               style="stroke-width:0.264583px"
+               x="112.82182"
+               y="39.900944">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="57.782681"
+             y="53.962341"
+             id="text6913"><tspan
+               sodipodi:role="line"
+               id="tspan6911"
+               style="stroke-width:0.264583px"
+               x="57.782681"
+               y="53.962341">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="59.995449"
+             y="40.140671"
+             id="text7733"><tspan
+               sodipodi:role="line"
+               id="tspan7731"
+               style="stroke-width:0.264583px"
+               x="59.995449"
+               y="40.140671">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="79.267723"
+             y="20.936205"
+             id="text8473"><tspan
+               sodipodi:role="line"
+               id="tspan8471"
+               style="stroke-width:0.264583px"
+               x="79.267723"
+               y="20.936205">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="33.283581"
+             y="39.534161"
+             id="text9335"><tspan
+               sodipodi:role="line"
+               id="tspan9333"
+               style="stroke-width:0.264583px"
+               x="33.283581"
+               y="39.534161">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="35.96928"
+             y="15.485229"
+             id="text10189"><tspan
+               sodipodi:role="line"
+               id="tspan10187"
+               style="stroke-width:0.264583px"
+               x="35.96928"
+               y="15.485229">x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="20.546003"
+             y="19.678825"
+             id="text11073"><tspan
+               sodipodi:role="line"
+               id="tspan11071"
+               style="stroke-width:0.264583px"
+               x="20.546003"
+               y="19.678825">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan21567">e</tspan></tspan></text>
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+             d="M 72.187466,23.414848 125.2253,53.477741"
+             id="path19554"
+             inkscape:connector-type="polyline"
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="cc" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+             d="M 125.2253,53.477741 24.258638,19.92485"
+             id="path19797"
+             sodipodi:nodetypes="cc" />
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="68.253281"
+             y="33.644226"
+             id="text48187"><tspan
+               sodipodi:role="line"
+               id="tspan48185"
+               style="stroke-width:0.264583px"
+               x="68.253281"
+               y="33.644226">u</tspan></text>
+        </g>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.27977"
+           y="20.617506"
+           id="text57455"><tspan
+             sodipodi:role="line"
+             id="tspan57453"
+             style="stroke-width:0.264583px"
+             x="146.27977"
+             y="20.617506">u := x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan68582">e</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan70146">s</tspan></tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.4888"
+           y="25.005051"
+           id="text59115"><tspan
+             sodipodi:role="line"
+             id="tspan59113"
+             style="stroke-width:0.264583px"
+             x="146.4888"
+             y="25.005051">v := u / ‖u‖</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.14749"
+           y="16.694304"
+           id="text80644"><tspan
+             sodipodi:role="line"
+             id="tspan80642"
+             style="stroke-width:0.264583px"
+             x="146.14749"
+             y="16.694304">‖x‖ := sqrt(  &lt;x , x&gt; )</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="146.35651"
+           y="12.218122"
+           id="text86154"><tspan
+             sodipodi:role="line"
+             id="tspan86152"
+             style="stroke-width:0.264583px"
+             x="146.35651"
+             y="12.218122">&lt;x , y&gt; := x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89594">1</tspan> y<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89596">1</tspan> + x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89598">2</tspan> y<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89600">2</tspan> + x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89602">3</tspan> y<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan89604">3</tspan> + ...</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           x="145.74232"
+           y="37.807999"
+           id="text286516"><tspan
+             sodipodi:role="line"
+             id="tspan286514"
+             style="stroke-width:0.264583px"
+             x="145.74232"
+             y="37.807999">data = {x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan286786">0</tspan>, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan286784">1</tspan>, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan286782">2</tspan>, ...}</tspan></text>
+      </g>
+      <g
+         id="g458787">
+        <rect
+           style="fill:#f9f9f9;stroke:none;stroke-width:0.176389;stroke-linecap:round"
+           id="rect336509"
+           width="185"
+           height="105"
+           x="5"
+           y="110"
+           rx="4.99999"
+           ry="4.99999" />
+        <g
+           id="g458579"
+           transform="translate(-3.7874628,-8.6123902)">
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="154.71429"
+             id="text289180"><tspan
+               sodipodi:role="line"
+               id="tspan289178"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="154.71429">for i in {0, 1, 2, 3, ...}:</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="161.32887"
+               id="tspan289182" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="18.035566"
+             y="168.37891"
+             id="text289180-1"><tspan
+               sodipodi:role="line"
+               id="tspan289178-1"
+               style="stroke-width:0.264583px"
+               x="18.035566"
+               y="168.37891">for k in {0,1,2, ..., n}:</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="18.035566"
+               y="174.99348"
+               id="tspan289182-5" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="22.054419"
+             y="172.76709"
+             id="text289180-1-4"><tspan
+               sodipodi:role="line"
+               id="tspan289178-1-8"
+               style="letter-spacing:-0.0258171px;stroke-width:0.264583px"
+               x="22.054419"
+               y="172.76709">if |N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan408803">i</tspan>| &lt; nebSize:</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="22.054419"
+               y="179.64548"
+               id="tspan289182-5-1" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="18.342405"
+             y="159.10248"
+             id="text291518"><tspan
+               sodipodi:role="line"
+               id="tspan291516"
+               style="stroke-width:0.264583px"
+               x="18.342405"
+               y="159.10248">Sort { π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan460111">0</tspan>, π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan460109">1</tspan>, ... π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan460107">n</tspan> } = { 0, 1,2, ..., n } so that Δ(i, π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan460113">j</tspan> ) ≤  Δ(i, π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan460115">j+1</tspan>) for all j in {0, 1, ... , n-1}</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="18.104017"
+             y="163.87959"
+             id="text305581"><tspan
+               sodipodi:role="line"
+               id="tspan305579"
+               style="stroke-width:0.264583px"
+               x="18.104017"
+               y="163.87959">N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan411745">i</tspan> ← ∅</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="28.944956"
+             y="195.11205"
+             id="text305581-9"><tspan
+               sodipodi:role="line"
+               id="tspan305579-8"
+               style="stroke-width:0.264583px"
+               x="28.944956"
+               y="195.11205">N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan428417">i</tspan> ← (N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan428415">i</tspan> / {a}) ∪ {π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan469603">k</tspan>}</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="25.098562"
+             y="177.36166"
+             id="text305581-9-4"><tspan
+               sodipodi:role="line"
+               id="tspan305579-8-4"
+               style="stroke-width:0.264583px"
+               x="25.098562"
+               y="177.36166">N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan414177">i</tspan> ← N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan414179">i</tspan> ∪ {π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan463559">k</tspan>}</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="22.054419"
+             y="181.84773"
+             id="text314906"><tspan
+               sodipodi:role="line"
+               id="tspan314902"
+               style="letter-spacing:-0.0258171px;stroke-width:0.264583px"
+               x="22.054419"
+               y="181.84773">else:</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="22.054419"
+               y="188.46231"
+               id="tspan314904" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="25.25202"
+             y="185.85492"
+             id="text315492"><tspan
+               sodipodi:role="line"
+               id="tspan315490"
+               style="stroke-width:0.264583px"
+               x="25.25202"
+               y="185.85492">a ← a in N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan416893">i</tspan> with Δ(i, a) ≥ Δ(i, b) for all b in N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan416895">i</tspan></tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="25.25202"
+             y="190.35423"
+             id="text322619"><tspan
+               sodipodi:role="line"
+               id="tspan322617"
+               style="stroke-width:0.264583px"
+               x="25.25202"
+               y="190.35423"
+               dy="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1">if ‖x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan421609">a</tspan> - x‖ &gt; ‖x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan424307"
+   dy="0 1">πk</tspan> - x‖:</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="28.944956"
+             y="204.12785"
+             id="text326428"><tspan
+               sodipodi:role="line"
+               id="tspan326426"
+               style="stroke-width:0.264583px"
+               x="28.944956"
+               y="204.12785">break loop for k; next x</tspan></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="25.25202"
+             y="199.61136"
+             id="text326434"><tspan
+               sodipodi:role="line"
+               id="tspan326432"
+               style="stroke-width:0.264583px"
+               x="25.25202"
+               y="199.61136">if ‖x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan430379">a</tspan> - x‖ &lt; Δ(π<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan473861">k</tspan> , i):</tspan></text>
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 18.419165,168.928 v 41.10144 h 3.534653"
+             id="path331964" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 22.601484,182.80787 v 24.98717 h 3.534653"
+             id="path332046"
+             sodipodi:nodetypes="ccc" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 22.601484,173.25519 v 5.31599 h 3.534653"
+             id="path332147"
+             sodipodi:nodetypes="ccc" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 25.366253,190.95272 v 5.31599 h 3.534653"
+             id="path332248"
+             sodipodi:nodetypes="ccc" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 25.366253,200.21987 v 5.31599 h 3.534653"
+             id="path332250"
+             sodipodi:nodetypes="ccc" />
+          <path
+             style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             d="m 14.25568,155.8174 v 57.11602 h 3.534653"
+             id="path332252"
+             sodipodi:nodetypes="ccc" />
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="217.08237"
+             id="text334685"><tspan
+               sodipodi:role="line"
+               id="tspan334681"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="217.08237">return (N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan335599">0</tspan>, N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan335597">1</tspan>, N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan335595">2</tspan>, ..., N<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan457721">n</tspan> )</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="223.96075"
+               id="tspan334683" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="126.62011"
+             id="text337912"><tspan
+               sodipodi:role="line"
+               id="tspan337908"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="126.62011">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan342466">s</tspan> ← x in data so that d(x, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan342468">0</tspan>) ≥ d(y, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan342470">0</tspan>) for all y in data</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="133.49849"
+               id="tspan337910" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="131.22527"
+             id="text342562"><tspan
+               sodipodi:role="line"
+               id="tspan342558"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="131.22527">x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan345168">e</tspan> ← x in data so that d(x, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan342554">s</tspan>) ≥ d(y, x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan342556">s</tspan>) for all y in data</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="138.10365"
+               id="tspan342560" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="135.81717"
+             id="text349910"><tspan
+               sodipodi:role="line"
+               id="tspan349906"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="135.81717">v ← (x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan350672">e</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan350670">s</tspan>) / d(x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan350668">e</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan350666">s</tspan>)</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="142.69556"
+               id="tspan349908" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="140.32047"
+             id="text354793"><tspan
+               sodipodi:role="line"
+               id="tspan354789"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="140.32047">H ← ( ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan354781">0 </tspan>- x‖, ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368131">1</tspan> - x‖, ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368133">2</tspan> - x‖, ..., ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368135">n</tspan> - x‖ )</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="147.19885"
+               id="tspan354791" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="144.91502"
+             id="text368309"><tspan
+               sodipodi:role="line"
+               id="tspan368305"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="144.91502">K ← ( ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368297">0 </tspan>- x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan370869">0</tspan>‖, ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368299">1</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan370867">1</tspan>‖, ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368301">2</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan370865">2</tspan>‖, ..., ‖x'<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan368303">n</tspan> - x<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan370863">n</tspan>‖ )</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="151.79341"
+               id="tspan368307" /></text>
+          <text
+             xml:space="preserve"
+             style="font-style:normal;font-weight:normal;font-size:2.64583px;line-height:6.61458px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+             x="14.083701"
+             y="149.80223"
+             id="text371257"><tspan
+               sodipodi:role="line"
+               id="tspan371253"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="149.80223">Δ(i, j) = sqrt( (K<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan372735">i</tspan> - K<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan372733">j</tspan>)<tspan
+   style="font-size:65%;baseline-shift:super"
+   id="tspan372727">2</tspan> + (H<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan372725">i</tspan> - H<tspan
+   style="font-size:65%;baseline-shift:sub"
+   id="tspan372723">j</tspan>)<tspan
+   style="font-size:65%;baseline-shift:super"
+   id="tspan372721">2</tspan> )</tspan><tspan
+               sodipodi:role="line"
+               style="stroke-width:0.264583px"
+               x="14.083701"
+               y="156.68062"
+               id="tspan371255" /></text>
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>

+ 166 - 0
library/MaxHeap.py

@@ -0,0 +1,166 @@
+
+class MaxHeap:
+    def __init__(self, maxSize=None, isGreaterThan=None, smalestValue=(-1,0.0)):
+        self.heap = []
+        self.size = 0
+        self.maxSize = maxSize
+        self.isGreaterThan = isGreaterThan if isGreaterThan is not None else (lambda a, b: a > b)
+        self.smalestValue = smalestValue
+        self.indices = set()
+        self.wasChanged = False
+        self.insert(smalestValue)
+
+    def copy(self):
+        c = MaxHeap(maxSize=self.maxSize, isGreaterThan=self.isGreaterThan, smalestValue=self.smalestValue)
+        c.heap = self.heap.copy()
+        c.size = self.size
+        c.indices = self.indices.copy()
+        c.wasChanged = self.wasChanged
+        return c
+
+    def insert(self, v):
+        if self.maxSize is not None and self.size >= self.maxSize:
+            return self.replaceMax(v)
+
+        if v[0] in self.indices:
+            return False
+
+        self.indices.add(v[0])
+        pos = self.size
+        self.size += 1
+        self.heap.append(v)
+        while pos > 0:
+            w = self.heap[pos // 2]
+            if not self.isGreaterThan(v, w):
+                break
+            self.heap[pos] = w
+            pos = pos // 2
+            self.heap[pos] = v
+        self.wasChanged = True
+        return True
+
+
+    def childPos(self, pos):
+        c = (pos + 1) * 2
+        return (c - 1, c)
+
+
+    def removeMax(self):
+        if self.heap == []:
+            self.size = 0
+            return False
+        
+        if self.size <= 1:
+            self.size = 0
+            self.heap = []
+            return True
+        
+        x = self.heap[0]
+        self.indices.remove(x[0])
+
+        self.heap[0] = self.heap[-1]
+        self.heap = self.heap[:-1]
+        self.size -= 1
+
+        x = self.heap[0]
+        pos = 0
+        size = self.size
+
+        while pos < size:
+            (left, right) = self.childPos(pos)
+
+            if left >= size:
+                break
+
+            y = self.heap[left]
+            if right >= size:
+                if self.isGreaterThan(y, x):
+                    self.heap[pos] = y
+                    self.heap[left] = x
+                break
+
+            z = self.heap[right]
+            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
+
+            if not self.isGreaterThan(v, x):
+                break
+
+            self.heap[pos] = v
+            self.heap[best] = x
+            pos = best
+
+        self.wasChanged = True
+        return True
+
+
+    def replaceMax(self, x):
+        if self.heap == []:
+            self.heap = [x]
+            self.size = 1
+            self.indices.add(x[0])
+            self.wasChanged = True
+            return True
+        
+        if x[0] in self.indices:
+            return False
+
+        if self.isGreaterThan(x, self.heap[0]):
+            return False
+
+        self.indices.remove((self.heap[0])[0])
+        self.indices.add(x[0])
+        self.heap[0] = x
+        pos = 0
+        size = len(self.heap)
+
+        while pos < size:
+            (left, right) = self.childPos(pos)
+
+            if left >= size:
+                break
+
+            y = self.heap[left]
+            if right >= size:
+                if self.isGreaterThan(y, x):
+                    self.heap[pos] = y
+                    self.heap[left] = x
+                break
+
+            z = self.heap[right]
+            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
+
+            if not self.isGreaterThan(v, x):
+                break
+
+            self.heap[pos] = v
+            self.heap[best] = x
+            pos = best
+
+        self.wasChanged = True
+        return True
+
+    def getMax(self):
+        if self.heap == []:
+            return self.smalestValue
+        return self.heap[0]
+
+
+    def setMaxSize(self, maxSize):
+        self.maxSize = maxSize
+        while self.size > maxSize:
+            self.removeMax()
+
+    def toArray(self, mapFn=None):
+        return list(self.indices)
+
+    def toOrderedArray(self):
+        c = self.copy()
+        result = []
+        while c.size > 0:
+            result.append(c.getMax())
+            c.removeMax()
+        result.reverse()
+        return result
+
+    def length(self):
+        return self.size

+ 160 - 0
library/NNSearch/MaxHeap.py

@@ -0,0 +1,160 @@
+
+class MaxHeap:
+    def __init__(self, maxSize=None, isGreaterThan=None, smalestValue=(-1,0.0)):
+        self.heap = []
+        self.size = 0
+        self.maxSize = maxSize
+        self.isGreaterThan = isGreaterThan if isGreaterThan is not None else (lambda a, b: a > b)
+        self.smalestValue = smalestValue
+        self.indices = set()
+        self.wasChanged = False
+        self.insert(smalestValue)
+
+    def copy(self):
+        c = MaxHeap(maxSize=self.maxSize, isGreaterThan=self.isGreaterThan, smalestValue=self.smalestValue)
+        c.heap = self.heap.copy()
+        c.size = self.size
+        c.indices = self.indices.copy()
+        c.wasChanged = self.wasChanged
+        return c
+
+    def insert(self, v):
+        if self.maxSize is not None and self.size >= self.maxSize:
+            return self.replaceMax(v)
+
+        if v[0] in self.indices:
+            return False
+
+        self.indices.add(v[0])
+        pos = self.size
+        self.size += 1
+        self.heap.append(v)
+        while pos > 0:
+            w = self.heap[pos // 2]
+            if not self.isGreaterThan(v, w):
+                break
+            self.heap[pos] = w
+            pos = pos // 2
+            self.heap[pos] = v
+        self.wasChanged = True
+        return True
+
+
+    def childPos(self, pos):
+        c = (pos + 1) * 2
+        return (c - 1, c)
+
+
+    def removeMax(self):
+        if self.heap == []:
+            self.size = 0
+            return False
+        
+        x = self.heap[0]
+        self.indices.remove(x[0])
+
+        self.heap[0] = self.heap[-1]
+        self.heap = self.heap[:-1]
+        self.size -= 1
+
+        x = self.heap[0]
+        pos = 0
+        size = self.size
+
+        while pos < size:
+            (left, right) = self.childPos(pos)
+
+            if left >= size:
+                break
+
+            y = self.heap[left]
+            if right >= size:
+                if self.isGreaterThan(y, x):
+                    self.heap[pos] = y
+                    self.heap[left] = x
+                break
+
+            z = self.heap[right]
+            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
+
+            if not self.isGreaterThan(v, x):
+                break
+
+            self.heap[pos] = v
+            self.heap[best] = x
+            pos = best
+
+        self.wasChanged = True
+        return True
+
+
+    def replaceMax(self, x):
+        if self.heap == []:
+            self.heap = [x]
+            self.size = 1
+            self.indices.add(x[0])
+            self.wasChanged = True
+            return True
+        
+        if x[0] in self.indices:
+            return False
+
+        if self.isGreaterThan(x, self.heap[0]):
+            return False
+
+        self.indices.remove((self.heap[0])[0])
+        self.indices.add(x[0])
+        self.heap[0] = x
+        pos = 0
+        size = len(self.heap)
+
+        while pos < size:
+            (left, right) = self.childPos(pos)
+
+            if left >= size:
+                break
+
+            y = self.heap[left]
+            if right >= size:
+                if self.isGreaterThan(y, x):
+                    self.heap[pos] = y
+                    self.heap[left] = x
+                break
+
+            z = self.heap[right]
+            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
+
+            if not self.isGreaterThan(v, x):
+                break
+
+            self.heap[pos] = v
+            self.heap[best] = x
+            pos = best
+
+        self.wasChanged = True
+        return True
+
+    def getMax(self):
+        if self.heap == []:
+            return self.smalestValue
+        return self.heap[0]
+
+
+    def setMaxSize(self, maxSize):
+        self.maxSize = maxSize
+        while self.size > maxSize:
+            self.removeMax()
+
+    def toArray(self, mapFn=None):
+        return list(self.indices)
+
+    def toOrderedArray(self):
+        c = self.copy()
+        result = []
+        while c.size > 0:
+            result.append(c.getMax())
+            c.removeMax()
+        return result.reverse()
+
+    def length(self):
+        return self.size

+ 182 - 334
library/NNSearch_experimental.py

@@ -1,13 +1,38 @@
 import math
+from ctypes import cdll, c_uint, c_double, c_void_p
 
 import tensorflow as tf
+import tensorflow.keras.layers as L
 import numpy as np
+import numpy.ctypeslib as npct
+
+array_2d_uint = npct.ndpointer(dtype=np.uint, ndim=2, flags='CONTIGUOUS')
+array_1d_double = npct.ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS')
+array_2d_double = npct.ndpointer(dtype=np.double, ndim=2, flags='CONTIGUOUS')
+
+
 from sklearn.neighbors import NearestNeighbors
 from library.timing import timing
+from library.MaxHeap import MaxHeap
+
+
+nbhLib = cdll.LoadLibrary("./library/c/libNeighborhood.so")
+nbhLib.Neighborhood.rettype = None
+nbhLib.Neighborhood.argtypes = [c_uint, c_uint, c_uint, array_2d_double, array_2d_uint]
+
+#nbhLib.NeighborhoodHeuristic.rettype = None
+#nbhLib.NeighborhoodHeuristic.argtypes = [c_uint, c_uint, c_uint, array_2d_double, array_2d_uint]
+
+
+
+def scalarP(a, b):
+    return sum(map(lambda c: c[0] * c[1], zip(a, b)))
 
+def norm2(v):
+    return sum(map(lambda z: z*z, v))
 
 def dist(x,y):
-    return sum(map(lambda z: (z[0] - z[1])*(z[0] - z[1]), zip(x, y)))
+    return norm2(x - y)
 
 def maxby(data, fn, startValue=0.0):
     m = startValue
@@ -17,8 +42,8 @@ def maxby(data, fn, startValue=0.0):
 
 def distancesToPoint(p, points):
     w = np.array(np.repeat([p], len(points), axis=0))
-    d = tf.keras.layers.Subtract()([w, np.array(points)])
-    t = tf.keras.layers.Dot(axes=(1,1))([d,d])
+    d = L.Subtract()([w, np.array(points)])
+    t = L.Dot(axes=(1,1))([d,d])
     # As the concrete distance is not needed and sqrt(x) is strict monotone
     # we avoid here unneccessary calculating of expensive roots.
     return t.numpy()
@@ -27,288 +52,44 @@ def distancesToPoint(p, points):
 def calculateCenter(points):
     if points.shape[0] == 1:
         return points[0]
-    return tf.keras.layers.Average()(list(points)).numpy()
-
-
-class MaxHeap:
-    def __init__(self, maxSize=None, isGreaterThan=None, smalestValue=(-1,0.0)):
-        self.heap = []
-        self.size = 0
-        self.maxSize = maxSize
-        self.isGreaterThan = isGreaterThan if isGreaterThan is not None else (lambda a, b: a > b)
-        self.smalestValue = smalestValue
-        self.indices = set()
-        self.wasChanged = False
-        self.insert(smalestValue)
-
-    def insert(self, v):
-        if self.maxSize is not None and self.size >= self.maxSize:
-            return self.replaceMax(v)
-
-        if v[0] in self.indices:
-            return False
-
-        self.indices.add(v[0])
-        pos = self.size
-        self.size += 1
-        self.heap.append(v)
-        while pos > 0:
-            w = self.heap[pos // 2]
-            if not self.isGreaterThan(v, w):
-                break
-            self.heap[pos] = w
-            pos = pos // 2
-            self.heap[pos] = v
-        self.wasChanged = True
-        return True
-
-
-    def childPos(self, pos):
-        c = (pos + 1) * 2
-        return (c - 1, c)
-
-
-    def removeMax(self):
-        if self.heap == []:
-            self.size = 0
-            return False
-        
-        x = self.heap[0]
-        self.indices.remove(x[0])
-
-        self.heap[0] = self.heap[-1]
-        self.heap = self.heap[:-1]
-        self.size -= 1
-
-        x = self.heap[0]
-        pos = 0
-        size = self.size
-
-        while pos < size:
-            (left, right) = self.childPos(pos)
-
-            if left >= size:
-                break
-
-            y = self.heap[left]
-            if right >= size:
-                if self.isGreaterThan(y, x):
-                    self.heap[pos] = y
-                    self.heap[left] = x
-                break
-
-            z = self.heap[right]
-            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
-
-            if not self.isGreaterThan(v, x):
-                break
-
-            self.heap[pos] = v
-            self.heap[best] = x
-            pos = best
-
-        self.wasChanged = True
-        return True
-
-
-    def replaceMax(self, x):
-        if self.heap == []:
-            self.heap = [x]
-            self.size = 1
-            self.indices.add(x[0])
-            self.wasChanged = True
-            return True
-        
-        if x[0] in self.indices:
-            return False
-
-        if self.isGreaterThan(x, self.heap[0]):
-            return False
-
-        self.indices.remove((self.heap[0])[0])
-        self.indices.add(x[0])
-        self.heap[0] = x
-        pos = 0
-        size = len(self.heap)
-
-        while pos < size:
-            (left, right) = self.childPos(pos)
-
-            if left >= size:
-                break
-
-            y = self.heap[left]
-            if right >= size:
-                if self.isGreaterThan(y, x):
-                    self.heap[pos] = y
-                    self.heap[left] = x
-                break
-
-            z = self.heap[right]
-            (best, v) = (left, y) if self.isGreaterThan(y, z) else (right, z)
-
-            if not self.isGreaterThan(v, x):
-                break
-
-            self.heap[pos] = v
-            self.heap[best] = x
-            pos = best
-
-        self.wasChanged = True
-        return True
-
-    def getMax(self):
-        if self.heap == []:
-            return self.smalestValue
-        return self.heap[0]
-
-
-    def setMaxSize(self, maxSize):
-        self.maxSize = maxSize
-        while self.size > maxSize:
-            self.removeMax()
+    return tf.keras.layers.Average()(np.array(points)).numpy()
 
-    def toArray(self, mapFn=None):
-            return list(self.indices)
 
+def centerPoints(points):
+    points = np.array(points)
+    center = L.Average()(list(points)).numpy()
+    ctr = np.array(np.repeat([center], points.shape[0], axis=0))
+    return L.Subtract()([ctr, points]).numpy()
+    
 
-    def length(self):
-        return self.size
+def maxNormPoints(points):
+    points = np.array(points)
+    a = L.Lambda(lambda x: np.abs(x))(points)
+    a = L.Reshape((points.shape[1], 1))(a)
+    m = L.GlobalMaxPooling1D()(a)
+    m = L.Reshape((1,))
+    return m.numpy()
 
 
+def twoNormSquaredPoints(points):
+    points = np.array(points)
+    return L.Dot(axes=(1,1))([points,points]).numpy()
+    
 
+def twoNormPoints(points):
+    points = np.array(points)
+    nsq = L.Dot(axes=(1,1))([points,points])
+    return L.Lambda(lambda x: np.sqrt(x))(points).numpy()
 
-class Ball:
-    def __init__(self, points=None, indices=None, parent=None, center=None):
-        if center is not None:
-            self.center = center
-        elif points is not None:
-            self.center = calculateCenter(np.array(points))
-        else:
-            raise ParameterError("Missing points or center")
-        self.radius = 0
-        self.points = []
-        self.indices = set()
-        self.childs = []
-        self.parent = parent
-
-        if points is not None and indices is not None:
-            for (i, r) in zip(indices, distancesToPoint(self.center, points)):
-                self.add(i, r)
-        elif points is not None:
-            raise ParameterError("Missing indices")
-
-    def findIt(self, r):
-        if r == self.points[0][1]:
-            return 0
-
-        upper = len(self.points) - 1
-        lower = 0
-        while upper > lower + 1:
-            h = (upper + lower) // 2
-            if self.points[h][1] >= r:
-                upper = h
-            else:
-                lower = h
-        return upper
-
-
-    def add(self, xi, r):
-        if xi in self.indices:
-            return False
-
-        # Here we know, that x is not in points.
-        newEntry = (xi, r)
-        self.indices.add(xi)
-
-        # Special case: empty list or new element will extend the radius:
-        # Here we can avoid the search
-        if self.points == [] or r >= self.radius:
-            self.points.append(newEntry)
-            self.radius = r
-            return True
-
-        # Special case: r <= min radius
-        # Here we can avoid the search
-        if self.points[0][1] >= r:
-            self.points = [newEntry] + self.points
-            return True
-
-        # Here we know that min radius < r < max radius.
-        # So len(points) >= 2.
-
-        pos = self.findIt(r)
-
-        # here shoul be: r(pos) >= r > r(pos - 1)
-        self.points = self.points[:pos] + [newEntry] + self.points[pos: ]
-
-        return True
-
-    def remove(self, xi, r):
-        if xi not in self.indices:
-            return False
-
-
-        # special case: remove the element with the heighest radius:
-        if self.points[-1][0] == xi:
-            self.indices.remove(xi)
-            self.points = self.points[: -1]
-            if self.points == []:
-                self.radius = 0.0
-            else:
-                self.radius = self.points[-1][1]
-            return True
-
-        pos = self.findIt(r)
-        nPoints = len(self.points)
-        # pos is the smalest position with:
-        # r(pos - 1) < r <= r(pos)
-
-        # Just in case: check that we remove the correct index.
-        while pos < nPoints and r == self.points[pos]:
-            if self.points[pos][0] == xi:
-                self.indices.remove(xi)
-                self.points = self.points[:pos] + self.points[(pos + 1): ]
-                return True
-            pos += 1
-
-        return False
-
-    def divideBall(self, X):
-        indices = [i for (i, _r) in self.points]
-        points = np.array([X[i] for i in indices])
-        
-        distances = tf.keras.layers.Dot(axes=(1,1))([points, points]).numpy()
-        ball = Ball(points, indices, center=np.zeros(len(points[0])))
-
-        h = len(ball) // 2
-        indicesA = [i for (i, _) in ball.points[:h]] 
-        indicesB = [i for (i, _) in ball.points[h:]] 
-        pointsA = np.array([X[i] for i in indicesA])
-        pointsB = np.array([X[i] for i in indicesB])
-
-        self.childs = []
-
-        print(f"{len(points)} -> <{len(pointsA)}|{len(pointsB)}>")
-        self.childs.append(Ball(pointsA, indicesA, self))
-        self.childs.append(Ball(pointsB, indicesB, self))
-
-        return self.childs
-
-    def __len__(self):
-        return len(self.points)
-
-    def smalestBallFor(self, i):
-        if i not in self.indices:
-            return None
-
-        for c in self.childs:
-            b = c.smalestBallFor(i)
-            if b is not None:
-                return b
-
-        return self
 
+def norms(points):
+    points = np.array(points)
+    a = L.Lambda(lambda x: np.abs(x))(points)
+    a = L.Reshape((points.shape[1], 1))(a)
+    m = L.GlobalMaxPooling1D()(a)
+    m = L.Reshape((1,))(m)
+    nsq = L.Dot(axes=(1,1))([points,points])
+    return L.Concatenate()([m, nsq]).numpy()
 
 
 class NNSearch:
@@ -446,75 +227,142 @@ class NNSearch:
         self.timerStop("NN_fit_chained_toList")
 
 
+    def fit_cLib(self, X, nebSize=None):
+        self.timerStart("NN_fit_cLib_init")
+        if nebSize == None:
+            nebSize = self.nebSize
+        
+        nbh = np.array([np.zeros(nebSize, dtype=np.uint) for i in range(X.shape[0])])
+        self.timerStop("NN_fit_cLib_init")
+        self.timerStart("NN_fit_cLib_call")
+        nbhLib.Neighborhood(nebSize, X.shape[0], X.shape[1], X, nbh)
+        self.timerStop("NN_fit_cLib_call")
+        self.timerStart("NN_fit_cLib_list")
+        self.neighbourhoods = list(nbh)
+        self.timerStop("NN_fit_cLib_list")
+
+    # def fit_cLibHeuristic(self, X, nebSize=None):
+    #     self.timerStart("NN_fit_cLib_init")
+    #     if nebSize == None:
+    #         nebSize = self.nebSize
+    #     
+    #     nbh = np.array([np.zeros(nebSize, dtype=np.uint) for i in range(X.shape[0])])
+    #     self.timerStop("NN_fit_cLib_init")
+    #     self.timerStart("NN_fit_cLib_call")
+    #     nbhLib.NeighborhoodHeuristic(nebSize, X.shape[0], X.shape[1], X, nbh)
+    #     self.timerStop("NN_fit_cLib_call")
+    #     self.timerStart("NN_fit_cLib_list")
+    #     self.neighbourhoods = list(nbh)
+    #     self.timerStop("NN_fit_cLib_list")
+        
+
     # ===============================================================
     # Heuristic search
     # ===============================================================
-    def fit_heuristic(self, X, nebSize=None):
+    def fit_heuristic(self, X, nebSize=None, debugLayer=0, withDouble=True):
         if nebSize == None:
             nebSize = self.nebSize
 
+        self.timerStart("NN_fit_heuristic_init")
         nPoints = len(X)
-
-
-        def walkUp(nbh, ball, x, i):
-            while ball.parent is not None:
-                print(f"{i}: up (r: {nbh.getMax()})")
-                oldBall = ball
-                ball = ball.parent
-                for c in ball.childs:
-                    if c != oldBall:
-                        walkDown(nbh, c, x)
-
-        def walkDown(nbh, ball, x):
-            if ball is None:
-                return
-
-            print(f"{i}: down (r: {nbh.getMax()})")
-
-            if dist(x, ball.center) - ball.radius < nbh.getMax()[1]:
-                if ball.childs == []:
-                    for (j, _) in ball.points:
-                        nbh.insert((j, dist(x, X[j])))
-                else:
-                    for c in ball.childs:
-                        walkDown(nbh, c, x)
-
-
-        def countBoles(b):
-            if b is None:
-                return 0
-
-            return 1 + sum(map(countBoles, b.childs))
-
-
-
-        root = Ball(X, range(len(X)))
-        queue = [root]
-        while queue != []:
-            ball = queue[0]
-            queue = queue[1:]
-            if len(ball) <= nebSize:
-                continue
-
-            queue = ball.divideBall(X) + queue
-
-
+        nFeatures = len(X[0])
+        nHeuristic = max(1, int(math.log(nFeatures)))
         isGreaterThan = lambda x, y: x[1] > y[1]
-        self.neighbourhoods = [MaxHeap(nPoints, isGreaterThan, (i, 0.0)) for i in range(len(X))]
+        self.neighbourhoods = [MaxHeap(maxSize=nebSize, isGreaterThan=isGreaterThan, smalestValue=(i, 0.0)) for i in range(len(X))]
 
-        print("#B: " + str(countBoles(root)))
 
-        exit()
+        self.timerStart("NN_fit_heuristic_lineStart")
         z = X[0]
+        farest = 0
+        bestDist = 0.0
         for (i, x) in enumerate(X):
-            nbh = self.neighbourhoods[i]
+            d = dist(x, z)
+            if d > bestDist:
+                farest = i
+                bestDist = d
 
-            b = root.smalestBallFor(i)
-            if b.parent is not None:
-                b = b.parent
+        lineStart = farest
+        z = X[lineStart]
+        self.timerStop("NN_fit_heuristic_lineStart")
 
-            for (j, _) in b.points:
-                d = dist(x, X[j])
-                nbh.insert((j, d))
+        # print(f"lineStart: {lineStart}@{z} ... {bestDist}")
 
-            walkUp(nbh, b, x, i)
+        self.timerStart("NN_fit_heuristic_lineEnd")
+        bestDist = 0.0
+        for (i, x) in enumerate(X):
+            d = dist(x, z)
+            if d > bestDist:
+                farest = i
+                bestDist = d
+
+        lineEnd = farest
+        self.timerStop("NN_fit_heuristic_lineEnd")
+
+        self.timerStart("NN_fit_heuristic_line")
+        # print(f"lineEnd: {lineEnd}@{X[lineEnd]} ... {bestDist}")
+        u = (X[lineEnd] - z)
+        uFactor = (1 / math.sqrt(norm2(u)))
+        u = uFactor * u
+        # print(f"u: {u} ... {norm2(u)}")
+
+        def heuristic(i,x):
+            p = z + (scalarP(u, x - z) * u)
+            dz = math.sqrt(dist(z, p))
+            dx = math.sqrt(dist(x, p)) 
+            return (i, dz, dx)
+
+        line = [heuristic(i, x) for (i,x) in enumerate(X) ]
+        line.sort(key= lambda a: a[1])
+        self.timerStop("NN_fit_heuristic_line")
+        self.timerStop("NN_fit_heuristic_init")
+
+        self.timerStart("NN_fit_heuristic_loop")
+        s = 0
+        ff = False
+        ptsDone = set()
+        for (i,(xi, di, dix)) in enumerate(line):
+            self.timerStart("NN_fit_heuristic_loop_init")
+            h = self.neighbourhoods[xi]
+            z = X[xi]
+            self.timerStop("NN_fit_heuristic_loop_init")
+            ptsDone.add(xi)
+
+            self.timerStart("NN_fit_heuristic_loop_distance")
+            ll = [(xj, norm2([dj - di, djx - dix])) for (xj, dj, djx) in line[i:]]
+            # ll = [(xj, dist(
+            #     np.array([di, dix] + list(X[xi][0:nHeuristic])),
+            #     np.array([dj, djx] + list(X[xj][0:nHeuristic]))))
+            #     for (xj, dj, djx) in line[1:]
+            #     ]
+            ll.sort(key = lambda a: a[1])
+            kk = distancesToPoint(z, [X[j] for (j, _) in ll])
+            self.timerStop("NN_fit_heuristic_loop_distance")
+
+            for (d, (xj, djx)) in zip(kk, ll):
+                ign = h.size >= nebSize and djx > h.getMax()[1]
+                if ign:
+                    break
+                else:
+                    #d = dist(X[xj], z)
+                    self.timerStart("NN_fit_heuristic_insert")
+                    s += 1
+                    h.insert((xj, d))
+                    k = self.neighbourhoods[xj]
+                    if not isinstance(k, list):
+                        k.insert((xi, d))
+                    self.timerStop("NN_fit_heuristic_insert")
+    
+                # if xi == debugLayer:
+                #     d = dist(X[xj], z)
+                #     hint = ""
+                #     if djx > d:
+                #         hint += "!!"
+                #     if ign:
+                #         hint += "*"
+                #     print(f"xj:{xj}   dx:{h.getMax()[1]:0.1f}   djx:{djx:0.1f}  d:{d:0.1f}" + hint)
+            self.timerStart("NN_fit_heuristic_toArray")
+            self.neighbourhoods[xi] = h.toArray()
+            self.timerStop("NN_fit_heuristic_toArray")
+        self.timerStop("NN_fit_heuristic_loop")
+        print(f"calculated distances: {s} / {nPoints * (nPoints - 1)}")
+                    

+ 106 - 0
library/c/Heuristic.c

@@ -0,0 +1,106 @@
+#include "MaxHeap.h"
+#include "utils.h"
+
+#define Point(i)    (&(data[i * nFeatures]))
+
+
+pyWord findFarestPointTo(SearchParams params, pyReal * point) {
+    pyReal d, bestDist = 0.0;
+    pyWord i, bestIdx = 0 - 1;
+
+    for(i = 0 ; i < params->nPoints ; i ++ ) {
+        d = distSquared(point, Point(i));
+        if (d > bestDist) {
+            bestDist = d;
+            bestIdx = i;
+        }
+    }
+
+    return bestIdx;
+}
+
+
+
+void NeighborhoodHeuristic(const pyWord nbhSize, const pyWord nPoints, const pyWord nFeatures, const pyReal * data, pyWord * neighborhoods) {
+    SearchParams params = {
+        .nbhSize = nbhSize,
+        .nPoints = nPoints,
+        .nFeatures = nFeatures,
+        .data = data,
+        .neighborhoods = neighborhoods
+    };
+
+    int success = 0;
+    pyWord i, j;
+    pyReal d;
+    pyReal lineVector = NULL;
+    pyReal accu = NULL;
+
+    if(initNbhHeaps(&params)) {
+        return;
+    }
+
+    do {
+
+        const pyWord lineStartIdx = findFarestPointTo(params, Point(0));
+        if(lineStartIdx >= params->nPoints) {
+            break;
+        }
+
+        pyReal * lineStartPoint = Point(lineStartIdx);
+        const pyWord lineEndIdx = findFarestPointTo(params, lineStartPoint);
+        pyReal * lineEndPoint = Point(lineEndIdx);
+
+
+        pyReal lineVector * = malloc(params->nFeatures * sizeof(pyReal));
+        if(!lineVector) {
+            break;
+        }
+        lineVector = vecSub(nFeatures, lineEndPoint, lineStartPoint); 
+        d = vecNorm(lineVector);
+        if(d == 0.0) {
+            break;
+        }
+        vecScale(nFeatures, lineVector, 1.0 / d, lineVector);
+
+
+        pyReal accu * = malloc(params->nFeatures * sizeof(pyReal));
+        if(!accu) {
+            break;
+        }
+
+        // Berechne alle Distanzen
+        for(i = 0; i < nPoints; i ++) {
+            const pyReal *x = Point(i);
+
+            vecSub(nFeatures, accu, x, lineStartPoint);
+            d = vecScalarProduct(nFeatures, lineVector, accu);
+            vecScale(nFeatures, accu, d, lineVector);
+            vecAdd(nFeatures, accu, accu, lineStartPoint);
+
+            const pyReal di1 = sqrt(distSquared(accu, lineStartPoint, nFeatures));
+            const pyReal di2 = sqrt(distSquared(accu, x, nFeatures));
+
+            
+            for(j = i + 1; j < nPoints; j ++) {
+                const pyReal * y = Point(j);
+                d = distSquared(x, y, nFeatures);
+                
+                maxHeap_insert(&(params.nbhHeaps.heaps[i]), j, d);
+                maxHeap_insert(&(params.nbhHeaps.heaps[j]), i, d);
+            }
+        }
+    } while(0);
+
+    if(lineVector) {
+        free(lineVector);
+    }
+
+    if(!success) {
+        nbhSearchBruteForce(&params);
+    }
+    
+    freeNbhHeaps(&params);
+}
+
+

+ 50 - 0
library/c/LinAlgebra.c

@@ -0,0 +1,50 @@
+#include "types.h"
+#include <math.h>
+
+pyReal vecScalarProduct(const pyWord size, const pyReal * a, const pyReal * b) {
+    pyWord i;
+    pyReal s = 0.0;
+
+    for(i = 0; i < size ; i ++) {
+        s += a[i] * b[i];
+    }
+
+    return s;
+}
+
+pyReal vecNorm(const pyWord size, const pyReal * a) {
+    pyWord i;
+    pyReal s = 0.0;
+
+    for(i = 0; i < size ; i ++) {
+        s += a[i] * a[i];
+    }
+
+    return sqrt(s);
+}
+
+pyReal vecDistSquared(const pyWord size, const pyReal * a, const pyReal * b) {
+    pyWord i;
+    pyReal s = 0.0, d;
+
+    for(i = 0; i < size ; i ++) {
+        d = a[i] - b[i]
+        s += d * d;
+    }
+
+    return s;
+}
+
+void vecSubst(const pyWord size, pyReal * dst, const pyReal * a, const pyReal * b ) {
+    pyWord i;
+    for(i = 0; i < size ; i ++) {
+        dst[i] = a[i] - b[i];
+    }
+}
+
+void vecScale(const pyWord size, pyReal * dst, pyReal s, const pyReal * a) {
+    pyWord i;
+    for(i = 0; i < size ; i ++) {
+        dst[i] = s * a[i];
+    }
+}

+ 12 - 0
library/c/LinAlgebra.h

@@ -0,0 +1,12 @@
+#ifndef _LIN_ALGEBRA_H_
+#define _LIN_ALGEBRA_H_
+
+#include "types.h"
+
+pyReal vecScalarProduct(const pyWord size, const pyReal * a, const pyReal * b);
+pyReal vecNorm(const pyWord size, const pyReal * a);
+pyReal vecDistSquared(const pyWord size, const pyReal * a, const pyReal * b);
+void vecSubst(const pyWord size, pyReal * dst, const pyReal * a, const pyReal * b );
+
+
+#endif

+ 11 - 0
library/c/Makefile

@@ -0,0 +1,11 @@
+c_files = Neighborhood.c utils.c MaxHeap.c
+h_files = Neighborhood.h utils.h MaxHeap.h types.h
+
+
+libNeighborhood.so: $(c_files) $(h_files)
+	gcc -shared -Wall -O2 $(c_files) -o $@
+
+
+test: test.c $(c_files)
+	gcc -Wall -O2 $< -o test.exe -DDoTest
+	./test.exe

+ 61 - 0
library/c/MaxHeap.c

@@ -0,0 +1,61 @@
+#include "MaxHeap.h"
+
+static inline void setItem(MaxHeap * heap, const pyWord pos, const pyWord x, const pyWord d) {
+    heap->indices[pos] = x;
+    heap->distances[pos] = d;
+}
+
+static inline void copyItem(MaxHeap * heap, const pyWord dst, const pyWord src) {
+    heap->indices[dst] = heap->indices[src];
+    heap->distances[dst] = heap->distances[src];
+}
+
+
+void maxHeap_insert(MaxHeap * heap, const pyWord id, const pyReal distance) {
+    if(!heap || heap->maxItems <= 0) {
+        return;
+    }
+
+    if(heap->size < heap->maxItems) {
+        pyWord pos = heap->size;
+        heap->size ++;
+        setItem(heap, pos, id, distance);
+        while(pos > 0) {
+            const pyWord p = pos / 2;
+            if (heap->distances[p] >= heap->distances[pos]) {
+                break;
+            }
+            copyItem(heap, pos, p);
+            pos = p;
+            setItem(heap, pos, id, distance);
+        }
+        return;
+    }
+
+    if(distance > heap->distances[0]) {
+        return;
+    }
+
+    setItem(heap, 0, id, distance);
+
+    pyWord pos = 0;
+    while(pos < heap->size) {
+        const pyWord right = (pos + 1) * 2;
+        const pyWord left = right - 1;
+
+        if(left >= heap->size) {
+            break;
+        }
+
+        const pyWord v = (right >= heap->size || heap->distances[right] < heap->distances[left]) ? left : right;
+
+        if(distance >= heap->distances[v]) {
+            break;
+        }
+
+        copyItem(heap, pos, v);
+        setItem(heap, v, id, distance);
+        pos = v;
+    }
+}
+

+ 8 - 0
library/c/MaxHeap.h

@@ -0,0 +1,8 @@
+#ifndef _MAX_HEAP_H_
+#define _MAX_HEAP_H_
+
+#include "types.h"
+
+void maxHeap_insert(MaxHeap * heap, const pyWord id, const pyReal distance);
+
+#endif

+ 46 - 0
library/c/Neighborhood.c

@@ -0,0 +1,46 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "MaxHeap.h"
+#include "utils.h"
+
+#define Point(i)    (&(params->data[i * params->nFeatures]))
+
+
+void nbhSearchBruteForce(SearchParams * params) {
+    pyWord i, j;
+    pyReal d;
+
+    // Berechne alle Distanzen
+    for(i = 0; i < params->nPoints; i ++) {
+        const pyReal *x = Point(i);
+        
+        for(j = i + 1; j < params->nPoints; j ++) {
+            const pyReal * y = Point(j);
+            d = distSquared(x, y, params->nFeatures);
+            
+            maxHeap_insert(&(params->nbhHeaps.heaps[i]), j, d);
+            maxHeap_insert(&(params->nbhHeaps.heaps[j]), i, d);
+        }
+    }
+}
+
+
+void Neighborhood(const pyWord nbhSize, const pyWord nPoints, const pyWord nFeatures, const pyReal * data, pyWord * neighborhoods) {
+    SearchParams params = {
+        .nbhSize = nbhSize,
+        .nPoints = nPoints,
+        .nFeatures = nFeatures,
+        .data = data,
+        .neighborhoods = neighborhoods
+    };
+
+    if(initNbhHeaps(&params)) {
+        return;
+    }
+
+    nbhSearchBruteForce(&params);
+    
+    freeNbhHeaps(&params);
+}
+
+

+ 10 - 0
library/c/Neighborhood.h

@@ -0,0 +1,10 @@
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include "types.h"
+
+void nbhSearchBruteForce(SearchParams * params);
+
+void Neighborhood(const pyWord nbhSize, const pyWord nPoints, const pyWord nFeatures, const pyReal * data, pyWord * neighborhoods);
+
+#endif

BIN
library/c/libNeighborhood.so


+ 72 - 0
library/c/test.c

@@ -0,0 +1,72 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Neighborhood.h"
+#include "MaxHeap.h"
+
+
+
+#ifdef DoTest
+void printHeap(const MaxHeap * heap) {
+    pyWord p;
+
+    printf("%u / %u\n", (unsigned int)heap->size, (unsigned int)heap->maxItems);
+    for(p = 0; p < heap->size; p ++) {
+        printf("[%u] %u -> %lf\n", (unsigned int) p, (unsigned int)heap->indices[p], heap->distances[p]);
+    }
+}
+
+
+int main(void) {
+#define testcases 5
+    pyReal distances[testcases];
+    pyWord idxs[testcases];
+    MaxHeap heap = {
+        .distances = distances,
+        .indices = idxs,
+        .maxItems = testcases,
+        .size = 1
+    };
+
+    distances[0] = 0.0;
+    idxs[0] = 999;
+
+    printHeap(&heap);
+    maxHeap_insert(&heap, 6, 6.0);
+    maxHeap_insert(&heap, 1, 1.0);
+    maxHeap_insert(&heap, 4, 4.0);
+    maxHeap_insert(&heap, 3, 3.0);
+    maxHeap_insert(&heap, 2, 2.0);
+    maxHeap_insert(&heap, 5, 5.0);
+    printHeap(&heap);
+
+
+
+#define nPoints 9
+#define nbhSize 5
+    pyWord nbh[nPoints * nbhSize];
+    pyReal points[2 * nPoints];
+    int x, y, p;
+    p = 0;
+    for(y = 0; y < 3 ; y ++) {
+        for(x = 0; x < 3 ; x ++) {
+            points[p] = x;
+            points[p + 1] = y;
+            printf("%d: (%d,%d)\n", p/2, x, y);
+            p += 2;
+        }
+    }
+
+    Neighborhood(nbhSize, nPoints, 2, points, nbh);
+    for(y = 0; y < nPoints; y++) {
+        pyWord * p = &nbh[y * nbhSize];
+        printf("%d [%d,%d] ", y, y % 3, y / 3);
+        for(x = 0; x < nbhSize ; x ++) {
+            printf(" (%u|%u,%u)", (unsigned int)p[x], (unsigned int)(p[x] % 3), (unsigned int)(p[x] / 3));
+        }
+        printf("\n");
+    }
+}
+#endif
+
+

+ 28 - 0
library/c/types.h

@@ -0,0 +1,28 @@
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+typedef unsigned long long pyWord;
+typedef double pyReal;
+
+typedef struct s_MaxHeap {
+    pyReal * distances;
+    pyWord * indices;
+    pyWord maxItems;
+    pyWord size;
+} MaxHeap;
+
+typedef struct s_NbhHeaps {
+    MaxHeap * heaps;
+    pyReal * distances;
+} NbhHeaps;
+
+typedef struct s_SearchParams {
+    const pyWord nbhSize;
+    const pyWord nPoints;
+    const pyWord nFeatures;
+    const pyReal * data;
+    pyWord * neighborhoods;
+    NbhHeaps nbhHeaps;
+} SearchParams;
+
+#endif

+ 73 - 0
library/c/utils.c

@@ -0,0 +1,73 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "utils.h"
+
+
+pyReal distSquared(const pyReal * u, const pyReal * v, const pyWord nFeatures) {
+    pyWord n;
+    pyReal s = 0.0, t;
+    
+    for( n = 0 ; n < nFeatures ; n ++) {
+        t = u[n] - v[n];
+        s += t * t;
+    }
+    
+    return s;
+}
+
+
+void freeNbhHeaps(SearchParams * params) {
+    if(params->nbhHeaps.heaps) {
+       free(params->nbhHeaps.heaps);
+        params->nbhHeaps.heaps = NULL;
+    }
+    
+    if(params->nbhHeaps.distances) {
+        free(params->nbhHeaps.distances);
+        params->nbhHeaps.distances = NULL;
+    }
+}
+
+
+int initNbhHeaps(SearchParams * params) {
+    pyWord i, j;
+    
+    pyWord * nbhI;
+    pyReal * distsI;
+
+    params->nbhHeaps.heaps = NULL;
+    params->nbhHeaps.distances = NULL;
+
+    params->nbhHeaps.distances = (pyReal*) malloc((params->nPoints + 1) * params->nbhSize * sizeof(pyReal));
+    if(params->nbhHeaps.distances == NULL) {
+        puts("Out of memory!");
+        return 1;
+    }
+
+    params->nbhHeaps.heaps = (MaxHeap*) malloc((params->nPoints + 1) * sizeof(MaxHeap));
+    if(params->nbhHeaps.heaps == NULL) {
+        free(params->nbhHeaps.distances);
+        params->nbhHeaps.distances = NULL;
+        puts("Out of memory!");
+        return 1;
+    }
+    
+    // Initialisiere Distanzen und Nachbarschaften
+    for(i = 0; i < params->nPoints; i ++) {
+        distsI = &(params->nbhHeaps.distances[i * params->nbhSize]);
+        nbhI = &(params->neighborhoods[i * params->nbhSize]);
+        params->nbhHeaps.heaps[i].distances = distsI;
+        params->nbhHeaps.heaps[i].indices = nbhI;
+        params->nbhHeaps.heaps[i].maxItems = params->nbhSize;
+        params->nbhHeaps.heaps[i].size = 1;
+
+        for(j = 0; j < params->nbhSize; j ++) {
+            nbhI[j] = i;
+            distsI[j] = 0.0;
+        }
+    }
+
+    return 0;
+}
+
+

+ 11 - 0
library/c/utils.h

@@ -0,0 +1,11 @@
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include "types.h"
+
+pyReal distSquared(const pyReal * u, const pyReal * v, const pyWord nFeatures);
+
+int initNbhHeaps(SearchParams * params);
+void freeNbhHeaps(SearchParams * params);
+
+#endif

+ 3 - 3
library/convGAN2.py → library/convGAN_experimental.py

@@ -27,7 +27,7 @@ from tensorflow.keras.layers import Lambda
 
 import time
 
-from library.NNSearch import NNSearch
+from library.NNSearch_experimental import NNSearch
 from library.timing import timing
 
 import warnings
@@ -43,7 +43,7 @@ def create01Labels(totalSize, sizeFirstHalf):
     labels.extend(repeat(np.array([0,1]), totalSize - sizeFirstHalf))
     return np.array(labels)
 
-class ConvGAN2(GanBaseClass):
+class ConvGAN_experimental(GanBaseClass):
     """
     This is a toy example of a GAN.
     It repeats the first point of the training-data-set.
@@ -378,7 +378,7 @@ class ConvGAN2(GanBaseClass):
         self.timing["NMB"].start()
         t = time.time()
         neigh = NNSearch(self.neb, timingDict=self.timing)
-        neigh.fit(data_min)
+        neigh.fit_cLib(data_min)
         self.tNbhFit += (time.time() - t)
         self.nNbhFit += 1
         self.timing["NMB"].stop()

+ 2 - 4
library/timing.py

@@ -1,5 +1,3 @@
-
-
 import time
 
 
@@ -11,11 +9,11 @@ class timing:
         self.runCount = 0
 
     def start(self):
-        self.startTime = time.time()
+        self.startTime = time.process_time()
 
     def stop(self):
         if self.startTime is not None:
-            self.duration += time.time() - self.startTime
+            self.duration += time.process_time() - self.startTime
             self.runCount += 1
         self.startTime = None
 

Некоторые файлы не были показаны из-за большого количества измененных файлов