diff --git a/.gitignore b/.gitignore index 660e1472c767d804713afcf89da9cf2d571d3244..a5346fffdf76d7e214e6a58686f518ba069f42e6 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ bin/* .coverage .cache +uml* skgilearn.egg-info/* htmlcov/ doc/_build/ diff --git a/examples/3.pautomac_light.train.dot.gv.pdf b/examples/3.pautomac_light.train.dot.gv.pdf deleted file mode 100644 index ec0f99e07bfcdac4b0e0c21fe5c94795515f90c1..0000000000000000000000000000000000000000 Binary files a/examples/3.pautomac_light.train.dot.gv.pdf and /dev/null differ diff --git a/examples/3.pautomac_light.train.dot.gv b/examples/3.pautomac_light.train.gv similarity index 91% rename from examples/3.pautomac_light.train.dot.gv rename to examples/3.pautomac_light.train.gv index c3ac1dc8017bc9d5ff6f406afafac9c1ef275d9c..630afa0c022bca2912dd7c273fa6247b5aacb683 100644 --- a/examples/3.pautomac_light.train.dot.gv +++ b/examples/3.pautomac_light.train.gv @@ -1,24 +1,17 @@ //3.pautomac_light.train.dot digraph { 0 [label="0 -______ -> -0.00 -0.08 >"] +______"] 1 [label="1 -______ -> 0.00 --0.02 >"] +______"] 2 [label="2 ______ -> -0.04 -0.45 >"] 3 [label="3 ______ -> -0.11 0.63 >"] 4 [label="4 ______ -> -0.09 -0.55 >"] 0 -> 1 [label="0:-0.24"] 0 -> 2 [label="0:0.35"] diff --git a/examples/3.pautomac_light.train.gv.pdf b/examples/3.pautomac_light.train.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..13325f666807d2f6f0e6e5f37700589d688fa25b Binary files /dev/null and b/examples/3.pautomac_light.train.gv.pdf differ diff --git a/examples/GraphvizTests.ipynb b/examples/GraphvizTests.ipynb index e5b19df6b49006144f5fc69fb0bd0ec02221226a..e4786fd67e9fc2c1abb4c8ef88cb615bff752dfd 100644 --- a/examples/GraphvizTests.ipynb +++ b/examples/GraphvizTests.ipynb @@ -1,106 +1,9 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from graphviz import Digraph\n", - "from graphviz import Source\n", - "from splearn.datasets.base import load_data_sample\n", - "from splearn.tests.datasets.get_dataset_path import get_dataset_path" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "train_file = '3.pautomac_light.train'\n", - "data = load_data_sample(adr=get_dataset_path(train_file))" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.nbL" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5000" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.nbEx" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Splearn_array([[ 3., 0., 3., ..., -1., -1., -1.],\n", - " [ 3., 3., -1., ..., -1., -1., -1.],\n", - " [ 3., 2., 0., ..., -1., -1., -1.],\n", - " ...,\n", - " [ 3., 1., 3., ..., -1., -1., -1.],\n", - " [ 3., 0., 3., ..., -1., -1., -1.],\n", - " [ 3., 3., 1., ..., -1., -1., -1.]])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.data" - ] - }, { "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [], - "source": [ - "from splearn import Learning, Hankel , Spectral" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, "outputs": [ { "name": "stdout", @@ -112,346 +15,6 @@ "End of Automaton computation\n" ] }, - { - "data": { - "text/plain": [ - "Spectral(lcolumns=7, lrows=7, mode_quiet=False, partial=True, rank=5,\n", - " smooth_method='none', sparse=True, version='classic')" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sp = Spectral()\n", - "sp.fit(X=data.data)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "A = sp.Automaton" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-0.00049344, 0.00306347, -0.04407393, -0.10777703, -0.08663914])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.initial" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 0.07757137, -0.02422029, -0.44681254, 0.62773208, -0.55467443])" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.final" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[array([[ 0.04512121, -0.2403897 , 0.34945 , -0.28116807, -0.21402523],\n", - " [ 0.06925801, -0.30062293, 0.20641375, -0.14960814, -0.55805732],\n", - " [ 0.02980115, -0.13866481, 0.18362213, -0.20969545, -0.14481622],\n", - " [ 0.00569934, -0.02338583, -0.06600665, 0.10749935, -0.15103655],\n", - " [-0.02008655, 0.09026348, -0.00552559, -0.03135532, 0.24329022]]),\n", - " array([[ 0.07744772, 0.09007074, -0.30472201, 0.27676245, 0.20289396],\n", - " [-0.0990298 , -0.08061847, 0.25853171, -0.1208633 , -0.11085208],\n", - " [-0.06171079, -0.06244152, 0.12007655, 0.00250637, -0.15679675],\n", - " [-0.00273697, -0.00900572, -0.00046003, -0.00855043, -0.05375465],\n", - " [ 0.03098733, 0.0397268 , -0.04997113, 0.00357694, 0.14182576]]),\n", - " array([[-0.06791915, -0.11357938, 0.37955393, -0.2178498 , -0.22977695],\n", - " [ 0.11596642, 0.14914957, -0.13357508, -0.00891606, 0.34841537],\n", - " [ 0.01173082, 0.0192738 , 0.04142658, -0.03534659, 0.02316491],\n", - " [ 0.00732891, 0.00553651, -0.02245608, 0.03611543, -0.03851434],\n", - " [-0.01058989, -0.01062662, -0.00054311, -0.02556748, 0.04984889]]),\n", - " array([[ 0.07276211, -0.01571956, 0.07428593, -0.10369862, 0.02475347],\n", - " [-0.05607105, -0.08896207, 0.27638225, -0.23711256, 0.07372294],\n", - " [-0.00739129, -0.0487418 , -0.62912397, 0.46816277, 0.09251699],\n", - " [-0.00711022, -0.05623318, -0.36606659, -0.0132978 , 0.64910332],\n", - " [ 0.00233552, -0.02156115, 0.09096243, -0.38438823, 0.66164772]])]" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.transitions" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'classic'" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.type" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.nbL" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A.nbS" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "th = 0.2\n", - "wa = Digraph(comment='3.pautomac_light.train')\n", - "for i in range(A.nbS):\n", - " label = \"{0:d}\\n______\\n> {1:.2f}\\n{2:.2f} >\".format(i, A.initial[i], A.final[i])\n", - " wa.node(str(i),label)\n", - "for l in range(A.nbL):\n", - " for i in range(A.nbS):\n", - " for j in range(A.nbS):\n", - " weight = A.transitions[l][i,j]\n", - " if (np.abs(weight) > th):\n", - " label = \"{0:d}:{1:.2f}\".format(l,weight)\n", - " wa.edge(str(i), str(j), label)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "// 3.pautomac_light.train\n", - "digraph {\n", - "\t0 [label=\"0\n", - "______\n", - "> -0.00\n", - "0.08 >\"]\n", - "\t1 [label=\"1\n", - "______\n", - "> 0.00\n", - "-0.02 >\"]\n", - "\t2 [label=\"2\n", - "______\n", - "> -0.04\n", - "-0.45 >\"]\n", - "\t3 [label=\"3\n", - "______\n", - "> -0.11\n", - "0.63 >\"]\n", - "\t4 [label=\"4\n", - "______\n", - "> -0.09\n", - "-0.55 >\"]\n", - "\t0 -> 1 [label=\"0:-0.24\"]\n", - "\t0 -> 2 [label=\"0:0.35\"]\n", - "\t0 -> 3 [label=\"0:-0.28\"]\n", - "\t0 -> 4 [label=\"0:-0.21\"]\n", - "\t1 -> 1 [label=\"0:-0.30\"]\n", - "\t1 -> 2 [label=\"0:0.21\"]\n", - "\t1 -> 4 [label=\"0:-0.56\"]\n", - "\t2 -> 3 [label=\"0:-0.21\"]\n", - "\t4 -> 4 [label=\"0:0.24\"]\n", - "\t0 -> 2 [label=\"1:-0.30\"]\n", - "\t0 -> 3 [label=\"1:0.28\"]\n", - "\t0 -> 4 [label=\"1:0.20\"]\n", - "\t1 -> 2 [label=\"1:0.26\"]\n", - "\t0 -> 2 [label=\"2:0.38\"]\n", - "\t0 -> 3 [label=\"2:-0.22\"]\n", - "\t0 -> 4 [label=\"2:-0.23\"]\n", - "\t1 -> 4 [label=\"2:0.35\"]\n", - "\t1 -> 2 [label=\"3:0.28\"]\n", - "\t1 -> 3 [label=\"3:-0.24\"]\n", - "\t2 -> 2 [label=\"3:-0.63\"]\n", - "\t2 -> 3 [label=\"3:0.47\"]\n", - "\t3 -> 2 [label=\"3:-0.37\"]\n", - "\t3 -> 4 [label=\"3:0.65\"]\n", - "\t4 -> 3 [label=\"3:-0.38\"]\n", - "\t4 -> 4 [label=\"3:0.66\"]\n", - "}\n" - ] - } - ], - "source": [ - "print(wa.source)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "//automata.dot\n", - "digraph {\n", - "\t0 [label=\"0\n", - "______\n", - "> -0.00\n", - "0.08 >\"]\n", - "\t1 [label=\"1\n", - "______\n", - "> 0.00\n", - "-0.02 >\"]\n", - "\t2 [label=\"2\n", - "______\n", - "> -0.04\n", - "-0.45 >\"]\n", - "\t3 [label=\"3\n", - "______\n", - "> -0.11\n", - "0.63 >\"]\n", - "\t4 [label=\"4\n", - "______\n", - "> -0.09\n", - "-0.55 >\"]\n", - "\t0 -> 1 [label=\"0:-0.24\"]\n", - "\t0 -> 2 [label=\"0:0.35\"]\n", - "\t0 -> 3 [label=\"0:-0.28\"]\n", - "\t0 -> 4 [label=\"0:-0.21\"]\n", - "\t1 -> 1 [label=\"0:-0.30\"]\n", - "\t1 -> 2 [label=\"0:0.21\"]\n", - "\t1 -> 4 [label=\"0:-0.56\"]\n", - "\t2 -> 3 [label=\"0:-0.21\"]\n", - "\t4 -> 4 [label=\"0:0.24\"]\n", - "\t0 -> 2 [label=\"1:-0.30\"]\n", - "\t0 -> 3 [label=\"1:0.28\"]\n", - "\t0 -> 4 [label=\"1:0.20\"]\n", - "\t1 -> 2 [label=\"1:0.26\"]\n", - "\t0 -> 2 [label=\"2:0.38\"]\n", - "\t0 -> 3 [label=\"2:-0.22\"]\n", - "\t0 -> 4 [label=\"2:-0.23\"]\n", - "\t1 -> 4 [label=\"2:0.35\"]\n", - "\t1 -> 2 [label=\"3:0.28\"]\n", - "\t1 -> 3 [label=\"3:-0.24\"]\n", - "\t2 -> 2 [label=\"3:-0.63\"]\n", - "\t2 -> 3 [label=\"3:0.47\"]\n", - "\t3 -> 2 [label=\"3:-0.37\"]\n", - "\t3 -> 4 [label=\"3:0.65\"]\n", - "\t4 -> 3 [label=\"3:-0.38\"]\n", - "\t4 -> 4 [label=\"3:0.66\"]\n", - "}\n", - "\n" - ] - } - ], - "source": [ - "nb_dec = 2\n", - "threshold = th\n", - "filename = \"automata.dot\"\n", - "prec = \".{:d}f\".format(nb_dec)\n", - "out = \"//{:s}\\ndigraph {{\\n\".format(filename)\n", - "for i in range(A.nbS):\n", - " label = \"{0:d}\\n______\\n> {1:\" + prec + \"}\\n{2:\" + prec + \"} >\"\n", - " label = label.format(i, A.initial[i], A.final[i])\n", - " out += \"\\t{0:d} [label=\\\"\".format(i)\n", - " out += label + \"\\\"]\\n\"\n", - "for l in range(A.nbL):\n", - " for i in range(A.nbS):\n", - " for j in range(A.nbS):\n", - " weight = A.transitions[l][i,j]\n", - " if (np.abs(weight) > threshold):\n", - " label = \"{0:d}:{1:\" + prec + \"}\"\n", - " label = label.format(l,weight)\n", - " out += \"\\t{0:d} -> {1:d} [label=\\\"\".format(i,j)\n", - " out += label + \"\\\"]\\n\"\n", - "out += \"}\\n\"\n", - "print(out)" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ { "data": { "image/svg+xml": [ @@ -461,269 +24,202 @@ "<!-- Generated by graphviz version 2.38.0 (20140413.2041)\n", " -->\n", "<!-- Title: %3 Pages: 1 -->\n", - "<svg width=\"769pt\" height=\"693pt\"\n", - " viewBox=\"0.00 0.00 769.23 692.83\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", - "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 688.833)\">\n", + "<svg width=\"779pt\" height=\"544pt\"\n", + " viewBox=\"0.00 0.00 778.50 544.34\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 540.34)\">\n", "<title>%3</title>\n", - "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-688.833 765.226,-688.833 765.226,4 -4,4\"/>\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-540.34 774.504,-540.34 774.504,4 -4,4\"/>\n", "<!-- 0 -->\n", "<g id=\"node1\" class=\"node\"><title>0</title>\n", - "<ellipse fill=\"none\" stroke=\"black\" cx=\"454.226\" cy=\"-636.749\" rx=\"40.1111\" ry=\"48.1667\"/>\n", - "<text text-anchor=\"middle\" x=\"454.226\" y=\"-655.549\" font-family=\"Times,serif\" font-size=\"14.00\">0</text>\n", - "<text text-anchor=\"middle\" x=\"454.226\" y=\"-640.549\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", - "<text text-anchor=\"middle\" x=\"454.226\" y=\"-625.549\" font-family=\"Times,serif\" font-size=\"14.00\">> -0.00</text>\n", - "<text text-anchor=\"middle\" x=\"454.226\" y=\"-610.549\" font-family=\"Times,serif\" font-size=\"14.00\">0.08 ></text>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"468.504\" cy=\"-509.47\" rx=\"40.1111\" ry=\"26.7407\"/>\n", + "<text text-anchor=\"middle\" x=\"468.504\" y=\"-513.27\" font-family=\"Times,serif\" font-size=\"14.00\">0</text>\n", + "<text text-anchor=\"middle\" x=\"468.504\" y=\"-498.27\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", "</g>\n", "<!-- 1 -->\n", "<g id=\"node2\" class=\"node\"><title>1</title>\n", - "<ellipse fill=\"none\" stroke=\"black\" cx=\"91.2259\" cy=\"-489.583\" rx=\"40.1111\" ry=\"48.1667\"/>\n", - "<text text-anchor=\"middle\" x=\"91.2259\" y=\"-508.383\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n", - "<text text-anchor=\"middle\" x=\"91.2259\" y=\"-493.383\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", - "<text text-anchor=\"middle\" x=\"91.2259\" y=\"-478.383\" font-family=\"Times,serif\" font-size=\"14.00\">> 0.00</text>\n", - "<text text-anchor=\"middle\" x=\"91.2259\" y=\"-463.383\" font-family=\"Times,serif\" font-size=\"14.00\">-0.02 ></text>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"104.504\" cy=\"-404.73\" rx=\"40.1111\" ry=\"26.7407\"/>\n", + "<text text-anchor=\"middle\" x=\"104.504\" y=\"-408.53\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n", + "<text text-anchor=\"middle\" x=\"104.504\" y=\"-393.53\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", "</g>\n", "<!-- 0->1 -->\n", "<g id=\"edge1\" class=\"edge\"><title>0->1</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M413.762,-633.458C356.474,-628.716 249.94,-614.281 171.226,-570.666 154.111,-561.183 138.209,-546.963 125.164,-533.125\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"127.545,-530.542 118.221,-525.509 122.371,-535.257 127.545,-530.542\"/>\n", - "<text text-anchor=\"middle\" x=\"191.226\" y=\"-559.466\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.24</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M427.911,-507.623C372.424,-505.241 270.643,-496.46 190.504,-464.6 171.21,-456.93 151.934,-444.308 136.536,-432.714\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"138.454,-429.773 128.405,-426.417 134.168,-435.307 138.454,-429.773\"/>\n", + "<text text-anchor=\"middle\" x=\"208.004\" y=\"-453.4\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.24</text>\n", "</g>\n", "<!-- 2 -->\n", "<g id=\"node3\" class=\"node\"><title>2</title>\n", - "<ellipse fill=\"none\" stroke=\"black\" cx=\"271.226\" cy=\"-342.416\" rx=\"40.1111\" ry=\"48.1667\"/>\n", - "<text text-anchor=\"middle\" x=\"271.226\" y=\"-361.216\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", - "<text text-anchor=\"middle\" x=\"271.226\" y=\"-346.216\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", - "<text text-anchor=\"middle\" x=\"271.226\" y=\"-331.216\" font-family=\"Times,serif\" font-size=\"14.00\">> -0.04</text>\n", - "<text text-anchor=\"middle\" x=\"271.226\" y=\"-316.216\" font-family=\"Times,serif\" font-size=\"14.00\">-0.45 ></text>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"285.504\" cy=\"-289.383\" rx=\"40.1111\" ry=\"37.4533\"/>\n", + "<text text-anchor=\"middle\" x=\"285.504\" y=\"-300.683\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", + "<text text-anchor=\"middle\" x=\"285.504\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", + "<text text-anchor=\"middle\" x=\"285.504\" y=\"-270.683\" font-family=\"Times,serif\" font-size=\"14.00\">-0.45 ></text>\n", "</g>\n", "<!-- 0->2 -->\n", "<g id=\"edge2\" class=\"edge\"><title>0->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M413.716,-631.974C360.806,-624.502 271.16,-602.428 239.226,-537.666 220.324,-499.332 225.355,-481.927 239.226,-441.5 242.515,-431.913 248.899,-432.665 253.226,-423.5 256.729,-416.079 259.567,-407.944 261.862,-399.793\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"265.308,-400.444 264.4,-389.888 258.527,-398.706 265.308,-400.444\"/>\n", - "<text text-anchor=\"middle\" x=\"256.726\" y=\"-485.883\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.35</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M428.131,-508.99C374.922,-507.114 284.447,-494.351 253.504,-431.6 242.941,-410.178 244.364,-399.926 253.504,-377.86 257.834,-367.407 266.158,-369.831 271.504,-359.86 275.306,-352.769 278.079,-344.767 280.101,-336.783\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"283.578,-337.264 282.281,-326.749 276.737,-335.778 283.578,-337.264\"/>\n", + "<text text-anchor=\"middle\" x=\"271.004\" y=\"-401.03\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.35</text>\n", "</g>\n", "<!-- 0->2 -->\n", "<g id=\"edge10\" class=\"edge\"><title>0->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M416.405,-620.115C383.087,-604.321 335.967,-576.672 310.226,-537.666 283.416,-497.041 274.329,-441.975 271.563,-400.846\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"275.049,-400.494 270.989,-390.707 268.06,-400.889 275.049,-400.494\"/>\n", - "<text text-anchor=\"middle\" x=\"330.226\" y=\"-485.883\" font-family=\"Times,serif\" font-size=\"14.00\">1:-0.30</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M431.172,-498.825C397.71,-488.143 350.072,-467.491 324.504,-431.6 304.703,-403.804 294.945,-366.35 290.141,-337\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"293.584,-336.356 288.641,-326.985 286.661,-337.393 293.584,-336.356\"/>\n", + "<text text-anchor=\"middle\" x=\"344.504\" y=\"-401.03\" font-family=\"Times,serif\" font-size=\"14.00\">1:-0.30</text>\n", "</g>\n", "<!-- 0->2 -->\n", "<g id=\"edge14\" class=\"edge\"><title>0->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M426.805,-600.768C413.418,-582.624 397.756,-559.695 386.226,-537.666 365.062,-497.233 372.828,-481.147 350.226,-441.5 338.394,-420.744 322.27,-399.838 307.674,-382.691\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"310.287,-380.362 301.096,-375.092 304.994,-384.943 310.287,-380.362\"/>\n", - "<text text-anchor=\"middle\" x=\"403.726\" y=\"-485.883\" font-family=\"Times,serif\" font-size=\"14.00\">2:0.38</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M447.446,-486.28C433.575,-471.28 415.308,-450.784 400.504,-431.6 382.941,-408.84 382.184,-400.529 364.504,-377.86 350.444,-359.833 333.611,-340.88 319.102,-325.243\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"321.576,-322.764 312.189,-317.852 316.463,-327.546 321.576,-322.764\"/>\n", + "<text text-anchor=\"middle\" x=\"418.004\" y=\"-401.03\" font-family=\"Times,serif\" font-size=\"14.00\">2:0.38</text>\n", "</g>\n", "<!-- 3 -->\n", "<g id=\"node4\" class=\"node\"><title>3</title>\n", - "<ellipse fill=\"none\" stroke=\"black\" cx=\"379.226\" cy=\"-195.25\" rx=\"40.1111\" ry=\"48.1667\"/>\n", - "<text text-anchor=\"middle\" x=\"379.226\" y=\"-214.05\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", - "<text text-anchor=\"middle\" x=\"379.226\" y=\"-199.05\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", - "<text text-anchor=\"middle\" x=\"379.226\" y=\"-184.05\" font-family=\"Times,serif\" font-size=\"14.00\">> -0.11</text>\n", - "<text text-anchor=\"middle\" x=\"379.226\" y=\"-169.05\" font-family=\"Times,serif\" font-size=\"14.00\">0.63 ></text>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"393.504\" cy=\"-163.43\" rx=\"40.1111\" ry=\"37.4533\"/>\n", + "<text text-anchor=\"middle\" x=\"393.504\" y=\"-174.73\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", + "<text text-anchor=\"middle\" x=\"393.504\" y=\"-159.73\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", + "<text text-anchor=\"middle\" x=\"393.504\" y=\"-144.73\" font-family=\"Times,serif\" font-size=\"14.00\">0.63 ></text>\n", "</g>\n", "<!-- 0->3 -->\n", "<g id=\"edge3\" class=\"edge\"><title>0->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M452.202,-588.563C449.086,-548.16 441.192,-489.137 421.226,-441.5 417.411,-432.398 411.851,-432.678 408.226,-423.5 402.101,-407.994 391.559,-315.319 385.014,-253.21\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"388.482,-252.721 383.959,-243.14 381.52,-253.45 388.482,-252.721\"/>\n", - "<text text-anchor=\"middle\" x=\"428.226\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.28</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M464.903,-482.461C460.482,-455.379 451.522,-412.196 435.504,-377.86 431.332,-368.917 426.223,-369.001 422.504,-359.86 416.994,-346.317 405.959,-264.115 399.203,-210.725\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"402.673,-210.272 397.952,-200.787 395.728,-211.146 402.673,-210.272\"/>\n", + "<text text-anchor=\"middle\" x=\"442.504\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.28</text>\n", "</g>\n", "<!-- 0->3 -->\n", "<g id=\"edge11\" class=\"edge\"><title>0->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M458.728,-588.765C459.333,-577.901 459.626,-566.385 459.226,-555.666 456.78,-490.122 460.9,-472.853 448.226,-408.5 437.216,-352.597 416.089,-290.887 400.1,-248.485\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"403.294,-247.039 396.464,-238.94 396.753,-249.531 403.294,-247.039\"/>\n", - "<text text-anchor=\"middle\" x=\"467.726\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">1:0.28</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M472.228,-482.704C473.313,-472.417 474.073,-460.458 473.504,-449.6 471.056,-402.857 473.374,-390.387 462.504,-344.86 448.592,-286.591 435.945,-274.845 414.504,-218.907 413.194,-215.488 411.835,-211.949 410.467,-208.392\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"413.713,-207.08 406.855,-199.005 407.18,-209.594 413.713,-207.08\"/>\n", + "<text text-anchor=\"middle\" x=\"483.004\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">1:0.28</text>\n", "</g>\n", "<!-- 0->3 -->\n", "<g id=\"edge15\" class=\"edge\"><title>0->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M469.799,-592.029C484.073,-546.158 501.295,-472.031 489.226,-408.5 477.258,-345.499 439.666,-281.229 411.741,-240.172\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"414.503,-238.013 405.941,-231.772 408.743,-241.99 414.503,-238.013\"/>\n", - "<text text-anchor=\"middle\" x=\"511.226\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">2:-0.22</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M481.022,-483.724C495.647,-451.614 516.417,-393.894 504.504,-344.86 491.04,-289.442 453.16,-234.958 425.383,-200.611\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"427.971,-198.248 418.915,-192.749 422.565,-202.695 427.971,-198.248\"/>\n", + "<text text-anchor=\"middle\" x=\"526.504\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">2:-0.22</text>\n", "</g>\n", "<!-- 4 -->\n", "<g id=\"node5\" class=\"node\"><title>4</title>\n", - "<ellipse fill=\"none\" stroke=\"black\" cx=\"442.226\" cy=\"-48.0833\" rx=\"40.1111\" ry=\"48.1667\"/>\n", - "<text text-anchor=\"middle\" x=\"442.226\" y=\"-66.8833\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n", - "<text text-anchor=\"middle\" x=\"442.226\" y=\"-51.8833\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", - "<text text-anchor=\"middle\" x=\"442.226\" y=\"-36.8833\" font-family=\"Times,serif\" font-size=\"14.00\">> -0.09</text>\n", - "<text text-anchor=\"middle\" x=\"442.226\" y=\"-21.8833\" font-family=\"Times,serif\" font-size=\"14.00\">-0.55 ></text>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"451.504\" cy=\"-37.4767\" rx=\"40.1111\" ry=\"37.4533\"/>\n", + "<text text-anchor=\"middle\" x=\"451.504\" y=\"-48.7767\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n", + "<text text-anchor=\"middle\" x=\"451.504\" y=\"-33.7767\" font-family=\"Times,serif\" font-size=\"14.00\">______</text>\n", + "<text text-anchor=\"middle\" x=\"451.504\" y=\"-18.7767\" font-family=\"Times,serif\" font-size=\"14.00\">0.55 ></text>\n", "</g>\n", "<!-- 0->4 -->\n", "<g id=\"edge4\" class=\"edge\"><title>0->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M477.607,-597.504C482.438,-588.842 487.257,-579.57 491.226,-570.666 507.101,-535.055 530.584,-456.273 535.226,-423.5 551.99,-305.134 500.17,-169.815 467.252,-98.7205\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"470.36,-97.1038 462.941,-89.5402 464.024,-100.08 470.36,-97.1038\"/>\n", - "<text text-anchor=\"middle\" x=\"558.226\" y=\"-338.716\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.21</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M489.442,-486.226C495.086,-479.575 500.897,-472.06 505.504,-464.6 532.125,-421.492 540.295,-409.486 550.504,-359.86 571.765,-256.513 513.67,-138.916 477.666,-78.6925\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"480.494,-76.608 472.309,-69.8811 474.513,-80.2442 480.494,-76.608\"/>\n", + "<text text-anchor=\"middle\" x=\"572.004\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.21</text>\n", "</g>\n", "<!-- 0->4 -->\n", "<g id=\"edge12\" class=\"edge\"><title>0->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M493.889,-625.691C551.161,-608.509 650.226,-567.65 650.226,-490.583 650.226,-490.583 650.226,-490.583 650.226,-194.25 650.226,-117.288 553.282,-77.7506 491.761,-60.4088\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"492.406,-56.9574 481.839,-57.7163 490.573,-63.7131 492.406,-56.9574\"/>\n", - "<text text-anchor=\"middle\" x=\"667.726\" y=\"-338.716\" font-family=\"Times,serif\" font-size=\"14.00\">1:0.20</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M508.544,-504.666C564.551,-496.893 659.504,-473.693 659.504,-405.73 659.504,-405.73 659.504,-405.73 659.504,-162.43 659.504,-89.1608 563.085,-58.2029 501.54,-45.9054\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"502.086,-42.4466 491.609,-44.021 500.781,-49.3239 502.086,-42.4466\"/>\n", + "<text text-anchor=\"middle\" x=\"679.504\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">1:-0.20</text>\n", "</g>\n", "<!-- 0->4 -->\n", "<g id=\"edge16\" class=\"edge\"><title>0->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M494.488,-632.238C569.076,-623.292 721.226,-592.546 721.226,-490.583 721.226,-490.583 721.226,-490.583 721.226,-194.25 721.226,-92.4819 573.338,-61.9517 492.904,-52.8728\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"492.809,-49.3435 482.497,-51.7751 492.075,-56.3048 492.809,-49.3435\"/>\n", - "<text text-anchor=\"middle\" x=\"741.226\" y=\"-338.716\" font-family=\"Times,serif\" font-size=\"14.00\">2:-0.23</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M507.328,-501.894C581.38,-488.244 735.504,-454.032 735.504,-405.73 735.504,-405.73 735.504,-405.73 735.504,-162.43 735.504,-61.6993 583.574,-42.0094 501.961,-38.6991\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"502.005,-35.1987 491.893,-38.3592 501.769,-42.1947 502.005,-35.1987\"/>\n", + "<text text-anchor=\"middle\" x=\"753.004\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">2:0.23</text>\n", "</g>\n", "<!-- 1->1 -->\n", "<g id=\"edge5\" class=\"edge\"><title>1->1</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M130.19,-503.016C141.132,-502.428 149.531,-497.95 149.531,-489.583 149.531,-484.092 145.914,-480.276 140.335,-478.135\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"140.676,-474.636 130.19,-476.15 139.332,-481.505 140.676,-474.636\"/>\n", - "<text text-anchor=\"middle\" x=\"169.531\" y=\"-485.883\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.30</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M142.076,-414.47C153.69,-414.274 162.809,-411.027 162.809,-404.73 162.809,-400.401 158.499,-397.513 152.031,-396.068\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"152.395,-392.587 142.076,-394.99 151.641,-399.546 152.395,-392.587\"/>\n", + "<text text-anchor=\"middle\" x=\"182.809\" y=\"-401.03\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.30</text>\n", "</g>\n", "<!-- 1->2 -->\n", "<g id=\"edge6\" class=\"edge\"><title>1->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M92.1967,-441.117C94.4462,-429.435 98.6615,-417.663 106.226,-408.5 134.628,-374.095 183.729,-357.787 221.004,-350.112\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"222.011,-353.483 231.177,-348.168 220.698,-346.607 222.011,-353.483\"/>\n", - "<text text-anchor=\"middle\" x=\"123.726\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.21</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M98.6025,-378.068C97.5137,-366.849 98.4802,-354.158 105.504,-344.86 135.321,-305.389 192.891,-293.557 234.921,-290.474\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"235.201,-293.964 244.977,-289.879 234.787,-286.976 235.201,-293.964\"/>\n", + "<text text-anchor=\"middle\" x=\"125.504\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.21</text>\n", "</g>\n", "<!-- 1->2 -->\n", "<g id=\"edge13\" class=\"edge\"><title>1->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M118.753,-454.187C131.671,-439.203 147.818,-422.016 164.226,-408.5 183,-393.035 205.798,-378.658 225.518,-367.371\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"227.338,-370.363 234.337,-362.41 223.905,-364.263 227.338,-370.363\"/>\n", - "<text text-anchor=\"middle\" x=\"181.726\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">1:0.26</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M125.308,-381.505C137.194,-369.636 152.82,-355.355 168.504,-344.86 190.42,-330.195 216.912,-317.501 239.142,-308.064\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"240.491,-311.293 248.382,-304.223 237.804,-304.83 240.491,-311.293\"/>\n", + "<text text-anchor=\"middle\" x=\"188.504\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">1:-0.26</text>\n", "</g>\n", "<!-- 1->2 -->\n", "<g id=\"edge18\" class=\"edge\"><title>1->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M128.266,-469.839C149.989,-457.997 177.369,-441.554 199.226,-423.5 212.595,-412.456 225.728,-398.902 237.041,-386.107\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"239.931,-388.117 243.835,-378.268 234.641,-383.532 239.931,-388.117\"/>\n", - "<text text-anchor=\"middle\" x=\"231.726\" y=\"-412.3\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.28</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M140.147,-392.071C160.935,-384.463 187.176,-373.43 208.504,-359.86 223.87,-350.084 239.114,-337.187 251.934,-325.161\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"254.387,-327.658 259.184,-318.212 249.543,-322.605 254.387,-327.658\"/>\n", + "<text text-anchor=\"middle\" x=\"248.504\" y=\"-348.66\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.28</text>\n", "</g>\n", "<!-- 1->3 -->\n", "<g id=\"edge19\" class=\"edge\"><title>1->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M58.6787,-460.48C48.705,-449.963 38.8801,-437.212 33.2259,-423.5 5.7168,-356.787 -20.957,-316.781 25.2259,-261.333 63.262,-215.666 240.22,-201.931 328.651,-197.887\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"328.885,-201.381 338.724,-197.451 328.582,-194.387 328.885,-201.381\"/>\n", - "<text text-anchor=\"middle\" x=\"39.2259\" y=\"-338.716\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.24</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M69.8367,-391.023C55.823,-384.043 40.982,-373.892 32.5041,-359.86 0.0558097,-306.155 -16.4967,-266.405 24.5041,-218.907 65.0444,-171.942 251.628,-164.763 342.916,-164.122\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"343.048,-167.622 353.032,-164.075 343.016,-160.622 343.048,-167.622\"/>\n", + "<text text-anchor=\"middle\" x=\"32.0041\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.24</text>\n", "</g>\n", "<!-- 1->4 -->\n", "<g id=\"edge7\" class=\"edge\"><title>1->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M70.1223,-448.415C66.6297,-440.348 63.433,-431.796 61.2259,-423.5 42.6367,-353.625 36.8748,-332.742 48.2259,-261.333 59.0265,-193.388 44.5356,-159.569 96.2259,-114.167 139.484,-76.1709 306.454,-58.8513 391.628,-52.3534\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"392.13,-55.8259 401.844,-51.5961 391.612,-48.845 392.13,-55.8259\"/>\n", - "<text text-anchor=\"middle\" x=\"68.2259\" y=\"-265.133\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.56</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M78.7257,-383.814C71.6586,-377.003 64.7748,-368.818 60.5041,-359.86 33.431,-303.071 36.1141,-280.779 47.5041,-218.907 58.35,-159.99 48.895,-130.588 95.5041,-92.9533 141.549,-55.7741 314.163,-43.792 400.98,-40.0722\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"401.279,-43.563 411.128,-39.6584 400.993,-36.5688 401.279,-43.563\"/>\n", + "<text text-anchor=\"middle\" x=\"67.5041\" y=\"-222.707\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.56</text>\n", "</g>\n", "<!-- 1->4 -->\n", "<g id=\"edge17\" class=\"edge\"><title>1->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M88.146,-441.308C88.5952,-430.35 90.0294,-418.866 93.2259,-408.5 116.165,-334.105 136.66,-319.658 188.226,-261.333 253.622,-187.364 345.645,-117.08 399.099,-78.852\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"401.214,-81.6427 407.337,-72.9962 397.158,-75.9373 401.214,-81.6427\"/>\n", - "<text text-anchor=\"middle\" x=\"205.726\" y=\"-265.133\" font-family=\"Times,serif\" font-size=\"14.00\">2:0.35</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M94.5949,-378.563C91.8365,-367.928 90.5467,-355.555 94.5041,-344.86 120.094,-275.702 148.274,-268.875 202.504,-218.907 269.679,-157.011 358.29,-97.2526 409.676,-64.4244\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"411.646,-67.3195 418.209,-59.0021 407.892,-61.4114 411.646,-67.3195\"/>\n", + "<text text-anchor=\"middle\" x=\"220.004\" y=\"-222.707\" font-family=\"Times,serif\" font-size=\"14.00\">2:0.35</text>\n", "</g>\n", "<!-- 2->2 -->\n", "<g id=\"edge20\" class=\"edge\"><title>2->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M310.19,-355.85C321.132,-355.261 329.531,-350.783 329.531,-342.416 329.531,-336.925 325.914,-333.109 320.335,-330.969\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"320.676,-327.469 310.19,-328.983 319.332,-334.339 320.676,-327.469\"/>\n", - "<text text-anchor=\"middle\" x=\"349.531\" y=\"-338.716\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.63</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M323.915,-300.974C335.125,-300.573 343.809,-296.709 343.809,-289.383 343.809,-284.461 339.889,-281.102 333.917,-279.306\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"334.326,-275.828 323.915,-277.793 333.279,-282.749 334.326,-275.828\"/>\n", + "<text text-anchor=\"middle\" x=\"363.809\" y=\"-285.683\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.63</text>\n", "</g>\n", "<!-- 2->3 -->\n", "<g id=\"edge8\" class=\"edge\"><title>2->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M241.048,-310.41C229.908,-295.145 222.187,-276.756 232.226,-261.333 253.475,-228.687 295.245,-212.293 328.845,-204.133\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"329.99,-207.463 338.979,-201.856 328.455,-200.634 329.99,-207.463\"/>\n", - "<text text-anchor=\"middle\" x=\"252.226\" y=\"-265.133\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.21</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M256.711,-262.59C245.271,-249.187 236.865,-232.758 246.504,-218.907 268.13,-187.829 309.659,-174.441 343.075,-168.693\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"343.802,-172.123 353.154,-167.144 342.738,-165.204 343.802,-172.123\"/>\n", + "<text text-anchor=\"middle\" x=\"266.504\" y=\"-222.707\" font-family=\"Times,serif\" font-size=\"14.00\">0:-0.21</text>\n", "</g>\n", "<!-- 2->3 -->\n", "<g id=\"edge21\" class=\"edge\"><title>2->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M279.828,-295.156C283.373,-283.464 288.328,-271.386 295.226,-261.333 305.522,-246.328 320.355,-233.224 334.7,-222.783\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"336.858,-225.545 343.051,-216.949 332.849,-219.806 336.858,-225.545\"/>\n", - "<text text-anchor=\"middle\" x=\"312.726\" y=\"-265.133\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.47</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M293.063,-252.195C296.691,-240.736 301.954,-228.554 309.504,-218.907 319.972,-205.531 334.688,-194.429 348.883,-185.814\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"350.702,-188.805 357.612,-180.773 347.202,-182.743 350.702,-188.805\"/>\n", + "<text text-anchor=\"middle\" x=\"327.004\" y=\"-222.707\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.47</text>\n", "</g>\n", "<!-- 3->2 -->\n", "<g id=\"edge22\" class=\"edge\"><title>3->2</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M356.967,-235.908C349.033,-249.115 339.703,-263.678 330.226,-276.333 323.577,-285.212 315.943,-294.3 308.422,-302.76\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"305.688,-300.565 301.577,-310.33 310.88,-305.26 305.688,-300.565\"/>\n", - "<text text-anchor=\"middle\" x=\"360.226\" y=\"-265.133\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.37</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M373.373,-195.918C364.991,-208.208 354.818,-222.142 344.504,-233.907 337.749,-241.612 330.01,-249.384 322.408,-256.548\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"319.867,-254.13 314.893,-263.485 324.615,-259.274 319.867,-254.13\"/>\n", + "<text text-anchor=\"middle\" x=\"375.504\" y=\"-222.707\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.37</text>\n", "</g>\n", "<!-- 3->4 -->\n", "<g id=\"edge23\" class=\"edge\"><title>3->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M404.82,-157.78C410.438,-148.714 415.971,-138.82 420.226,-129.167 423.653,-121.39 426.668,-112.95 429.28,-104.564\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"432.691,-105.368 432.16,-94.7864 425.977,-103.39 432.691,-105.368\"/>\n", - "<text text-anchor=\"middle\" x=\"442.726\" y=\"-117.967\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.65</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M415.803,-131.906C420.742,-124.317 425.647,-116.036 429.504,-107.953 433.147,-100.321 436.387,-91.9375 439.187,-83.7156\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"442.52,-84.7847 442.269,-74.1928 435.86,-82.6292 442.52,-84.7847\"/>\n", + "<text text-anchor=\"middle\" x=\"455.504\" y=\"-96.7533\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.65</text>\n", "</g>\n", "<!-- 4->3 -->\n", "<g id=\"edge24\" class=\"edge\"><title>4->3</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M407.931,-73.4177C395.491,-84.3055 382.871,-98.2815 376.226,-114.167 373.096,-121.649 371.547,-129.877 371.021,-138.122\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"367.522,-138.081 370.85,-148.139 374.521,-138.201 367.522,-138.081\"/>\n", - "<text text-anchor=\"middle\" x=\"396.226\" y=\"-117.967\" font-family=\"Times,serif\" font-size=\"14.00\">3:-0.38</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M418.585,-59.2899C407.522,-68.1794 396.48,-79.6052 390.504,-92.9533 387.244,-100.236 385.867,-108.39 385.618,-116.479\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"382.122,-116.703 385.85,-126.62 389.12,-116.543 382.122,-116.703\"/>\n", + "<text text-anchor=\"middle\" x=\"408.004\" y=\"-96.7533\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.38</text>\n", "</g>\n", "<!-- 4->4 -->\n", "<g id=\"edge9\" class=\"edge\"><title>4->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M482.285,-54.7636C492.687,-54.353 500.531,-52.1262 500.531,-48.0833 500.531,-45.4932 497.312,-43.6486 492.263,-42.5493\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"492.619,-39.0672 482.285,-41.4029 491.82,-46.0214 492.619,-39.0672\"/>\n", - "<text text-anchor=\"middle\" x=\"518.031\" y=\"-44.3833\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.24</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M491.563,-43.2292C501.965,-42.8756 509.809,-40.9581 509.809,-37.4767 509.809,-35.2464 506.59,-33.6579 501.541,-32.7113\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"501.859,-29.2257 491.563,-31.7241 501.17,-36.1917 501.859,-29.2257\"/>\n", + "<text text-anchor=\"middle\" x=\"527.309\" y=\"-33.7767\" font-family=\"Times,serif\" font-size=\"14.00\">0:0.24</text>\n", "</g>\n", "<!-- 4->4 -->\n", "<g id=\"edge25\" class=\"edge\"><title>4->4</title>\n", - "<path fill=\"none\" stroke=\"black\" d=\"M481.459,-60.5739C508.016,-63.7077 535.531,-59.5442 535.531,-48.0833 535.531,-38.0997 514.652,-33.6535 491.715,-34.7445\"/>\n", - "<polygon fill=\"black\" stroke=\"black\" points=\"491.136,-31.2804 481.459,-35.5926 491.713,-38.2566 491.136,-31.2804\"/>\n", - "<text text-anchor=\"middle\" x=\"553.031\" y=\"-44.3833\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.66</text>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M490.248,-48.1821C516.955,-50.9748 544.809,-47.4063 544.809,-37.4767 544.809,-28.7882 523.484,-24.97 500.26,-26.0221\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"499.959,-22.5348 490.248,-26.7712 500.481,-29.5153 499.959,-22.5348\"/>\n", + "<text text-anchor=\"middle\" x=\"562.309\" y=\"-33.7767\" font-family=\"Times,serif\" font-size=\"14.00\">3:0.66</text>\n", "</g>\n", "</g>\n", "</svg>\n" ], "text/plain": [ - "<graphviz.files.Source at 0x7f25558204a8>" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "wa.render('graphivz-output/3.pautomac_light.train.gv', view=False)\n", - "Source.from_file(\"graphivz-output/3.pautomac_light.train.gv\")" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "src = Source(out)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'graphivz-output/3.pautomac_light_man.train.gv.pdf'" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "src.render('graphivz-output/3.pautomac_light_man.train.gv', view=True) " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Start Hankel matrix computation\n", - "End of Hankel matrix computation\n", - "Start Building Automaton from Hankel matrix\n", - "End of Automaton computation\n" - ] - }, - { - "data": { - "text/plain": [ - "'3.pautomac_light.train.dot.gv.pdf'" + "<graphviz.files.Source at 0x7f99d48d7630>" ] }, - "execution_count": 1, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -737,11 +233,12 @@ "sp = Spectral()\n", "sp.fit(X=data.data)\n", "dotfile = \"3.pautomac_light.train.dot\"\n", - "dot = sp.Automaton.get_dot(threshold = 0.2, title = dotfile)\n", + "dot = sp.automaton.get_dot(threshold = 0.2, title = dotfile)\n", "# To display the dot string one can use graphviz:\n", "from graphviz import Source\n", "src = Source(dot)\n", - "src.render(dotfile + '.gv', view=True) " + "src.render(train_file + '.gv', view=False)\n", + "Source.from_file(train_file + '.gv')" ] }, { diff --git a/examples/graphivz-output/3.pautomac_light.train.gv b/examples/graphivz-output/3.pautomac_light.train.gv deleted file mode 100644 index 4e539a24850c3d44cae8a1e118f6eb50ec05dab9..0000000000000000000000000000000000000000 --- a/examples/graphivz-output/3.pautomac_light.train.gv +++ /dev/null @@ -1,48 +0,0 @@ -// 3.pautomac_light.train -digraph { - 0 [label="0 -______ -> -0.00 -0.08 >"] - 1 [label="1 -______ -> 0.00 --0.02 >"] - 2 [label="2 -______ -> -0.04 --0.45 >"] - 3 [label="3 -______ -> -0.11 -0.63 >"] - 4 [label="4 -______ -> -0.09 --0.55 >"] - 0 -> 1 [label="0:-0.24"] - 0 -> 2 [label="0:0.35"] - 0 -> 3 [label="0:-0.28"] - 0 -> 4 [label="0:-0.21"] - 1 -> 1 [label="0:-0.30"] - 1 -> 2 [label="0:0.21"] - 1 -> 4 [label="0:-0.56"] - 2 -> 3 [label="0:-0.21"] - 4 -> 4 [label="0:0.24"] - 0 -> 2 [label="1:-0.30"] - 0 -> 3 [label="1:0.28"] - 0 -> 4 [label="1:0.20"] - 1 -> 2 [label="1:0.26"] - 0 -> 2 [label="2:0.38"] - 0 -> 3 [label="2:-0.22"] - 0 -> 4 [label="2:-0.23"] - 1 -> 4 [label="2:0.35"] - 1 -> 2 [label="3:0.28"] - 1 -> 3 [label="3:-0.24"] - 2 -> 2 [label="3:-0.63"] - 2 -> 3 [label="3:0.47"] - 3 -> 2 [label="3:-0.37"] - 3 -> 4 [label="3:0.65"] - 4 -> 3 [label="3:-0.38"] - 4 -> 4 [label="3:0.66"] -} diff --git a/examples/graphivz-output/3.pautomac_light.train.gv.pdf b/examples/graphivz-output/3.pautomac_light.train.gv.pdf deleted file mode 100644 index ec0f99e07bfcdac4b0e0c21fe5c94795515f90c1..0000000000000000000000000000000000000000 Binary files a/examples/graphivz-output/3.pautomac_light.train.gv.pdf and /dev/null differ diff --git a/examples/graphivz-output/3.pautomac_light_man.train.gv.pdf b/examples/graphivz-output/3.pautomac_light_man.train.gv.pdf deleted file mode 100644 index ec0f99e07bfcdac4b0e0c21fe5c94795515f90c1..0000000000000000000000000000000000000000 Binary files a/examples/graphivz-output/3.pautomac_light_man.train.gv.pdf and /dev/null differ diff --git a/examples/graphivz-output/round-table.gv b/examples/graphivz-output/round-table.gv deleted file mode 100644 index 48aa015997756e0312cae651ceffdc8082ced691..0000000000000000000000000000000000000000 --- a/examples/graphivz-output/round-table.gv +++ /dev/null @@ -1,9 +0,0 @@ -// The Round Table -digraph { - A [label="King Arthur"] - B [label="Sir Bedevere the Wise"] - L [label="Sir Lancelot the Brave"] - A -> B - A -> L - B -> L [constraint=false] -} diff --git a/examples/graphivz-output/round-table.gv.pdf b/examples/graphivz-output/round-table.gv.pdf deleted file mode 100644 index ed7810cf4f968b2f4ccd117b59a3e01137c243bd..0000000000000000000000000000000000000000 Binary files a/examples/graphivz-output/round-table.gv.pdf and /dev/null differ diff --git a/splearn/__init__.py b/splearn/__init__.py index 384681e1332f8beeacb454fcc22ae815bc98850c..88d3dc0f47225850a99aff6e86da8fb109219213 100644 --- a/splearn/__init__.py +++ b/splearn/__init__.py @@ -1,5 +1,5 @@ from splearn.automaton import Automaton -from splearn.spectral import Learning +#from splearn.spectral import Learning from splearn.spectral import Spectral from splearn.hankel import Hankel __version__ = "1.1.0" diff --git a/splearn/automaton.py b/splearn/automaton.py index 44338b9e36d867799c36f21f2bccf29189135e7e..f5a09fe34ec567dccaee5aa4ab7ba3dac45b5d90 100644 --- a/splearn/automaton.py +++ b/splearn/automaton.py @@ -4,7 +4,6 @@ import numpy as np - class Automaton(object): """ Define an automaton with parameters @@ -149,7 +148,7 @@ class Automaton(object): raise TypeError(mess) if (type_value == 'classic' or type_value == 'prefix' or type_value == 'suffix' or type_value == 'factor'): - self._type = type_value + self._type = type_value else: raise ValueError("type must be classic, prefix, suffix or factor.") @@ -286,6 +285,26 @@ class Automaton(object): lh[x+1][i, j] = self.val(w) dval[w] = lh[x+1][i, j] return lh + + def to_hankel(self, lrows, lcolumns, mode_quiet=False): + """ Return an Hankel instance (denses, classic and not partial) with matrices + built on lrows and lcolumns from an automaton + + - Input: + + :param list lrows: prefixes + :param list lcolumns: suffixes + :param boolean mode_quiet: (default value = False) True for no + output message. + + - Output: + + :returns: Hankel instance + :rtype: Hankel + """ + from splearn.hankel import Hankel + lhankels = self.BuildHankels(lrows, lcolumns) + return Hankel(mode_quiet=mode_quiet, lhankels=lhankels, rank=self.nbS) def mirror(self): """ Compute the mirror automaton @@ -637,15 +656,31 @@ class Automaton(object): prec = ".{:d}f".format(nb_dec) out = "//{:s}\ndigraph {{\n".format(title) for i in range(self.nbS): - label = "{0:d}\n______\n> {1:" + prec + "}\n{2:" + prec + "} >" - label = label.format(i, self.initial[i], self.final[i]) - out += "\t{0:d} [label=\"".format(i) - out += label + "\"]\n" + if np.abs(self.initial[i]) > threshold and np.abs(self.final[i]) > threshold: + label = "{0:d}\n______\n> {1:" + prec + "}\n{2:" + prec + "} >" + label = label.format(i, self.initial[i], self.final[i]) + out += "\t{0:d} [label=\"".format(i) + out += label + "\"]\n" + elif np.abs(self.initial[i]) > threshold: + label = "{0:d}\n______\n> {1:" + prec + "}" + label = label.format(i, self.initial[i]) + out += "\t{0:d} [label=\"".format(i) + out += label + "\"]\n" + elif np.abs(self.final[i]) > threshold: + label = "{0:d}\n______\n{1:" + prec + "} >" + label = label.format(i, self.final[i]) + out += "\t{0:d} [label=\"".format(i) + out += label + "\"]\n" + else: + label = "{0:d}\n______" + label = label.format(i) + out += "\t{0:d} [label=\"".format(i) + out += label + "\"]\n" for l in range(self.nbL): for i in range(self.nbS): for j in range(self.nbS): weight = self.transitions[l][i,j] - if (np.abs(weight) > threshold): + if np.abs(weight) > threshold: label = "{0:d}:{1:" + prec + "}" label = label.format(l,weight) out += "\t{0:d} -> {1:d} [label=\"".format(i,j) diff --git a/splearn/hankel.py b/splearn/hankel.py index b20f7bd0929a2e8f24709df6d4cc7f29b14a2722..41d58ea824dc070c6ca485e8399eece504b3abc7 100644 --- a/splearn/hankel.py +++ b/splearn/hankel.py @@ -4,9 +4,9 @@ """ from __future__ import division, print_function import scipy.sparse as sps +import scipy.sparse.linalg as lin import numpy as np - class Hankel(object): """ A Hankel instance , compute the list of Hankel matrices @@ -25,7 +25,7 @@ class Hankel(object): - Input: - :param dict sample: sample dictionary + :param dict sample_instance: sample dictionary :param dict pref: prefix dictionary :param dict suff: suffix dictionary :param dict fact: factor dictionary @@ -47,26 +47,45 @@ class Hankel(object): matrix is sparse :param boolean mode_quiet: (default value = False) True for no output message. + :param list of all Hankel matrices. At least one of the two parameters + *sample_instance* or *lhankels* has to be not None. If *sample_instance* is given, + the **Hankel** instance is built directly from the sample dictionnary, + else it is deduced from the *lhankels* list of matrices. + :param int rank: the rank of the Hankel matrices. Required if **Hankel** is build from *lhankels*. """ def __init__( - self, sample_instance, + self, sample_instance=None, lrows=[], lcolumns=[], version="classic", partial=False, - sparse=False, mode_quiet=False): - # Size of the alphabet - self.nbL = sample_instance.nbL - # Number of samples - self.nbEx = sample_instance.nbEx + sparse=False, mode_quiet=False, lhankels=None, rank = -1): + self.version = version self.partial = partial self.sparse = sparse - self.lhankel = self.build(sample=sample_instance.sample, - pref=sample_instance.pref, - suff=sample_instance.suff, - fact=sample_instance.fact, - lrows=lrows, lcolumns=lcolumns, - mode_quiet=mode_quiet) + self.buildFromSample = True + if sample_instance is not None: + # Size of the alphabet + self.nbL = sample_instance.nbL + # Number of samples + self.nbEx = sample_instance.nbEx + self.lhankel = self.build(sample=sample_instance.sample, + pref=sample_instance.pref, + suff=sample_instance.suff, + fact=sample_instance.fact, + lrows=lrows, lcolumns=lcolumns, + mode_quiet=mode_quiet) + elif lhankels is not None: + # Size of the alphabet + self.nbL = len(lhankels) - 1 + # Number of samples + if rank == -1: + raise ValueError("Building an Hankel instance from list of hankel matrices required a valid rank.") + self.nbS = rank + self.lhankel = lhankels + self.buildFromSample = False + else: + raise ValueError("At least sample_instance or lhankel has to be not None.") @property def nbL(self): @@ -245,3 +264,72 @@ class Hankel(object): dcolumns = {lcolumns[i]: i for i in range(nbColumns)} return (drows, dcolumns) + + def to_automaton(self, rank, mode_quiet): + """ Return an automaton from the current Hankel matrix + + - Input: + + :param int rank: the ranking number + :param boolean mode_quiet: True for no output message. + + - Output: + + :returns: An automaton instance + :rtype: Automaton + """ + from splearn.automaton import Automaton + if not mode_quiet: + print("Start Building Automaton from Hankel matrix") + matrix_shape =min(self.lhankel[0].shape) + if (min(self.lhankel[0].shape) < rank) : + raise ValueError("The value of parameter rank ("+ str(rank) + + ") should be less than " + + "the smaller dimension of the Hankel Matrix (" + + str(matrix_shape) + ")") + if not self.sparse: + hankel = self.lhankel[0] + [u, s, v] = np.linalg.svd(hankel) + u = u[:, :rank] + v = v[:rank, :] + # ds = np.zeros((rank, rank), dtype=complex) + ds = np.diag(s[:rank]) + pis = np.linalg.pinv(v) + del v + pip = np.linalg.pinv(np.dot(u, ds)) + del u, ds + init = np.dot(hankel[0, :], pis) + term = np.dot(pip, hankel[:, 0]) + trans = [] + for x in range(self.nbL): + hankel = self.lhankel[x+1] + trans.append(np.dot(pip, np.dot(hankel, pis))) + + else: + hankel = self.lhankel[0] + [u, s, v] = lin.svds(hankel, k=rank) + ds = np.diag(s) + pis = np.linalg.pinv(v) + del v + pip = np.linalg.pinv(np.dot(u, ds)) + del u, ds + init = hankel[0, :].dot(pis)[0, :] + term = np.dot(pip, hankel[:, 0].toarray())[:, 0] + trans = [] + for x in range(self.nbL): + hankel = self.lhankel[x+1] + trans.append(np.dot(pip, hankel.dot(pis))) + + A = Automaton(nbL=self.nbL, nbS=rank, initial=init, final=term, + transitions=trans, type=self.version) + if self.buildFromSample: + A.initial = A.initial / self.nbEx + if self.version == "prefix": + A = A.transformation(source="prefix", target="classic") + if self.version == "factor": + A = A.transformation(source="factor", target="classic") + if self.version == "suffix": + A = A.transformation(source="suffix", target="classic") + if not mode_quiet: + print ("End of Automaton computation") + return A diff --git a/splearn/spectral.py b/splearn/spectral.py index ce8e027aa4ecaaaa230cccec7cb109bea7912b7b..883ffdfd0d6af8c6e718f442a3816af47f645845 100644 --- a/splearn/spectral.py +++ b/splearn/spectral.py @@ -6,12 +6,9 @@ """ from __future__ import division, print_function import numpy as np -import scipy.sparse as sps -import scipy.sparse.linalg as lin import math from splearn.datasets.data_sample import Splearn_array from splearn.hankel import Hankel -from splearn.automaton import Automaton from sklearn.base import BaseEstimator from sklearn.utils import check_array from sklearn.utils.validation import NotFittedError @@ -33,7 +30,7 @@ class Spectral(BaseEstimator): Start Building Automaton from Hankel matrix End of Automaton computation Spectral(lcolumns=6, lrows=6, partial=True, rank=5, smooth_method='trigram', sparse=True, version='classic') - >>> sp.Automaton.initial + >>> sp.automaton.initial array([-0.00049249, 0.00304676, -0.04405996, -0.10765322, -0.08660063]) >>> sp.predict(data.data) array([ 4.38961058e-04, 1.10616861e-01, 1.35569353e-03, ..., @@ -91,6 +88,8 @@ class Spectral(BaseEstimator): self.smooth_method = smooth_method self._rule_smooth_method(smooth_method) self.mode_quiet = mode_quiet + self._automaton = None + self._hankel = None def get_params(self, deep=True): # suppose this estimator has parameters @@ -110,6 +109,23 @@ class Spectral(BaseEstimator): "smooth_method" : self.smooth_method, "mode_quiet" : self.mode_quiet } + @property + def automaton(self): + """Automaton build by the fit method. None by default""" + return self._automaton + + @automaton.setter + def automaton(self, automaton): + pass + + @property + def hankel(self): + """Hankel build by the fit method. None by default""" + return self._hankel + + @hankel.setter + def hankel(self, hankel): + pass def _rule_smooth_method(self, value): if self.smooth_method not in ['none', 'trigram']: @@ -157,16 +173,16 @@ class Spectral(BaseEstimator): check_array(X) if not isinstance(X, Splearn_array): - self.Automaton = None + self._hankel = None + self._automaton = None return self X = self._polulate_dictionnaries(X) - learn = Learning(data=X) - self.Automaton = learn.LearnAutomaton(rank=self.rank, - lrows=self.lrows, lcolumns=self.lcolumns, - version=self.version, - partial=self.partial, - sparse=self.sparse, - mode_quiet = self.mode_quiet) + self._hankel = Hankel(sample_instance=X, + lrows=self.lrows, lcolumns=self.lcolumns, + version=self.version, + partial=self.partial, sparse=self.sparse, + mode_quiet=self.mode_quiet) + self._automaton = self._hankel.to_automaton(self.rank, self.mode_quiet) # for smooth option compute trigram dictionnary if self.smooth == 1: self.trigram = self._threegramdict(X.sample) @@ -421,10 +437,13 @@ class Spectral(BaseEstimator): """ check_array(X) - if not hasattr(self, 'Automaton'): + if not hasattr(self, 'automaton'): raise NotFittedError("This %(name)s instance is not fitted " "yet" % {'name': type(self).__name__}) - if self.Automaton is None: + if self._automaton is None: + print("No Automaton has been computed, " + "check the format of the input fit data") + warnings.warn("check the format of the input fit data", UserWarning) return X Y = self.predict_proba(X) @@ -447,12 +466,12 @@ class Spectral(BaseEstimator): """ #check_is_fitted(self, "classes_") X = check_array(X) - if not hasattr(self, 'Automaton'): + if not hasattr(self, 'automaton'): raise NotFittedError("This %(name)s instance is not fitted " "yet" % {'name': type(self).__name__}) # if Automaton is None because the fit pass through doing nothing - if self.Automaton is None: + if self._automaton is None: print("No Automaton has been computed, " "check the format of the input fit data") warnings.warn("check the format of the input fit data", UserWarning) @@ -472,10 +491,10 @@ class Spectral(BaseEstimator): w = X[line, :] w = w[w >= 0] w = tuple([int(x) for x in w[0:]]) - val = self.Automaton.val(w) + val = self._automaton.val(w) if self.smooth == 1 and val <= 0: - Y[i] = self._trigramprobability(w, trigram_test) - trigram_index[i] = True + Y[i] = self._trigramprobability(w, trigram_test) + trigram_index[i] = True else: Y[i] = val i += 1 @@ -569,214 +588,4 @@ class Spectral(BaseEstimator): raise ValueError(msg) return perplexity else: - return - self.loss(X, y, normalize=True) - -class Learning(object): - """ A learning instance - - :Example: - - >>> from splearn.spectral import Learning, Spectral - >>> train_file = '0.spice.train' - >>> from splearn.datasets.base import load_data_sample - >>> from splearn.tests.datasets.get_dataset_path import get_dataset_path - >>> train_file = '3.pautomac_light.train' # '4.spice.train' - >>> data_sample = load_data_sample(adr=get_dataset_path(train_file)) - >>> from splearn.spectral import Learning, Spectral - >>> cl = Spectral() - >>> cl.set_params(partial=True, lcolumns=7, lrows=7) - Spectral(lcolumns=[], lrows=[], partial=True, rank=5, sparse=False, version='classic') - >>> cl.fit(data_sample.data) - >>> S_app = Learning(data=data_sample.data) - - - Input: - - :param Splearn_array data: an instance of Splearn_array - (ndarray, nbL, nbEx, and dictionaries ) - - """ - - def __init__(self, data): - - # # Size of the alphabet - # self.nbL = sample_instance.nbL - # # Number of samples - # self.nbEx = sample_instance.nbEx - # # The dictionary that contains the sample - # self.sample = sample_instance.sample - # # The dictionary that contains the prefixes - # self.pref = sample_instance.pref - # # The dictionary that contains the suffixes - # self.suff = sample_instance.suff - # # The dictionary that contains the factors - # self.fact = sample_instance.fact - # The pLearn that contains Samples dictionaries - self.splearn_object = data - - @property - def splearn_object(self): - """Sample object, contains dictionaries""" - - return self._splearn_object - - @splearn_object.setter - def splearn_object(self, splearn_object): - if not isinstance(splearn_object, Splearn_array): - raise TypeError("sample_object should be a Splearn_array") - self._splearn_object = splearn_object - - @staticmethod - def BuildAutomatonFromHankel(lhankel, nbL, rank, mode_quiet, - sparse=True, version='classic', - ): - """ Build an automaton from Hankel matrix - - - Input: - - :param list lhankel: list of Hankel matrix - :param int nbL: the number of letters - :param int rank: the ranking number - :param boolean sparse: (default value = False) True if Hankel - matrix is sparse - :param version: version of Automaton - :param boolean mode_quiet: True for no output message. - - - Output: - - :returns: An automaton instance - :rtype: Automaton - """ - if not mode_quiet: - print("Start Building Automaton from Hankel matrix") - if not sparse: - hankel = lhankel[0] - [u, s, v] = np.linalg.svd(hankel) - u = u[:, :rank] - v = v[:rank, :] - # ds = np.zeros((rank, rank), dtype=complex) - ds = np.diag(s[:rank]) - pis = np.linalg.pinv(v) - del v - pip = np.linalg.pinv(np.dot(u, ds)) - del u, ds - init = np.dot(hankel[0, :], pis) - term = np.dot(pip, hankel[:, 0]) - trans = [] - for x in range(nbL): - hankel = lhankel[x+1] - trans.append(np.dot(pip, np.dot(hankel, pis))) - - else: - hankel = lhankel[0] - [u, s, v] = lin.svds(hankel, k=rank) - ds = np.diag(s) - pis = np.linalg.pinv(v) - del v - pip = np.linalg.pinv(np.dot(u, ds)) - del u, ds - init = hankel[0, :].dot(pis)[0, :] - term = np.dot(pip, hankel[:, 0].toarray())[:, 0] - trans = [] - for x in range(nbL): - hankel = lhankel[x+1] - trans.append(np.dot(pip, hankel.dot(pis))) - - A = Automaton(nbL=nbL, nbS=rank, initial=init, final=term, - transitions=trans, type=version) - # ms=np.linalg.pinv(vt) - # init=h.dot(ms)/self.nbEx - # print(type(init),init.shape) - # mp=np.linalg.pinv(u.dot(np.diag(s))) - # v=v.todense() - # term=np.dot(mp,v) - # trans=[] # a suivre - if not mode_quiet: - print ("End of Automaton computation") - return A - - def LearnAutomaton(self, rank, lrows=[], lcolumns=[], version="classic", - partial=False, sparse=False, - mode_quiet=False): - """ Learn Automaton from sample - - - Input: - - :param int rank: the ranking number - :param lrows: number or list of rows, - a list of strings if partial=True; - otherwise, based on self.pref if version="classic" or - "prefix", self.fact otherwise - :type lrows: int or list of int - :param lcolumns: number or list of columns - a list of strings if partial=True ; - otherwise, based on self.suff if version="classic" or "suffix", - self.fact otherwise - :type lcolumns: int or list of int - :param string version: (default = "classic") version name - :param boolean partial: (default value = False) build - of partial Hankel matrix - :param boolean sparse: (default value = False) True if Hankel - matrix is sparse - :param boolean mode_quiet: (default value = False) True for no - output message. - - - Output: - - :returns: An automaton instance - :rtype: Automaton - - """ - lhankel = Hankel(sample_instance=self.splearn_object, - lrows=lrows, lcolumns=lcolumns, - version=version, - partial=partial, sparse=sparse, - mode_quiet=mode_quiet).lhankel - matrix_shape =min(lhankel[0].shape) - if (min(lhankel[0].shape) < rank) : - raise ValueError("The value of parameter rank ("+ str(rank) - + ") should be less than " + - "the smaller dimension of the Hankel Matrix (" + - str(matrix_shape) + ")") - A = self.BuildAutomatonFromHankel(lhankel=lhankel, - nbL=self.splearn_object.nbL, - rank=rank, - sparse=sparse, - version=version, - mode_quiet=mode_quiet) - - - A.initial = A.initial / self.splearn_object.nbEx - if version == "prefix": - A = A.transformation(source="prefix", target="classic") - if version == "factor": - A = A.transformation(source="factor", target="classic") - if version == "suffix": - A = A.transformation(source="suffix", target="classic") - return A - -# @staticmethod -# def Perplexity(A, adr): -# """ Perplexity calculation """ -# Cible = Automaton.load_Spice_Automaton("./" + adr + ".target") -# Test = Learning(adr="./"+adr+".test") -# sA, sC = 0, 0 -# for w in Test.sample_object.sample: -# sA = sA + abs(A.val(w)) -# sC = sC + abs(Cible.val(w)) -# s = 0 -# for w in Test.sample_object.sample: -# s = s + Cible.val(w)/sC*math.log(abs(A.val(w))/sA) -# p = math.exp(-s) -# return p - -# if __name__ == '__main__': -# from sksplearn.datasets.get_dataset_path import get_dataset_path -# adr = get_dataset_path("essai") -# P = Learning(adr=adr, type='SPiCe') -# -# print("nbL = " + str(P.nbL)) -# print("nbEx = " + str(P.nbEx)) -# print("samples = " + str(P.sample)) -# print("prefixes = " + str(P.pref)) -# print("suffixes = " + str(P.suff)) -# print("factors = " + str(P.fact)) + return - self.loss(X, y, normalize=True) \ No newline at end of file diff --git a/examples/graphivz-output/3.pautomac_light_man.train.gv b/splearn/tests/datasets/3.pautomac_light.train.gv similarity index 89% rename from examples/graphivz-output/3.pautomac_light_man.train.gv rename to splearn/tests/datasets/3.pautomac_light.train.gv index d0bd11b3045b9f722f9097bc03efa9c2b894cf26..630afa0c022bca2912dd7c273fa6247b5aacb683 100644 --- a/examples/graphivz-output/3.pautomac_light_man.train.gv +++ b/splearn/tests/datasets/3.pautomac_light.train.gv @@ -1,24 +1,17 @@ -//automata.dot +//3.pautomac_light.train.dot digraph { 0 [label="0 -______ -> -0.00 -0.08 >"] +______"] 1 [label="1 -______ -> 0.00 --0.02 >"] +______"] 2 [label="2 ______ -> -0.04 -0.45 >"] 3 [label="3 ______ -> -0.11 0.63 >"] 4 [label="4 ______ -> -0.09 -0.55 >"] 0 -> 1 [label="0:-0.24"] 0 -> 2 [label="0:0.35"] diff --git a/splearn/tests/test_automaton.py b/splearn/tests/test_automaton.py index 69dd1e84ef323b7219b5220f40709bbabbc92619..72416fa6f960957bd08abf0a4f778ad8350b6b5b 100644 --- a/splearn/tests/test_automaton.py +++ b/splearn/tests/test_automaton.py @@ -6,7 +6,8 @@ import math from splearn.automaton import Automaton from splearn.tests.datasets.get_dataset_path import get_dataset_path - +from splearn.datasets.base import load_data_sample +from splearn import Spectral class UnitaryTest(unittest.TestCase): @@ -259,9 +260,9 @@ class UnitaryTest(unittest.TestCase): A = Automaton(nbl, nbe, i, t, trans) np.testing.assert_almost_equal(A.sum(), 1) - def test_load_Spice_Automaton(self): + def test_load_Pautomac_Automaton(self): adr = get_dataset_path("pautomac3.txt") - A = Automaton.load_Spice_Automaton(adr) + A = Automaton.load_Pautomac_Automaton(adr) r = A.nbL, A.nbS self.assertEqual(r, (4, 25)) # à compléter @@ -289,6 +290,20 @@ class UnitaryTest(unittest.TestCase): dic = A.calc_prefix_completion_weights([-1]) self.assertEqual(set(dic.keys()), {0, 1, -1}, "test_calc_prefix_completion_weights failed") + + def test_get_dot(self): + train_file = '3.pautomac_light.train' + data = load_data_sample(adr=get_dataset_path(train_file)) + sp = Spectral() + sp.fit(X=data.data) + dotfile = "3.pautomac_light.train.dot" + dot = sp.automaton.get_dot(threshold = 0.2, title = dotfile) + print(dot) + gold_file = train_file + ".gv" + gold_str = "" + with open(get_dataset_path(gold_file), 'r') as f: + gold_str = f.read() + self.assertEqual(dot, gold_str, "test_get_dot failed") if __name__ == '__main__': unittest.main() diff --git a/splearn/tests/test_hankel.py b/splearn/tests/test_hankel.py index 8e6e34eb0fe46eb30ae56dbd9cd3d0d0ed287b30..10574395b3a4539cbf72cc94cf44b7b42e952b09 100644 --- a/splearn/tests/test_hankel.py +++ b/splearn/tests/test_hankel.py @@ -5,7 +5,7 @@ import unittest from splearn.datasets.base import load_data_sample from splearn.automaton import Automaton -from splearn.spectral import Spectral, Learning +from splearn.spectral import Spectral from splearn.hankel import Hankel from splearn.tests.datasets.get_dataset_path import get_dataset_path import numpy as np diff --git a/splearn/tests/test_spectral.py b/splearn/tests/test_spectral.py index ce71a715785c9a240107f8d89f860ae288e1ecc0..20ab7ec68def9acc35e94dd00377c5859ddb610e 100644 --- a/splearn/tests/test_spectral.py +++ b/splearn/tests/test_spectral.py @@ -5,7 +5,7 @@ import numpy as np import unittest from splearn.datasets.base import load_data_sample from splearn.automaton import Automaton -from splearn.spectral import Learning, Spectral +from splearn.spectral import Spectral from splearn.tests.datasets.get_dataset_path import get_dataset_path @@ -17,14 +17,14 @@ class UnitaryTest(unittest.TestCase): cl = Spectral(partial=False, sparse=False, version="prefix") X = data.data cl.fit(X=X) - np.testing.assert_almost_equal(cl.Automaton.initial, + np.testing.assert_almost_equal(cl.automaton.initial, np.array([-1.21159819e+00, -1.78488894e-01, 1.30974719e-02, -1.66533454e-16, -3.70074342e-17])) cl = Spectral(partial=False, sparse=False, version="suffix") cl.fit(X=X) - np.testing.assert_almost_equal(cl.Automaton.initial, + np.testing.assert_almost_equal(cl.automaton.initial, np.array( [-3.16481225e-01, -1.95397796e-01, 2.89623130e-01, 1.85499764e-15, @@ -32,7 +32,7 @@ class UnitaryTest(unittest.TestCase): cl = Spectral(partial=False, sparse=False, version="factor", mode_quiet=False) cl.fit(X=X) - np.testing.assert_almost_equal(cl.Automaton.final, + np.testing.assert_almost_equal(cl.automaton.final, np.array([ -4.35303631e-01, 1.49070953e+00, 4.80783716e-01, @@ -55,7 +55,7 @@ class UnitaryTest(unittest.TestCase): self.assertEqual(param.get("partial"),False) X = np.array([[1, 2, 3, 6],[0, 0, 2, 5]]) cl.fit(X=X) - self.assertEqual(cl.Automaton, None) + self.assertEqual(cl.automaton, None) def test_fit_spectral_notsparse(self): adr = get_dataset_path("essai") @@ -63,7 +63,7 @@ class UnitaryTest(unittest.TestCase): data = load_data_sample(adr=adr) cl = Spectral(partial=False, sparse=False) cl.fit(X=data.data) - self.assertIsInstance(cl.Automaton,Automaton,"fit function fails") + self.assertIsInstance(cl.automaton,Automaton,"fit function fails") def test_fit_spectral_sparse(self): adr = get_dataset_path("essai") @@ -71,7 +71,7 @@ class UnitaryTest(unittest.TestCase): data = load_data_sample(adr=adr) cl = Spectral(partial=False, sparse=True, rank=4) cl.fit(X=data.data) - self.assertIsInstance(cl.Automaton,Automaton,"fit function fails") + self.assertIsInstance(cl.automaton,Automaton,"fit function fails") def test_predict_spectral(self): @@ -108,28 +108,6 @@ class UnitaryTest(unittest.TestCase): self.assertAlmostEquals(p, -1.7917594692280561) - def test_constructor_learning(self): - adr = get_dataset_path("essai") - # adr = get_dataset_path("3.pautomac.train") - data = load_data_sample(adr=adr) - cl = Spectral() - cl._polulate_dictionnaries(data.data) - # adr = get_dataset_path("essai") - L = Learning(data=data.data) - with self.assertRaises(TypeError): - data.nbL = 2.0 - # nbEx assignment - with self.assertRaises(TypeError): - data.nbEx = 6.0 - # # sample assignment - # self.assertRaises(TypeError, L.splearn_object.sample, "sample") - # # pref assignment - # self.assertRaises(TypeError, L.splearn_object.pref, "pref") - # # suff assignment - # self.assertRaises(TypeError, L.splearn_object.suff, "suff") - # # fact assignment - # self.assertRaises(TypeError, L.splearn_object.fact, "fact") - def test_trigram(self): adr = get_dataset_path("3.pautomac.train") data = load_data_sample(adr=adr) @@ -216,9 +194,8 @@ class UnitaryTest(unittest.TestCase): def test_BuildAutomatonFromHankel(self): # a suivre A = Automaton.SimpleExample() - lh = A.BuildHankels([(), (0,), (1,)], [(), (0,), (1,)]) - B = Learning.BuildAutomatonFromHankel(lh, nbL=2, rank=2, sparse=False, - mode_quiet=False) + H = A.to_hankel([(), (0,), (1,)], [(), (0,), (1,)], mode_quiet=False) + B = H.to_automaton(rank=2, mode_quiet=False) np.testing.assert_almost_equal(A.val([]), B.val([])) np.testing.assert_almost_equal(A.val([0]), B.val([0])) np.testing.assert_almost_equal(A.val([0, 0]), B.val([0, 0]))