From 99e4a6ba21c7f0dddd6488df84bf513bf1ed834d Mon Sep 17 00:00:00 2001
From: Caroline DE POURTALES <cdepourt@montana.irit.fr>
Date: Tue, 5 Apr 2022 14:53:49 +0200
Subject: [PATCH] can upload an skl tree

---
 .gitignore                                    |   8 +-
 assets/header.css                             |  11 +-
 callbacks.py                                  |  40 ++--
 callbacks_detached.py                         | 172 ++++++++++++++++++
 .../DecisionTree/DecisionTreeComponent.py     |  88 +++++----
 pages/application/DecisionTree/utils/data.py  |   6 +-
 pages/application/DecisionTree/utils/dtree.py |  14 +-
 pages/application/DecisionTree/utils/dtviz.py |  10 +-
 .../DecisionTree/utils/upload_tree.py         |   5 +-
 pages/application/application.py              | 130 +++++++------
 requirements.txt                              |   5 +-
 tests/adult/adult.pkl                         | Bin 0 -> 3033 bytes
 tests/adult/adult_2.pkl                       | Bin 0 -> 2166 bytes
 tests/adult/adult_3.pkl                       | Bin 0 -> 1734 bytes
 tests/adult/adult_data_00000.inst             |   1 +
 .../decision_tree_classifier_20170212.pkl     | Bin 0 -> 1916 bytes
 tests/iris/iris.csv                           | 151 +++++++++++++++
 tests/iris/iris.pkl                           | Bin 0 -> 2047 bytes
 tests/iris/iris.txt                           |   4 +
 tests/iris/iris01.json                        |   4 +
 tests/iris/iris2.pkl                          | Bin 0 -> 2047 bytes
 tests/iris/iris_00000.txt                     |   1 +
 tests/zoo/inst/zoo_00.inst                    |   1 +
 tests/zoo/inst/zoo_01.inst                    |   1 +
 tests/zoo/inst/zoo_02.inst                    |   1 +
 tests/zoo/inst/zoo_11.inst                    |   1 +
 tests/zoo/zoo.csv                             | 102 +++++++++++
 tests/zoo/zoo.dt                              |  85 +++++++++
 tests/zoo/zoo.json                            |  18 ++
 tests/zoo/zoo.map                             |  38 ++++
 tests/zoo/zoo.pkl                             | Bin 0 -> 2303 bytes
 utils.py                                      |  13 +-
 32 files changed, 756 insertions(+), 154 deletions(-)
 create mode 100644 callbacks_detached.py
 create mode 100644 tests/adult/adult.pkl
 create mode 100644 tests/adult/adult_2.pkl
 create mode 100644 tests/adult/adult_3.pkl
 create mode 100644 tests/adult/adult_data_00000.inst
 create mode 100644 tests/iris/decision_tree_classifier_20170212.pkl
 create mode 100644 tests/iris/iris.csv
 create mode 100644 tests/iris/iris.pkl
 create mode 100644 tests/iris/iris.txt
 create mode 100644 tests/iris/iris01.json
 create mode 100644 tests/iris/iris2.pkl
 create mode 100644 tests/iris/iris_00000.txt
 create mode 100644 tests/zoo/inst/zoo_00.inst
 create mode 100644 tests/zoo/inst/zoo_01.inst
 create mode 100644 tests/zoo/inst/zoo_02.inst
 create mode 100644 tests/zoo/inst/zoo_11.inst
 create mode 100644 tests/zoo/zoo.csv
 create mode 100644 tests/zoo/zoo.dt
 create mode 100644 tests/zoo/zoo.json
 create mode 100644 tests/zoo/zoo.map
 create mode 100644 tests/zoo/zoo.pkl

diff --git a/.gitignore b/.gitignore
index 7c8041b..f7db4b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,10 +3,4 @@ __pycache__
 pages/application/DecisionTree/utils/__pycache__
 pages/application/DecisionTree/__pycache__
 pages/application/__pycache__
-decision_tree_classifier_20170212.pkl
-push_command
-adult.pkl
-adult_data_00000.inst
-iris_00000.txt
-tests
-create_pkl.py
\ No newline at end of file
+tests/push_command
\ No newline at end of file
diff --git a/assets/header.css b/assets/header.css
index 8da0dd9..3a882ea 100644
--- a/assets/header.css
+++ b/assets/header.css
@@ -36,6 +36,12 @@ div.sidebar.col-3 {
     background-color:gray;
   }
 
+.sidebar .check-boxes{
+    width: 100%;
+    height: 40px;
+    text-align: center;
+}
+
 .sidebar .upload {
         width: 100%;
         height: 50px;
@@ -49,7 +55,6 @@ div.sidebar.col-3 {
 
 .sidebar .Select-control {
     width: 100%;
-    height: 30px;
     line-height: 30px;
     border-width: 1px;
     border-radius: 5px;
@@ -60,9 +65,9 @@ div.sidebar.col-3 {
     background-color: rgb(26,26,26);
 }
 
-.sidebar .sidebar-dropdown{
+.sidebar .dropdown{
     width: 100%;
-    height: 40px;
+    height: 30px;
     line-height: 30px;
     border-width: 1px;
     border-radius: 5px;
diff --git a/callbacks.py b/callbacks.py
index 04930d6..5ef62d6 100644
--- a/callbacks.py
+++ b/callbacks.py
@@ -54,10 +54,13 @@ def register_callbacks(page_home, page_course, page_application, app):
         if ctx.triggered:
             ihm_id = ctx.triggered[0]['prop_id'].split('.')[0]
             model_application = page_application.model
+
+            # Choice of model
             if ihm_id == 'ml_model_choice' :
-                    model_application.update_ml_model(value_ml_model)
-                    return None, None, None, None, None
+                model_application.update_ml_model(value_ml_model)
+                return None, None, None, None, None
 
+            # Choice of pkl pretrained model
             elif ihm_id == 'ml_pretrained_model_choice':
                 if model_application.ml_model is None :
                     raise PreventUpdate
@@ -69,6 +72,7 @@ def register_callbacks(page_home, page_course, page_application, app):
                 else :
                     return pretrained_model_filename, None, None, None, None
 
+            # Choice of information for the model
             elif ihm_id == 'model_info_choice':
                 if model_application.ml_model is None :
                     raise PreventUpdate
@@ -76,36 +80,38 @@ def register_callbacks(page_home, page_course, page_application, app):
                 model_application.update_pretrained_model_layout_with_info(model_info, model_info_filename)
                 return pretrained_model_filename, model_info_filename, None, model_application.component.network, None
 
+            # Choice of instance to explain
             elif ihm_id == 'ml_instance_choice' :
-                if model_application.ml_model is None or model_application.pretrained_model is None :
+                if model_application.ml_model is None or model_application.pretrained_model is None or model_application.enum<=0 or model_application.xtype is None :
                     raise PreventUpdate
                 instance = parse_contents_instance(instance_contents, instance_filename)
-                model_application.update_instance(instance, enum, xtype)
+                model_application.update_instance(instance)
                 return pretrained_model_filename, model_info_filename, instance_filename, model_application.component.network, model_application.component.explanation   
 
+            # Choice of number of expls
             elif ihm_id == 'number_explanations' :
-                if model_application.ml_model is None or model_application.pretrained_model is None or model_application.instance is None:
+                if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.xtype is None:
                     raise PreventUpdate
-                instance = parse_contents_instance(model_application.instance, instance_filename)
-                model_application.update_instance(instance, enum, xtype)
+                model_application.update_enum(enum)
                 return pretrained_model_filename, model_info_filename, instance_filename, model_application.component.network, model_application.component.explanation   
 
+            # Choice of AxP or CxP
             elif ihm_id == 'explanation_type' :
-                if model_application.ml_model is None or model_application.pretrained_model is None or model_application.instance is None:
+                if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 :
                     raise PreventUpdate
-                instance = parse_contents_instance(model_application.instance, instance_filename)
-                model_application.update_instance(instance, enum, xtype)
+                model_application.update_xtype(xtype)
                 return pretrained_model_filename, model_info_filename, instance_filename, model_application.component.network, model_application.component.explanation
             
+            # Choice of solver 
             elif ihm_id == 'solver_sat' :
-                if model_application.ml_model is None or model_application.pretrained_model is None or model_application.instance is None:
+                if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 or len(model_application.xtype)==0:
                     raise PreventUpdate
-                instance = parse_contents_instance(model_application.instance, instance_filename)
-                model_application.update_instance(instance, enum, xtype, solver=solver)
+                model_application.update_solver(solver)
                 return pretrained_model_filename, model_info_filename, instance_filename, model_application.component.network, model_application.component.explanation             
             
+            # Choice of AxP to draw
             elif ihm_id == 'expl_choice' :
-                if instance_contents is None :
+                if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 or len(model_application.xtype)==0:
                     raise PreventUpdate
                 model_application.update_expl(expl_choice)
                 return pretrained_model_filename, model_info_filename, instance_filename, model_application.component.network, model_application.component.explanation             
@@ -133,14 +139,14 @@ def register_callbacks(page_home, page_course, page_application, app):
             return False, False, False, options
 
     @app.callback(
-        Output('model_info_choice', 'disabled'),
-        Input('add_info_model_choice', 'value'),
+        Output('choice_info_div', 'hidden'),
+        Input('add_info_model_choice', 'on'),
         prevent_initial_call=True
     )
     def add_model_info(add_info_model_choice):
         model_application = page_application.model
         model_application.update_info_needed(add_info_model_choice)
-        if add_info_model_choice==1:
+        if add_info_model_choice:
             return False
         else :
             return True
diff --git a/callbacks_detached.py b/callbacks_detached.py
new file mode 100644
index 0000000..a8db16f
--- /dev/null
+++ b/callbacks_detached.py
@@ -0,0 +1,172 @@
+import dash
+import pandas as pd
+from dash import Input, Output, State
+from dash.dependencies import Input, Output, State
+from dash.exceptions import PreventUpdate
+
+from utils import parse_contents_graph, parse_contents_instance, parse_contents_data
+
+
+def register_callbacks(page_home, page_course, page_application, app):
+    page_list = ['home', 'course', 'application']
+
+    @app.callback(
+        Output('page-content', 'children'),
+        Input('url', 'pathname'))
+    def display_page(pathname):
+        if pathname == '/':
+            return page_home
+        if pathname == '/application':
+            return page_application.view.layout        
+        if pathname == '/course':
+            return page_course
+
+    @app.callback(Output('home-link', 'active'),
+                Output('course-link', 'active'),
+                Output('application-link', 'active'),
+                Input('url', 'pathname'))
+    def navbar_state(pathname):
+        active_link = ([pathname == f'/{i}' for i in page_list])
+        return active_link[0], active_link[1], active_link[2]
+
+    @app.callback(
+        Output('graph', 'children'),
+        Input('ml_model_choice', 'value'),
+        prevent_initial_call=True
+    )
+    def update_ml_type(value_ml_model):
+        model_application = page_application.model
+        model_application.update_ml_model(value_ml_model)
+        return None
+
+    @app.callback(
+        Output('pretrained_model_filename', 'children'),
+        Output('graph', 'children'),
+        Input('ml_pretrained_model_choice', 'contents'),
+        State('ml_pretrained_model_choice', 'filename'),
+        prevent_initial_call=True
+    )
+    def update_ml_pretrained_model(pretrained_model_contents, pretrained_model_filename):
+        model_application = page_application.model
+        if model_application.ml_model is None :
+            raise PreventUpdate
+        graph = parse_contents_graph(pretrained_model_contents, pretrained_model_filename)
+        model_application.update_pretrained_model(graph)
+        if not model_application.add_info :
+            model_application.update_pretrained_model_layout()
+            return pretrained_model_filename, model_application.component.network
+        else :
+            return pretrained_model_filename, None
+
+    @app.callback(
+        Output('info_filename', 'children'),
+        Output('graph', 'children'),
+        Input('model_info_choice', 'contents'),
+        State('model_info_choice', 'filename'),
+        prevent_initial_call=True
+    )
+    def update_info_model(model_info, model_info_filename):
+        model_application = page_application.model
+        if model_application.ml_model is None :
+            raise PreventUpdate
+        model_info = parse_contents_data(model_info, model_info_filename)
+        model_application.update_pretrained_model_layout_with_info(model_info, model_info_filename)
+        return model_info_filename, model_application.component.network
+
+    @app.callback(
+        Output('instance_filename', 'children'),
+        Output('graph', 'children'),
+        Output('explanation', 'children'),
+        Input('ml_instance_choice', 'contents'),
+        State('ml_instance_choice', 'filename'),
+        prevent_initial_call=True
+    )
+    def update_instance(instance_contents, instance_filename):
+        model_application = page_application.model
+        if model_application.ml_model is None or model_application.pretrained_model is None or model_application.enum<=0 or model_application.xtype is None :
+            raise PreventUpdate
+        instance = parse_contents_instance(instance_contents, instance_filename)
+        model_application.update_instance(instance)
+        return instance_filename, model_application.component.network, model_application.component.explanation   
+
+    @app.callback(
+        Output('explanation', 'children'),
+        Input('number_explanations', 'value'),
+        prevent_initial_call=True
+    )
+    def update_enum(enum):
+        model_application = page_application.model
+        if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.xtype is None:
+            raise PreventUpdate
+        model_application.update_enum(enum)
+        return model_application.component.explanation   
+
+    @app.callback(
+        Output('explanation', 'children'),
+        Input('explanation_type', 'value'),
+        prevent_initial_call=True
+    )
+    def update_xtype(xtype):
+        model_application = page_application.model
+        if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 :
+            raise PreventUpdate
+        model_application.update_xtype(xtype)
+        return  model_application.component.explanation
+    
+    @app.callback(
+    Output('explanation', 'children'),
+    Input('solver_sat', 'value'),
+    prevent_initial_call=True
+)
+    def update_solver(solver):
+        model_application = page_application.model
+        if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 or len(model_application.xtype)==0:
+            raise PreventUpdate
+        model_application.update_solver(solver)
+        return model_application.component.explanation             
+    
+    @app.callback(
+        Output('graph', 'children'),
+        Input('expl_choice', 'value'),
+        prevent_initial_call=True
+    )
+    def update_expl_choice( expl_choice):
+        model_application = page_application.model
+        if model_application.ml_model is None or model_application.pretrained_model is None or len(model_application.instance)==0 or model_application.enum<=0 or len(model_application.xtype)==0:
+            raise PreventUpdate
+        model_application.update_expl(expl_choice)
+        return model_application.component.network            
+
+    @app.callback(
+        Output('explanation', 'hidden'),
+        Output('navigate_label', 'hidden'),    
+        Output('navigate_dropdown', 'hidden'),
+        Output('expl_choice', 'options'),
+        Input('explanation', 'children'),
+        Input('explanation_type', 'value'),
+        prevent_initial_call=True
+    )
+    def layout_buttons_navigate_expls(explanation, explanation_type):
+        if explanation is None or len(explanation_type)==0:
+            return True, True, True, {}
+        elif "AXp" not in explanation_type and "CXp" in explanation_type:
+            return False, True, True, {}
+        else : 
+            options = {}
+            model_application = page_application.model
+            for i in range (len(model_application.list_expls)):
+                options[str(model_application.list_expls[i])] = model_application.list_expls[i]
+            return False, False, False, options
+
+    @app.callback(
+        Output('choice_info_div', 'hidden'),
+        Input('add_info_model_choice', 'on'),
+        prevent_initial_call=True
+    )
+    def add_model_info(add_info_model_choice):
+        model_application = page_application.model
+        model_application.update_info_needed(add_info_model_choice)
+        if add_info_model_choice:
+            return False
+        else :
+            return True
diff --git a/pages/application/DecisionTree/DecisionTreeComponent.py b/pages/application/DecisionTree/DecisionTreeComponent.py
index a1e2363..8a56c32 100644
--- a/pages/application/DecisionTree/DecisionTreeComponent.py
+++ b/pages/application/DecisionTree/DecisionTreeComponent.py
@@ -15,27 +15,32 @@ from pages.application.DecisionTree.utils.dtviz import (visualize,
 
 class DecisionTreeComponent():
 
-    def __init__(self, tree, info=None, type_info=''):
-
+    def __init__(self, tree, type_tree='SKL', info=None, type_info=''):
 
         if info is not None and '.csv' in type_info: 
             self.categorical = True
             data = Data(info)
             fvmap = data.mapping_features()
             feature_names = data.names[:-1]
-
-            self.uploaded_dt = UploadedDecisionTree(tree, 'SKL', maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
+            self.uploaded_dt = UploadedDecisionTree(tree, type_tree, maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
             self.dt_format, self.map, features_names_mapping = self.uploaded_dt.dump(fvmap, feat_names=feature_names)
-            self.mapping_instance = self.create_fvmap_inverse_with_info(features_names_mapping)
-
 
         elif info is not None and '.txt' in type_info :
             self.categorical = True
             fvmap = {}
-
-            self.uploaded_dt = UploadedDecisionTree(tree, 'SKL', maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
+            feature_names = []
+            for i,line in enumerate(info.split('\n')):
+                fid, TYPE = line.split(',')[:2]
+                dom = line.split(',')[2:]
+                assert (fid not in feature_names)
+                feature_names.append(fid)
+                assert (TYPE in ['Binary', 'Categorical'])
+                fvmap[f'f{i}'] = dict()
+                dom = sorted(dom)
+                for j,v in enumerate(dom):
+                    fvmap[f'f{i}'][j] = (fid, True, v)
+            self.uploaded_dt = UploadedDecisionTree(tree, type_tree, maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
             self.dt_format, self.map, features_names_mapping = self.uploaded_dt.dump(fvmap, feat_names=feature_names)
-            self.mapping_instance = self.create_fvmap_inverse_with_info(features_names_mapping)
 
         else : 
             self.categorical = False
@@ -43,42 +48,46 @@ class DecisionTreeComponent():
                 feature_names = tree.feature_names_in_
             except:
                 feature_names = [f'f{i}' for i in range(tree.n_features_in_)]
-
-            self.uploaded_dt = UploadedDecisionTree(tree, 'SKL', maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
+            self.uploaded_dt = UploadedDecisionTree(tree, type_tree, maxdepth=tree.get_depth(), feature_names=feature_names, nb_classes=tree.n_classes_)
             self.dt_format, self.map, features_names_mapping = self.uploaded_dt.convert_dt(feat_names=feature_names)
-            self.mapping_instance = self.create_fvmap_inverse_threashold(features_names_mapping)
-
-        self.dt = DecisionTree(from_dt=self.dt_format, mapfile = self.map)
+        
+        self.mapping_instance = self.create_fvmap_inverse(features_names_mapping)
+        self.dt = DecisionTree(from_dt=self.dt_format, mapfile = self.map, feature_names = feature_names)
         dot_source = visualize(self.dt)
-        self.network = [dbc.Row(dash_interactive_graphviz.DashInteractiveGraphviz(dot_source=dot_source, style = {"width": "60%",
+        self.network = html.Div([dash_interactive_graphviz.DashInteractiveGraphviz(dot_source=dot_source, style = {"width": "60%",
                                                                                                                 "height": "90%",
-                                                                                                                "background-color": "transparent"}))]
+                                                                                                                "background-color": "transparent"})])
         self.explanation = []
 
 
-    def create_fvmap_inverse_with_info(self, features_names_mapping) :
-        mapping_instance = {}
-        for feat in features_names_mapping :
-            feat_dic = {}
-            feature_description = feat.split(',')
-            name_feat, id_feat =  feature_description[1].split(':')
+    def create_fvmap_inverse(self, instance):
+        def create_fvmap_inverse_with_info(features_names_mapping) :
+            mapping_instance = {}
+            for feat in features_names_mapping :
+                feat_dic = {}
+                feature_description = feat.split(',')
+                name_feat, id_feat =  feature_description[1].split(':')
 
-            for mapping in feature_description[2:]:
-                real_value, mapped_value = mapping.split(':')
-                feat_dic[np.float32(real_value)] = int(mapped_value)
-            mapping_instance[name_feat] = feat_dic
+                for mapping in feature_description[2:]:
+                    real_value, mapped_value = mapping.split(':')
+                    feat_dic[np.float32(real_value)] = int(mapped_value)
+                mapping_instance[name_feat] = feat_dic
 
-        return mapping_instance
+            return mapping_instance
 
+        def create_fvmap_inverse_threashold(features_names_mapping) :
+            mapping_instance = {}
+            for feat in features_names_mapping :
+                feature_description = feat.split(',')
+                name_feat, id_feat =  feature_description[1].split(':')
+                mapping_instance[name_feat] = float(feature_description[2].split(':')[0])
 
-    def create_fvmap_inverse_threashold(self, features_names_mapping) :
-        mapping_instance = {}
-        for feat in features_names_mapping :
-            feature_description = feat.split(',')
-            name_feat, id_feat =  feature_description[1].split(':')
-            mapping_instance[name_feat] = float(feature_description[2].split(':')[0])
+            return mapping_instance
 
-        return mapping_instance
+        if self.categorical :
+            return create_fvmap_inverse_with_info(instance)
+        else : 
+            return create_fvmap_inverse_threashold(instance)
 
 
     def translate_instance(self, instance):
@@ -114,14 +123,17 @@ class DecisionTreeComponent():
         explanation = self.dt.explain(instance_translated, enum=enum, xtype = xtype, solver=solver)
 
         dot_source = visualize_instance(self.dt, instance_translated)
-        self.network = [dbc.Row(dash_interactive_graphviz.DashInteractiveGraphviz(
+        self.network = html.Div([dash_interactive_graphviz.DashInteractiveGraphviz(
             dot_source=dot_source, style = {"width": "50%",
                                              "height": "80%",
                                             "background-color": "transparent"}
-        ))]
+        )])
 
 
         #Creating a clean and nice text component
+        #instance plotting
+        self.explanation.append(html.H4("Instance : \n"))
+        self.explanation.append(html.P(str([str(instance[i]) for i in range (len(instance))])))
         for k in explanation.keys() :
             if k != "List of path explanation(s)":
                 if k in ["List of abductive explanation(s)","List of contrastive explanation(s)"] :
@@ -140,8 +152,8 @@ class DecisionTreeComponent():
     def draw_explanation(self, instance, expl) :
         instance = self.translate_instance(instance)
         dot_source = visualize_expl(self.dt, instance, expl)
-        self.network = [dbc.Row(dash_interactive_graphviz.DashInteractiveGraphviz(
+        self.network = html.Div([dash_interactive_graphviz.DashInteractiveGraphviz(
                                 dot_source=dot_source, 
                                 style = {"width": "50%",
                                         "height": "80%",
-                                        "background-color": "transparent"}))]
+                                        "background-color": "transparent"})])
diff --git a/pages/application/DecisionTree/utils/data.py b/pages/application/DecisionTree/utils/data.py
index d23ddb6..91aded5 100644
--- a/pages/application/DecisionTree/utils/data.py
+++ b/pages/application/DecisionTree/utils/data.py
@@ -13,17 +13,13 @@
 from __future__ import print_function
 import collections
 import itertools
-import os, pickle
+import pickle
 import six
 import gzip
 from six.moves import range
 import numpy as np
 import pandas as pd
 
-#from  sklearn.preprocessing import OneHotEncoder
-from sklearn.model_selection import train_test_split
-
-
 #
 #==============================================================================
 class Data(object):
diff --git a/pages/application/DecisionTree/utils/dtree.py b/pages/application/DecisionTree/utils/dtree.py
index 1e7e270..1eb9de3 100644
--- a/pages/application/DecisionTree/utils/dtree.py
+++ b/pages/application/DecisionTree/utils/dtree.py
@@ -45,7 +45,7 @@ class DecisionTree():
         Simple decision tree class.
     """
 
-    def __init__(self, from_dt=None, mapfile=None, verbose=0):
+    def __init__(self, from_dt=None, mapfile=None, feature_names=None, verbose=0):
         """
             Constructor.
         """
@@ -62,6 +62,7 @@ class DecisionTree():
         self.feids = {}
         self.fdoms = {}
         self.fvmap = {}
+        self.feature_names = {f'f{i}' : feature_names[i] for i, f in enumerate(feature_names)}
 
         # OHE mapping
         OHEMap = collections.namedtuple('OHEMap', ['dir', 'opp'])
@@ -145,7 +146,6 @@ class DecisionTree():
         """
             Parse feature-value mapping from a file.
         """
-
         self.fvmap = {}
 
         lines = mapfile.split('\n')
@@ -184,7 +184,6 @@ class DecisionTree():
         """
             Convert ITI trees with '!=' edges to multi-edges.
         """
-
         # new feature domains
         fdoms = collections.defaultdict(lambda: [])
 
@@ -227,7 +226,6 @@ class DecisionTree():
         """
             Traverse the tree and extract explicit paths.
         """
-
         if root in self.terms:
             # store the path
             term = self.terms[root]
@@ -242,7 +240,6 @@ class DecisionTree():
         """
             Run the tree and obtain the prediction given an input instance.
         """
-
         root = self.root_node
         depth = 0
         path = []
@@ -297,7 +294,6 @@ class DecisionTree():
             Hitting set based encoding of the problem.
             (currently not incremental -- should be fixed later)
         """
-
         sets = []
         for t, paths in self.paths.items():
             # ignoring the right class
@@ -334,12 +330,9 @@ class DecisionTree():
         """
         #contaiins all the elements for explanation
         explanation_dic = {}
-        #instance plotting
-        explanation_dic["Instance : "] = str([str(inst[i]) for i in range (len(inst))])
 
         self.feids = {f'f{i}': i for i, f in enumerate(inst)}
         inst = [(f'f{i}', int(inst[i][1])) for i,f in enumerate(inst)]
-
         path, term, depth = self.execute(inst, pathlits)
 
         #decision path
@@ -347,10 +340,8 @@ class DecisionTree():
         explanation_dic["Decision path of instance : "] = decision_path_str
         explanation_dic["Decision path length : "] = 'Path length is :'+ str(depth)
 
-
         if self.ohmap.dir:
             f2v = {fv[0]: fv[1] for fv in inst}
-
             # updating fvmap for printing ohe features
             for fo, fis in self.ohmap.dir.items():
                 self.fvmap[tuple([fo, None])] = '(' + ' AND '.join([self.fvmap[tuple([fi, f2v[fi]])] for fi in fis]) + ')'
@@ -394,7 +385,6 @@ class DecisionTree():
         """
             Enumerate contrastive explanations.
         """
-
         def process_set(done, target):
             for s in done:
                 if s <= target:
diff --git a/pages/application/DecisionTree/utils/dtviz.py b/pages/application/DecisionTree/utils/dtviz.py
index 0c940c6..21ea639 100755
--- a/pages/application/DecisionTree/utils/dtviz.py
+++ b/pages/application/DecisionTree/utils/dtviz.py
@@ -10,16 +10,14 @@
 
 #
 #==============================================================================
-from pages.application.DecisionTree.utils.dtree import DecisionTree
 import getopt
-import os
 import pygraphviz
-import sys
 
 #
 #==============================================================================
 def create_legend(g):
     legend = g.subgraphs()[-1]
+    legend.graph_attr.update(size="2,2")    
     legend.add_node("a", style = "invis")
     legend.add_node("b", style = "invis")
     legend.add_node("c", style = "invis")
@@ -50,7 +48,7 @@ def visualize(dt):
 
     # non-terminal nodes
     for n in dt.nodes:
-        g.add_node(n, label=dt.nodes[n].feat)
+        g.add_node(n, label=dt.feature_names[dt.nodes[n].feat])
         node = g.get_node(n)
         node.attr['shape'] = 'circle'
         node.attr['fontsize'] = 13
@@ -98,7 +96,7 @@ def visualize_instance(dt, instance):
 
     # non-terminal nodes
     for n in dt.nodes:
-        g.add_node(n, label=dt.nodes[n].feat)
+        g.add_node(n, label=dt.feature_names[dt.nodes[n].feat])
         node = g.get_node(n)
         node.attr['shape'] = 'circle'
         node.attr['fontsize'] = 13
@@ -156,7 +154,7 @@ def visualize_expl(dt, instance, expl):
 
     # non-terminal nodes
     for n in dt.nodes:
-        g.add_node(n, label=dt.nodes[n].feat)
+        g.add_node(n, label=dt.feature_names[dt.nodes[n].feat])
         node = g.get_node(n)
         node.attr['shape'] = 'circle'
         node.attr['fontsize'] = 13
diff --git a/pages/application/DecisionTree/utils/upload_tree.py b/pages/application/DecisionTree/utils/upload_tree.py
index b3e1297..98bf190 100644
--- a/pages/application/DecisionTree/utils/upload_tree.py
+++ b/pages/application/DecisionTree/utils/upload_tree.py
@@ -14,7 +14,6 @@ from anytree import Node, RenderTree,AsciiStyle
 import json
 import numpy as np
 import math
-import os
 import six
 
 
@@ -297,8 +296,8 @@ class UploadedDecisionTree:
         map += f"{len(self.intvs)}"
         for f in self.intvs:
             for j,t in enumerate(self.intvs[f][:-1]):
-                map += f"\n{f} {j} <={t}"
-            map += f"\n{f} {j+1} >{t}"  
+                map += f"\n{f} {j} <={np.round(float(t),4)}"
+            map += f"\n{f} {j+1} >{np.round(float(t),4)}"  
 
 
         if feat_names is not None:
diff --git a/pages/application/application.py b/pages/application/application.py
index 925a1b7..db54577 100644
--- a/pages/application/application.py
+++ b/pages/application/application.py
@@ -1,5 +1,6 @@
 from dash import dcc, html
 import dash_bootstrap_components as dbc
+import dash_daq as daq
 
 from pages.application.DecisionTree.DecisionTreeComponent import DecisionTreeComponent
 
@@ -23,6 +24,10 @@ class Model():
         self.add_info = False
         self.model_info = ''
 
+        self.enum=1
+        self.xtype = ['AXp', 'CXp']
+        self.solver="g3"
+
         self.instance = ''
 
         self.list_expls = []
@@ -47,12 +52,24 @@ class Model():
 
     def update_pretrained_model_layout_with_info(self, model_info, model_info_filename):
         self.model_info = model_info
-        self.component = self.component_class(self.pretrained_model, self.model_info, model_info_filename)
+        self.component = self.component_class(self.pretrained_model, info=self.model_info, type_info=model_info_filename)
 
-    def update_instance(self, instance, enum, xtype, solver="g3"):
+    def update_instance(self, instance):
         self.instance = instance
-        self.list_expls = self.component.update_with_explicability(self.instance, enum, xtype, solver)    
+        self.list_expls = self.component.update_with_explicability(self.instance, self.enum, self.xtype, self.solver)    
+        
+    def update_enum(self, enum):
+        self.enum = enum
+        self.list_expls = self.component.update_with_explicability(self.instance, self.enum, self.xtype, self.solver)    
         
+    def update_xtype(self, xtype):
+        self.xtype = xtype
+        self.list_expls = self.component.update_with_explicability(self.instance, self.enum, self.xtype, self.solver)    
+
+    def update_solver(self, solver):
+        self.solver = solver
+        self.list_expls = self.component.update_with_explicability(self.instance, self.enum, self.xtype, self.solver)    
+              
     def update_expl(self, expl):
         self.expl = expl
         self.component.draw_explanation(self.instance, expl)
@@ -62,17 +79,18 @@ class View():
     def __init__(self, model):
         self.model = model
 
-        self.ml_menu_models = dcc.Dropdown(self.model.ml_models, 
+        self.ml_menu_models = html.Div([                                    
+                                    html.Br(),                                
+                                    html.Label("Choose the Machine Learning algorithm :"),
+                                    html.Br(),
+                                    dcc.Dropdown(self.model.ml_models, 
                                             id='ml_model_choice',
-                                            className="sidebar-dropdown")
-
-        self.ml_library_used = dcc.Dropdown(options = [{'label': 'Scikit-learn ', 'value': "SKL"},
-                                                        {'label': 'ITI', 'value': "ITI"},
-                                                        {'label': 'IAI', 'value': "IAI"}], 
-                                            id='ml_library_choice',
-                                            className="sidebar-dropdown")
+                                            className="dropdown")])
 
         self.pretrained_model_upload = html.Div([
+                                    html.Hr(),
+                                    html.Label("Choose the pretrained model : "),
+                                    html.Br(),
                                     dcc.Upload(        
                                         id='ml_pretrained_model_choice',
                                         children=html.Div([
@@ -83,15 +101,20 @@ class View():
                                     ),
                                     html.Div(id='pretrained_model_filename')])
 
-        self.add_model_info_choice = dcc.RadioItems(id="add_info_model_choice", 
-                                                    options = [{'label': 'Yes ', 'value': 1},
-                                                               {'label': 'No', 'value': 0}], 
-                                                    value=0, className="sidebar-dropdown")
+        self.add_model_info_choice = html.Div([
+                                    html.Hr(),
+                                    html.Label("Do you wish to upload more info for your model ? : "),
+                                    html.Br(),
+                                    daq.BooleanSwitch(id='add_info_model_choice', on=False, color="#000000",)])
 
-        self.model_info = html.Div([
+        self.model_info = html.Div(id="choice_info_div",
+                                    hidden=True,
+                                    children=[
+                                    html.Hr(),
+                                    html.Label("Choose the pretrained model dataset (csv) or feature definition file (txt): "),
+                                    html.Br(),
                                     dcc.Upload(        
                                         id='model_info_choice',
-                                        disabled=True,
                                         children=html.Div([
                                             'Drag and Drop or ',
                                             html.A('Select File')
@@ -101,6 +124,9 @@ class View():
                                     html.Div(id='info_filename')])
 
         self.instance_upload = html.Div([
+                                    html.Hr(),
+                                    html.Label("Choose the instance to explain : "),
+                                    html.Br(),
                                     dcc.Upload(        
                                         id='ml_instance_choice',
                                         children=html.Div([
@@ -111,34 +137,7 @@ class View():
                                     ),
                                     html.Div(id='instance_filename')])
 
-        self.sidebar = dcc.Tabs(children=[
-                                dcc.Tab(label='Basic Parameters', children = [    
-                                    html.Br(),                                
-                                    html.Label("Choose the Machine Learning algorithm :"),
-                                    html.Br(),
-                                    self.ml_menu_models,
-                                    html.Hr(),
-                                    html.Label("Choose the Machine Learning library used :"),
-                                    html.Br(),
-                                    self.ml_library_used,
-                                    html.Hr(),
-                                    html.Label("Choose the pretrained model : "),
-                                    html.Br(),
-                                    self.pretrained_model_upload, 
-                                    html.Hr(),
-                                    html.Label("Do you wish to upload more info for your model ? : "),
-                                    html.Br(),
-                                    self.add_model_info_choice,
-                                    html.Hr(),
-                                    html.Label("Choose the pretrained model dataset (csv) or feature definition file (txt): "),
-                                    html.Br(),
-                                    self.model_info,
-                                    html.Hr(),
-                                    html.Label("Choose the instance to explain : "),
-                                    html.Br(),
-                                    self.instance_upload], className="sidebar"),
-                                dcc.Tab(label='Advanced Parameters', children = [ 
-                                    html.Br(),
+        self.num_explanation = html.Div([
                                     html.Label("Choose the number of explanations : "),
                                     html.Br(),
                                     dcc.Input(
@@ -146,29 +145,48 @@ class View():
                                         value=1,
                                         type="number",
                                         placeholder="How many explanations ?",
-                                        className="sidebar-dropdown"),
-                                    html.Hr(),
+                                        className="dropdown"),
+                                    html.Hr()])
+
+        self.type_explanation = html.Div([
                                     html.Label("Choose the kind of explanation : "),
                                     html.Br(),
                                     dcc.Checklist(
                                         id="explanation_type",
                                         options={'AXp' : "Abductive Explanation", 'CXp': "Contrastive explanation"},
                                         value = ['AXp', 'CXp'],
-                                        className="sidebar-dropdown",
+                                        className="check-boxes",
                                         inline=True),
-                                    html.Hr(),
-                                    html.Label("Choose the SAT solver : "),
+                                    html.Hr()])
+
+        self.solver = html.Div([    html.Label("Choose the SAT solver : "),
+                                    html.Br(),
+                                    dcc.Dropdown(['g3', 'g4', 'lgl', 'mcb', 'mcm', 'mpl', 'm22', 'mc', 'mgh'], 'g3', id='solver_sat') ])
+                                    
+        self.sidebar = dcc.Tabs(children=[
+                                dcc.Tab(label='Basic Parameters', children = [    
+                                    self.ml_menu_models,
+                                    self.pretrained_model_upload, 
+                                    self.add_model_info_choice,
+                                    self.model_info,
+                                    self.instance_upload], className="sidebar"),
+                                dcc.Tab(label='Advanced Parameters', children = [ 
                                     html.Br(),
-                                    dcc.Dropdown(['g3', 'g4', 'lgl', 'mcb', 'mcm', 'mpl', 'm22', 'mc', 'mgh'], 'g3', id='solver_sat') 
+                                    self.num_explanation,
+                                    self.type_explanation,
+                                    self.solver
                                 ], className="sidebar")])
                             
  
-        self.expl_choice = dcc.Dropdown(self.model.list_expls,
+        self.expl_choice = html.Div([html.H5(id = "navigate_label", hidden=True, children="Navigate through the explanations and plot them on the tree : "),
+                                    html.Div(id='navigate_dropdown', hidden=True,
+                                    children = [dcc.Dropdown(self.model.list_expls,
                                         id='expl_choice',  
-                                        className="dropdown")
+                                        className="dropdown")])])
                                             
-        self.layout = dbc.Row([ dbc.Col([self.sidebar], width=3, class_name="sidebar"), 
+        self.layout = dbc.Row([ dbc.Col([self.sidebar], 
+                                        width=3, class_name="sidebar"), 
                                 dbc.Col([dbc.Row(id = "graph", children=[]),
-                                         dbc.Row(html.Div([html.H5(id = "navigate_label", hidden=True, children="Navigate through the explanations and plot them on the tree : "),
-                                                          html.Div(self.expl_choice, id='navigate_dropdown', hidden=True)]))], width=5, class_name="column_graph"), 
+                                         dbc.Row(self.expl_choice)], 
+                                         width=5, class_name="column_graph"), 
                                 dbc.Col(html.Main(id = "explanation", children=[], hidden=True), width=4)])
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index aab2fca..4f70ae6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,5 +11,6 @@ scipy>=1.2.1
 dash_bootstrap_components
 dash_interactive_graphviz
 python-sat[pblib,aiger]
-pygraphviz
-anytree
\ No newline at end of file
+pygraphviz==1.9
+anytree==2.8.0
+dash_daq==0.5.0
\ No newline at end of file
diff --git a/tests/adult/adult.pkl b/tests/adult/adult.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..b18b4d6e77cfc501320ebd5650158dd268530d1d
GIT binary patch
literal 3033
zcmZo*nR<?!0StOXi?ef56N~cnN{Uib_2QFr5{rvdi>LI6xuhm%7H8(?g%qWxI)g+r
z(=t<wrc9oq**K+kibfA-a#3bUYEfo>-jp7e^vt}>DLov;1v!}|C8<SIdRUTDi%X{T
zaONgf#HXYdlw?ftX6X^k&CH80PRuRHNiB{CtDWM_)FXf_nv<HCHpQE@M*<{Ro|>7S
zQ4*h4l$czSnV%OAmUIWZn}LC$hX-VMT54iRX;EtN6u%xm5Fex_J}*B7EW%Tin3s~D
z8(&<KSdu!$JEcbyWNc<`L1|HDNo9OWYI0F(VsYveBs0MQ5D&3=ieC?Ba&kd@VopIu
zB9bD$ym*+k@tJw?Q@nY4xboujOG^q$ON!&Ccr*5JfQ?Blj-S#anpc`zP^p)kUzDns
zTbfgnnOIbmSUIJKC%z~(IX|zsq^LBxWXj|zJ*;4bQ+n9*QXonuPw{4Go#M=xG^Kq?
z&=d`CMsKFpDH)PItSKdx1*sq{Oqmu_Iy)e;%sq@YQ~dn=y#D|H{~t_vGn7n8>U0)>
z1sEfgF{NZm63D@DH)e?SuoWjK<|KlmH$&B#g9)axZA#FT9$rwm!aWS~Eh{Lo#82rF
zLQOm%(J4JFpcFG@@)U2L42cYBkQXv!Ax>oUW^A33q1uULLCKUPZ$^-8?G%j+PH&bT
zE>PUXC+C;ul}z!L>|q6Qi>F|kB-uJ8L#c;3%+w4y1Zt-EY4mXAq^6a`Cud~lq)h4I
zE&`_^D32ZLjVV2xB^gDj#Toe^H5{-MF{Otu51e>lsdP$@I3y{gro^Mklz?(UhDU}{
z4@xX#sA0x|H$&T$49|>UZ;rMp8D1H|-U4k?GQ2@L(~#m1t4@iwDH%Q)QQit|Q!;!r
zg1t4`reyeKM0p#uO)2%Z@MiQD0L8IW1uFvsEP7$_%?yo12=%}|Xj;J9$^Y-$PgjG8
zfXD{#!w#^><^%~aFfgz{X$ZyPARu4Ov&Z(9{WUR&2$;OK%>iaU8(4sWfdfiIs0a2T
zs<Zl4L+{)B!3+jT%vt0BGoKa2XJBA}(Gczj`vp^_gl3*yXkQ2sfRGL`4xn_6jQ>G7
z5Cseepfp1=i~*t#L=e<>qy5;g3lZn-`9SL7SjT}K%7*z17ETah7KaNSw~G{3+_%3A
zQvj0KwUD5FJH$A%v|Mi6PliPpNS{F^L4CV+T@U^E;l8~W%wZsX8gm`EKr{mb122?D
zb`Ar>1N*#pDGusaFWYy(6oAwTtagCb01P}JJ_92CK)7NK4Q&?Jj^>uwPe67j17UYU
z^fNF-BI}z_O3;4{zrC_%xG%CdhZz8(8@wH0@xl+{GcYiq$HS>h`d^K=T($oJ^AJeg
zhvfwAYgjkWVzJbDdtT%OXflPMzPe1#e==S-?O($ZKFGc`vmFFMGy?;J5R^ub2NyZ5
z*i56#_6K1e0jX<RPtZP)G7vrpB4Ah{!$BCzhGk1wI)DiOu-{>SBH(7=d3y$A`xw?a
zh(HuDh(c-9@Dy^mW03KFdem|Ia#+Fuse8AApgTE4tu%C658IzYj)y6P<Ke&})qpaK
z`}S)=_Q3FhR0nY=TLMa>x|79WSK=uxn|F8YJCP+kS_rzc!D>!P%fI{f_o3#3sR!8v
z_3ip%`zLed4f|Ed>3Ttx1GqY6D@)8NO~qPKf$Ht{DH&0nPVk(Rzu5t7s|J+znFZlH
zZ-CG@`XRK>R0z$$0HF^=LujyO1_p)$5imZK)_~9_p!z3FhVU8UA#_0@gnrQtp)>j*
z^nn%#{a`7CX82$aw&Fttln>@HFia?g@}cq!-Vpu{sQyEXAp8eu5ISNagl4FN&^l8f
z^p2GfTB998Lyc!p$bj%+@(PU*e%dk!?N9=t=Rw_>kPqQAL_%nXAP7C73PLmFK<EZ&
zxH-f?_#28L^nqw_?3E_<2tpeM@p*~4sm0({UF;N%9%je%)G0lj;rT_`;MUTV9!}Sk
z(&R)?b971%pKoGOW=Ud>LU2i9Nonzv9xngn<kAADEKg8s4wzY-ky$XMhb1U6Idw`8
zb8u?KlpY@E!~(ER_r%ORB(6_>aq*NMK9Bs;qGE-D)FOrQ)YR-LJ?ze)_E1sfl++%9
bcxam|zAUu})IOim!)mB!pl39tG)WHtDsa^m

literal 0
HcmV?d00001

diff --git a/tests/adult/adult_2.pkl b/tests/adult/adult_2.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..5221943e347e0a7b2b83e0ee33e5c603396536a3
GIT binary patch
literal 2166
zcmZo*nVQYP00upx#o0NjiA8yOB}J*Jdhy9QiN(dK#Z!93TvC%Wi!<}{LW)vTok60R
zX_=`-QzlQ*Y@AX%MWcr^xhS(FwJ0+`Z%PkKdS+hclpc=af}G5flGLIpJuFG7#U)dE
zICB##;!{!!N;0N+GxrGQX6D5gC*~I9q!!16)lTtd>JdN|%}Gs6o8ryiBLNaDPt8ov
zD2Y!iN=z=v%+HGlOS*&I&A`CW!viurEj6*Ev?#TBieC>Oh!0W|pO>Ek7U3yM%uC77
zjV~@qEJ>Z>ozf!;GBz`}ptLBnq%uAwHMuA?u{d=Kl9}KDh=<rb#jl4mIk_M{F{dCS
z5lInWUOdd&_{_ZcDc(FiTzT>Nr6mQWCB^Ymycv5qz{aE&$4}`I%`43<sMJf&FG|(R
zEzK#(Oe`u&ten!r6JL~?oS#=*QdF8;GG+3V9#*izDLw3YDG()-r+728PH|>Tn$kWc
zXo`k6qc>CQlnlup)|8UUf>e+erc8?|ogENa<{n0yDSm!_UjP69{|_d-8A_%kbvg^c
z0*n#Lm{Kw&3FKh78#Bau*ou=Aa}q((o1yB=!30y;HYI3E4=*TO;T{J0mKBs(;-~Zo
zp(Y-X=#(B7P>Pu{d5SkrhD3%m$O{>=5GOKvGqz62Q0+vrpkzvtHzP>4c8W#@r#EvC
z7bx!Hlk-dSN~U=8_OODu#Zxd%;%%Lhq13}1W@?5U0yR_oG<vvlQqxM}lQS}NQl|89
z7lG3dl*bPB#*`k;l8mC%;*5Nd8V*>Bn9{?S2TnY&R63<c9Fi1LQ{vHNN<g_F!y`kf
z2PGCV)G*_~o1tw=hG#~wH%Hr)46lq}Z-KTc8QvhBX-M&hRi{MTlnkGYC~t+fDH*;Q
z!QL8eQ!@NAqPz{-rj&YHcr$tnfa2Kc1``7VEPA0in}Hb`i4f|6ebBUkwUhtfx1X*C
z5do16-iIAvk<AJcU|?Wifzl9)!$Cm4nrDygE&FR?5D_qWZJPtke3*Key<mBU2lgSV
zv-(v-@7w!vLPWsioJ9_x^oflBK{;S~28II*rb-FTJiE}o5Gnws9AXISyWnxVNMXf&
z`@6^v*|pGt4Qv1d13Q#PmS$jhV4wFc#X<e*W%~|Zr~(iru$rKK4Q&?Jj^>uwPk;qH
zNFPHbL4Bt#>3=oea@GC^H&_FN_^{jo7ED|a5e9U3y2xq8W*S|#KgbVN0HT`K6SNPc
z41^DY2pCq#aDZ9Q0~LeW3*r8--(i0u;AY@?dj@3t7}gQA?~Xyn`{_}~?aN^XgA9DP
zf}p-#iKnz|-rce9ge6FjK93d$aB;;}mY7qTinSmBm5c3DGNL-2FMxC~<ZpHW(;86P
zXBLF-ya7Vr=!eigQz0}11B5;h4WUm!<tI#r@DH>==m$$7G{Xmbu=yV<Ap9Lr`9q7K
z{FM+|qa8wn6*Dj}C}cqRJD~CkjSzm?G6?NZ0-+~VL1=~?u-8kIdIX`RR(xJ!ZfY^O
z%88w#(ZlSRo;sz6Gd#a28(fr6>EU!uDNRlU)s<6v_<R$KGD{M36oN|<OG=BU^l<qn
zCzlpLWqE>9bHL2vjLd>5JuE?q$*EI%n1fR*ru6VQCl-Kpx+iAlA#r{3i;Ji9@Ok8y
z78NTLq!uZZr>164>0x&U71l+SQ&M{b;-M8nd|7G{sPQnRht*KeK+kAOX_6iQaWd-v

literal 0
HcmV?d00001

diff --git a/tests/adult/adult_3.pkl b/tests/adult/adult_3.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..b17bc94372fe8d7b6db981e73bf34d6f37b1d07b
GIT binary patch
literal 1734
zcmZo*nYx>e0StOXi?ef56N~cnN{Uib_2QFr5{rvdi>LI6xuhm%7H8(?g%qWxI)g+r
z(=t<wrc9oq**K+kibfA-a#3bUYEfo>-jp7e^vt}>DLov;1v!}|C8<SIdRUTDi%X{T
zaONgf#HXYdlw?ftX6g~l&CH80PRuRHNiB{Cs|AS)AdBXtCZ<jC=I@aJ36`g3re~DI
zrxhh8mt^MW#e*f?!R}^YVCdli8J?D!SW;S)T0F(ChY!RDsfo|aPXUYY6eZ@R<mbj0
zmn4>?PVr9Z5d|5WnOjg=lvz?4pOTtfl$uzaIt9s0Z~(+ZY@XuR!<n315TBS+kdcU_
zh%YZ5W^H_CUi=hqo*u5e`25n6g3^-W_$l6uJse<TQj6oK^oZt_<`z`yCFd8V>gAT^
zlw>9r6(v?q>EVelN=?qsD=sN2O)i--c}fo}SmBf&_Pi8`lF3uN8Cs_}GbT-GpAs}h
z!<*5YsdY+*WDjdfNo7GQNDEV@#gxtth%9ptqs<gQKR>Vk|Ns976W$CZQ<6HJ1z-Wj
z2xUwunUVx@Fx-t9Vm)ld$%#3Mpy<s|b>?7#scf4PG^K|Z6s~X&gM7;hN-XhHdW29D
z4@h)M4+|*8Oqo2zn<qmeLmK3T3|WX18NC@>r(~#hB3V!}CCQr+BwIU0BZJeMsfP;`
zck#*jrFkV&yxDtLLEPdgm?p8ePRUT}VGc7jLk@wODSjF~Tsf&}CGp7_nK>y_dbo?g
zX$Z<=hk9d54`)e6QEG8UK1dA*EJaM|;mZRj9#|@!(jyK@3aKgaXfh?BT#(_Bq11yC
z3mIydap29+HYLL|BiNgxZAyk$MzFU)+msA%kj^xu_`|AGqHRisPezotLfe!K-;7{y
zjkYNnei>2T25nPHy)C>My#+vV?9{=?zyOP07HH%#Lum;0z&>bNz}m_G@7qsTgNT60
z2JgcTu*im~hv^5&Gca&C2*_9S?6JLNe@zS`0w%9*a{#3?Wc&}x0n0Nm90*aJ)vp?Q
z-`<ZCDgvVBEF!2c?_G+6`qj(!9lRh7P%N<8ffdSxmRt-RP(FluVDBQQ6`N^v+5R9u
zL<CGWttV(7SRDfc!$BAWL@Q(v)VIU_M8M6!^Y#qL_A#t;0B0|@vc#OyRICXXl#kn|
zWJGm37l3SH$lvS$rZu3n&nyVvc>{#L(GQ`0rb6fgEfD&_QV6|cC4|;!htOcd7#J87
zG9Y}Iyh0<`X{AX$g3w$LpO=`MS`042Vy9^IFgvEFPU+zc&o9ab7Y$Q-I9*dplM_K@
z@01=s-^8NKlEfT^;F83W(&8yST>i<)r3Fw~o}knmFta!#vtUXOOHg8R>XaVl;M9sK
zJv`2d1z?@-iJ5swT%Y{n;we3R9{Ht3#R>(fMGEDqso7I{*quR{zo>FbYL7rXw19~(
WODzIbMpJrN4fPE4jHZ+(=>Y(kc4*W9

literal 0
HcmV?d00001

diff --git a/tests/adult/adult_data_00000.inst b/tests/adult/adult_data_00000.inst
new file mode 100644
index 0000000..c72f4e9
--- /dev/null
+++ b/tests/adult/adult_data_00000.inst
@@ -0,0 +1 @@
+f0=1,f1=9,f2=9,f3=7,f4=9,f5=5,f6=7,f7=0,f8=5,f9=3,f10=0,f11=15
\ No newline at end of file
diff --git a/tests/iris/decision_tree_classifier_20170212.pkl b/tests/iris/decision_tree_classifier_20170212.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..006ccc214168eed2a37f7201ccca66648f1112f7
GIT binary patch
literal 1916
zcmZo*nOexs00upx#o0NjiA8yOB}J*Jdhy9QiN(dK#Z!93TvC%Wi!<}{LW)vTok60R
zX_=`-QzlQ*Y@AX%MWcr^xhS(FwJ0+`Z%PkKdS+hclpc=af}G5flGLIpJuFG7#U)dE
zICB##;!{!!N;0N+GxrGQX6D5gC*~I9q!!16)lTtd>JdN|%}Gs6o8rydBLNaDPt8ov
zD2Y!iN=z=v%+HGlOS*&I&A`CW!viurEj6*Ev?#TBieC>Oh!0W|pO>Ek7U3yM%uC77
zjV~@qEJ>Z>ozf!;GBz`}ptLBnq%uAwHMuA?u{d=Kl9}KDh=<rb#jl4mIk_M{F{dCS
z5lInWUOdd&_{_ZcDc&qSTzT>Nr6mQWCB^Ymycv5qz{aE&$4}`I%`43<sMJf&FG|(R
zEzK#(Oe`u&ten!r6JL~?oS#=*QdF8;GG+3V9#*izDLw3YDG()-r+728PH|>Tn$kWc
zXo`k6qc?Nwlnlup)|8UUf>e+eCVz`5ogENa<{rkHDSm!_UjP69{|_d-?MtR4b;eH7
z=wS;^Ey*uVoYKP;mReMtnVg@KUo@qMGc2<R6miLkQ&LN&B!T<@cXozY4_k3^Voo9`
z#xs<9m@-jpvO%+np=3&uGY2y?YNoVJ37XQw3yKbSIDrC*6_m!}r}PM+ra6%4lpYpP
zLYp#qiZ@G!M20lT=NYmPpD}tfwob_~>O``jWJ;1ZBS^M(ibe*fH**gcD9YoL^Gowe
zrg(Gqu!6Y7Q!q{9Y@L#!)WaNRYK9273^hNE9<H3!w37JbjLe*rDLvdp;DiU|u|qvE
zrH8X5qbRjFBOj!O1D1@Y^zh|@QyeU5Pw5edq@UE3cr=+3P}a%tM-R{pW7LQP1*bPd
z+mwuej3{r8wka8b8ByK>ZBsIWKswWqq5`W<iMA;j!5N9(3T;y|LNcPfHQJ_Rgk~gq
z8?;R+^|tV4^cDam45uHA3=E9WC}TpS85kI5%$&*Wa4^YuHM7%G`%I`jm~x1603|YH
z{13_j%QG+>fYJ;~Fb0@*bbuuhHZY%o0mf%wU?@mS`(+<tEGrnl;l4dQM}pKH2y|cp
z(F_a>tWX-}UWh(nhX*VVHWtJjwAX~ngQ*4=qTQ(s)&M0M9EsHTz#irgh)xLyXtBz`
z0TDnZFPu3e?x11q6Ps7R!CnqohQW@YeMK_I3uTU9#IO$<>~IQKJV>C}XW=jfl;PRR
z5_3vZv1SucVbeY(BeB!@21KW$1DI}bhR_TSP#P)@mc&9}sE4V6nZp4!2P)ox%RHDm
z7!6g=@WCF!vPCrqE-r#54pGg(011N9q#i+N?vBq(%uOu@m*=1&lczYfATdWTCp9m<
xBx6bsH-uZBnF8YR6r`3QOG3C1$<!W!cxd?+Uj`~|K@}vcp`L-B(Uj68JphtUcMSjl

literal 0
HcmV?d00001

diff --git a/tests/iris/iris.csv b/tests/iris/iris.csv
new file mode 100644
index 0000000..1b9d029
--- /dev/null
+++ b/tests/iris/iris.csv
@@ -0,0 +1,151 @@
+"sepal.length","sepal.width","petal.length","petal.width","variety"
+5.1,3.5,1.4,.2,"Setosa"
+4.9,3,1.4,.2,"Setosa"
+4.7,3.2,1.3,.2,"Setosa"
+4.6,3.1,1.5,.2,"Setosa"
+5,3.6,1.4,.2,"Setosa"
+5.4,3.9,1.7,.4,"Setosa"
+4.6,3.4,1.4,.3,"Setosa"
+5,3.4,1.5,.2,"Setosa"
+4.4,2.9,1.4,.2,"Setosa"
+4.9,3.1,1.5,.1,"Setosa"
+5.4,3.7,1.5,.2,"Setosa"
+4.8,3.4,1.6,.2,"Setosa"
+4.8,3,1.4,.1,"Setosa"
+4.3,3,1.1,.1,"Setosa"
+5.8,4,1.2,.2,"Setosa"
+5.7,4.4,1.5,.4,"Setosa"
+5.4,3.9,1.3,.4,"Setosa"
+5.1,3.5,1.4,.3,"Setosa"
+5.7,3.8,1.7,.3,"Setosa"
+5.1,3.8,1.5,.3,"Setosa"
+5.4,3.4,1.7,.2,"Setosa"
+5.1,3.7,1.5,.4,"Setosa"
+4.6,3.6,1,.2,"Setosa"
+5.1,3.3,1.7,.5,"Setosa"
+4.8,3.4,1.9,.2,"Setosa"
+5,3,1.6,.2,"Setosa"
+5,3.4,1.6,.4,"Setosa"
+5.2,3.5,1.5,.2,"Setosa"
+5.2,3.4,1.4,.2,"Setosa"
+4.7,3.2,1.6,.2,"Setosa"
+4.8,3.1,1.6,.2,"Setosa"
+5.4,3.4,1.5,.4,"Setosa"
+5.2,4.1,1.5,.1,"Setosa"
+5.5,4.2,1.4,.2,"Setosa"
+4.9,3.1,1.5,.2,"Setosa"
+5,3.2,1.2,.2,"Setosa"
+5.5,3.5,1.3,.2,"Setosa"
+4.9,3.6,1.4,.1,"Setosa"
+4.4,3,1.3,.2,"Setosa"
+5.1,3.4,1.5,.2,"Setosa"
+5,3.5,1.3,.3,"Setosa"
+4.5,2.3,1.3,.3,"Setosa"
+4.4,3.2,1.3,.2,"Setosa"
+5,3.5,1.6,.6,"Setosa"
+5.1,3.8,1.9,.4,"Setosa"
+4.8,3,1.4,.3,"Setosa"
+5.1,3.8,1.6,.2,"Setosa"
+4.6,3.2,1.4,.2,"Setosa"
+5.3,3.7,1.5,.2,"Setosa"
+5,3.3,1.4,.2,"Setosa"
+7,3.2,4.7,1.4,"Versicolor"
+6.4,3.2,4.5,1.5,"Versicolor"
+6.9,3.1,4.9,1.5,"Versicolor"
+5.5,2.3,4,1.3,"Versicolor"
+6.5,2.8,4.6,1.5,"Versicolor"
+5.7,2.8,4.5,1.3,"Versicolor"
+6.3,3.3,4.7,1.6,"Versicolor"
+4.9,2.4,3.3,1,"Versicolor"
+6.6,2.9,4.6,1.3,"Versicolor"
+5.2,2.7,3.9,1.4,"Versicolor"
+5,2,3.5,1,"Versicolor"
+5.9,3,4.2,1.5,"Versicolor"
+6,2.2,4,1,"Versicolor"
+6.1,2.9,4.7,1.4,"Versicolor"
+5.6,2.9,3.6,1.3,"Versicolor"
+6.7,3.1,4.4,1.4,"Versicolor"
+5.6,3,4.5,1.5,"Versicolor"
+5.8,2.7,4.1,1,"Versicolor"
+6.2,2.2,4.5,1.5,"Versicolor"
+5.6,2.5,3.9,1.1,"Versicolor"
+5.9,3.2,4.8,1.8,"Versicolor"
+6.1,2.8,4,1.3,"Versicolor"
+6.3,2.5,4.9,1.5,"Versicolor"
+6.1,2.8,4.7,1.2,"Versicolor"
+6.4,2.9,4.3,1.3,"Versicolor"
+6.6,3,4.4,1.4,"Versicolor"
+6.8,2.8,4.8,1.4,"Versicolor"
+6.7,3,5,1.7,"Versicolor"
+6,2.9,4.5,1.5,"Versicolor"
+5.7,2.6,3.5,1,"Versicolor"
+5.5,2.4,3.8,1.1,"Versicolor"
+5.5,2.4,3.7,1,"Versicolor"
+5.8,2.7,3.9,1.2,"Versicolor"
+6,2.7,5.1,1.6,"Versicolor"
+5.4,3,4.5,1.5,"Versicolor"
+6,3.4,4.5,1.6,"Versicolor"
+6.7,3.1,4.7,1.5,"Versicolor"
+6.3,2.3,4.4,1.3,"Versicolor"
+5.6,3,4.1,1.3,"Versicolor"
+5.5,2.5,4,1.3,"Versicolor"
+5.5,2.6,4.4,1.2,"Versicolor"
+6.1,3,4.6,1.4,"Versicolor"
+5.8,2.6,4,1.2,"Versicolor"
+5,2.3,3.3,1,"Versicolor"
+5.6,2.7,4.2,1.3,"Versicolor"
+5.7,3,4.2,1.2,"Versicolor"
+5.7,2.9,4.2,1.3,"Versicolor"
+6.2,2.9,4.3,1.3,"Versicolor"
+5.1,2.5,3,1.1,"Versicolor"
+5.7,2.8,4.1,1.3,"Versicolor"
+6.3,3.3,6,2.5,"Virginica"
+5.8,2.7,5.1,1.9,"Virginica"
+7.1,3,5.9,2.1,"Virginica"
+6.3,2.9,5.6,1.8,"Virginica"
+6.5,3,5.8,2.2,"Virginica"
+7.6,3,6.6,2.1,"Virginica"
+4.9,2.5,4.5,1.7,"Virginica"
+7.3,2.9,6.3,1.8,"Virginica"
+6.7,2.5,5.8,1.8,"Virginica"
+7.2,3.6,6.1,2.5,"Virginica"
+6.5,3.2,5.1,2,"Virginica"
+6.4,2.7,5.3,1.9,"Virginica"
+6.8,3,5.5,2.1,"Virginica"
+5.7,2.5,5,2,"Virginica"
+5.8,2.8,5.1,2.4,"Virginica"
+6.4,3.2,5.3,2.3,"Virginica"
+6.5,3,5.5,1.8,"Virginica"
+7.7,3.8,6.7,2.2,"Virginica"
+7.7,2.6,6.9,2.3,"Virginica"
+6,2.2,5,1.5,"Virginica"
+6.9,3.2,5.7,2.3,"Virginica"
+5.6,2.8,4.9,2,"Virginica"
+7.7,2.8,6.7,2,"Virginica"
+6.3,2.7,4.9,1.8,"Virginica"
+6.7,3.3,5.7,2.1,"Virginica"
+7.2,3.2,6,1.8,"Virginica"
+6.2,2.8,4.8,1.8,"Virginica"
+6.1,3,4.9,1.8,"Virginica"
+6.4,2.8,5.6,2.1,"Virginica"
+7.2,3,5.8,1.6,"Virginica"
+7.4,2.8,6.1,1.9,"Virginica"
+7.9,3.8,6.4,2,"Virginica"
+6.4,2.8,5.6,2.2,"Virginica"
+6.3,2.8,5.1,1.5,"Virginica"
+6.1,2.6,5.6,1.4,"Virginica"
+7.7,3,6.1,2.3,"Virginica"
+6.3,3.4,5.6,2.4,"Virginica"
+6.4,3.1,5.5,1.8,"Virginica"
+6,3,4.8,1.8,"Virginica"
+6.9,3.1,5.4,2.1,"Virginica"
+6.7,3.1,5.6,2.4,"Virginica"
+6.9,3.1,5.1,2.3,"Virginica"
+5.8,2.7,5.1,1.9,"Virginica"
+6.8,3.2,5.9,2.3,"Virginica"
+6.7,3.3,5.7,2.5,"Virginica"
+6.7,3,5.2,2.3,"Virginica"
+6.3,2.5,5,1.9,"Virginica"
+6.5,3,5.2,2,"Virginica"
+6.2,3.4,5.4,2.3,"Virginica"
+5.9,3,5.1,1.8,"Virginica"
\ No newline at end of file
diff --git a/tests/iris/iris.pkl b/tests/iris/iris.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..cf2d521581ec57be284bb87b75316940c16fdcb7
GIT binary patch
literal 2047
zcmZo*naan+00upx#o0NjiA8yOB}J*Jdhy9QiN(dK#Z!93TvC%Wi!<}{LW)vTok60R
zX_=`-QzlQ*Y@AX%MWcr^xhS(FwJ0+`Z%PkKdS+hclpc=af}G5flGLIpJuFG7#U)dE
zICB##;!{!!N;0N+GxrGQX6D5gC*~I9q!!16)lTtd>JdN|%}Gs6o8rybBLNaDPt8ov
zD2Y!iN=z=v%+HGlOS*&I&A`CW!viurEj6*Ev?#TBieC>Oh!0W|pO>Ek7U3yM%uC77
zjV~@qEJ>Z>*CPrtH8Z!Mv?#NrGCn0WxhOTUICTn=kzoJFLoA-+*Tb2dTo9j_Q;?B}
zq=+vs9%gHNW?uXhZ<Zdey!iaml7iBb;&_njIl#uG7ROKN5zfj_%E?U9%PY+-sEjYj
zOwP_pozf%d2jV&w6(v@N7bO-HAO|c*acL4*|CAn9u&yaR?0G4PAhlB_Pw8PT&PXgs
zo#M^hI;Dp-zbFM1+KkRqdRS9RDhpDlWXQl+lc)4BhZ-79@n-O5Y@ZS|MZ=r9htXz=
zpP!$%fj5gcN6D0=9<Ickoc!|m+}y;1DIKLrN(>AP!3+!xsSFGZB@7G<`3wvU#S9D#
zi3|)3AURmj!)TCL7*u@`RBa{$14A-YZw{0O$$`{@Fi1T}4M;4Vfq?<U&V$k*eaLoA
z-3PS^6u{sx0f(t5I9T+O^NUjTa!YecG9iJ}!&aP}n3D)fqZz6_OqmwQUeD0<^Yioi
z|NsAgFyYNmG9}5GgBfc6l(s2BQ+jwo2?!pwpm=2k<%;+zJwm9t1SC48hXs^1rc9pV
z&5|Jj$rKsV8FCr&-i)nNG88kEGL$onGPFBNlVG+?4Pb=W2Z@l{DH<7^-poB*pyV2#
zoL`z(GR2#-hZV#vo`Pm3C)`Zc9_BDpGi1N@FxE`*)9B&KNlhz>PtM59Ntx2aT?EcM
zP#!zfS5ta8OEQX5i!<^;YB*q7VM-5Q9yrs$a_5vDaY!CWO^HX7DFNmF3~%B>*qfnk
zN`_BHm^Vk;lnmdDFmHjjDH(nsy=eqv$6KOpN``+%thYkjl#GCkFmH{vDH(wovEBx4
zQ%b!pycxX(N~R=%0~i{t4A4RiPBSntoH%oa#o<vB)1Cy+r}pKL;D?Y0VjV!q0U7^;
zav%yA4nS!JO&9}2H@G;!B9slpXJBA}@gdwl_D!?6F1DS&Z|?^YfRGKr4lEEB11pq<
zxtD=~;li0S!VVDOGxiz~29$Im=*|n9r<O-rueAr|FQ_I41_noh`ecq5${a7eV-NEO
zM6ZMcJA}c&0i}`LS&)_{><}=`r{s^#Dtk4kJcM#4+CJ3q;vlFGVm<?dGK>MD8yp>`
z>VO!ax`3@LF{d;YGuL=CdNa4fa$_twv4FKSxIhEj2}-*{Xt+376&3<bJxoP|BSb%g
z2$Y6OG&n%{AMBwbFbXOz0HtBVP<0J3d(gxg7|_&1<w1d=0A-gZ^$5g63#|CE)FMzV
RKBb4%P|rZmXi9049spa?ZJ_`F

literal 0
HcmV?d00001

diff --git a/tests/iris/iris.txt b/tests/iris/iris.txt
new file mode 100644
index 0000000..d356f5d
--- /dev/null
+++ b/tests/iris/iris.txt
@@ -0,0 +1,4 @@
+sepal.length,Categorical,7.6,6.8,7.1,4.9,4.4,6.2,6,7.3,5.9,7.4,5.2,5.6,4.8,6.5,5.5,4.6,6.6,6.4,7,4.5,7.2,5.1,5.8,5.3,6.9,6.1,6.7,4.7,7.7,6.3,5.7,7.9,5.4,4.3,5
+sepal.width,Categorical,4.2,4.4,3.1,2.4,2.9,2,3.8,4.1,4,3.2,2.7,3.3,2.2,2.5,2.3,3.6,3.5,3.9,2.8,2.6,3.7,3,3.4
+petal.length,Categorical,4.2,4.9,4.4,6,5.9,5.2,5.6,4.8,1,5.5,4.6,6.6,1.1,3.8,1.5,6.4,4.1,4,4.5,1.6,3.3,1.4,5.1,1.7,5.8,3.5,3.6,5.3,1.9,6.9,6.1,6.7,4.7,3.9,1.2,1.3,6.3,5.7,3.7,5.4,3,4.3,5
+petal.width,Categorical,2.4,.2,1,2,1.1,1.5,.6,.5,2.2,.3,1.6,1.4,2.5,1.7,2.3,1.8,2.1,1.9,1.2,1.3,.1,.4
\ No newline at end of file
diff --git a/tests/iris/iris01.json b/tests/iris/iris01.json
new file mode 100644
index 0000000..59c0840
--- /dev/null
+++ b/tests/iris/iris01.json
@@ -0,0 +1,4 @@
+{"sepal.length":4.9,
+"sepal.width":3,
+"petal.length":1.4,
+"petal.width":0.2}
diff --git a/tests/iris/iris2.pkl b/tests/iris/iris2.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..cf2d521581ec57be284bb87b75316940c16fdcb7
GIT binary patch
literal 2047
zcmZo*naan+00upx#o0NjiA8yOB}J*Jdhy9QiN(dK#Z!93TvC%Wi!<}{LW)vTok60R
zX_=`-QzlQ*Y@AX%MWcr^xhS(FwJ0+`Z%PkKdS+hclpc=af}G5flGLIpJuFG7#U)dE
zICB##;!{!!N;0N+GxrGQX6D5gC*~I9q!!16)lTtd>JdN|%}Gs6o8rybBLNaDPt8ov
zD2Y!iN=z=v%+HGlOS*&I&A`CW!viurEj6*Ev?#TBieC>Oh!0W|pO>Ek7U3yM%uC77
zjV~@qEJ>Z>*CPrtH8Z!Mv?#NrGCn0WxhOTUICTn=kzoJFLoA-+*Tb2dTo9j_Q;?B}
zq=+vs9%gHNW?uXhZ<Zdey!iaml7iBb;&_njIl#uG7ROKN5zfj_%E?U9%PY+-sEjYj
zOwP_pozf%d2jV&w6(v@N7bO-HAO|c*acL4*|CAn9u&yaR?0G4PAhlB_Pw8PT&PXgs
zo#M^hI;Dp-zbFM1+KkRqdRS9RDhpDlWXQl+lc)4BhZ-79@n-O5Y@ZS|MZ=r9htXz=
zpP!$%fj5gcN6D0=9<Ickoc!|m+}y;1DIKLrN(>AP!3+!xsSFGZB@7G<`3wvU#S9D#
zi3|)3AURmj!)TCL7*u@`RBa{$14A-YZw{0O$$`{@Fi1T}4M;4Vfq?<U&V$k*eaLoA
z-3PS^6u{sx0f(t5I9T+O^NUjTa!YecG9iJ}!&aP}n3D)fqZz6_OqmwQUeD0<^Yioi
z|NsAgFyYNmG9}5GgBfc6l(s2BQ+jwo2?!pwpm=2k<%;+zJwm9t1SC48hXs^1rc9pV
z&5|Jj$rKsV8FCr&-i)nNG88kEGL$onGPFBNlVG+?4Pb=W2Z@l{DH<7^-poB*pyV2#
zoL`z(GR2#-hZV#vo`Pm3C)`Zc9_BDpGi1N@FxE`*)9B&KNlhz>PtM59Ntx2aT?EcM
zP#!zfS5ta8OEQX5i!<^;YB*q7VM-5Q9yrs$a_5vDaY!CWO^HX7DFNmF3~%B>*qfnk
zN`_BHm^Vk;lnmdDFmHjjDH(nsy=eqv$6KOpN``+%thYkjl#GCkFmH{vDH(wovEBx4
zQ%b!pycxX(N~R=%0~i{t4A4RiPBSntoH%oa#o<vB)1Cy+r}pKL;D?Y0VjV!q0U7^;
zav%yA4nS!JO&9}2H@G;!B9slpXJBA}@gdwl_D!?6F1DS&Z|?^YfRGKr4lEEB11pq<
zxtD=~;li0S!VVDOGxiz~29$Im=*|n9r<O-rueAr|FQ_I41_noh`ecq5${a7eV-NEO
zM6ZMcJA}c&0i}`LS&)_{><}=`r{s^#Dtk4kJcM#4+CJ3q;vlFGVm<?dGK>MD8yp>`
z>VO!ax`3@LF{d;YGuL=CdNa4fa$_twv4FKSxIhEj2}-*{Xt+376&3<bJxoP|BSb%g
z2$Y6OG&n%{AMBwbFbXOz0HtBVP<0J3d(gxg7|_&1<w1d=0A-gZ^$5g63#|CE)FMzV
RKBb4%P|rZmXi9049spa?ZJ_`F

literal 0
HcmV?d00001

diff --git a/tests/iris/iris_00000.txt b/tests/iris/iris_00000.txt
new file mode 100644
index 0000000..108004d
--- /dev/null
+++ b/tests/iris/iris_00000.txt
@@ -0,0 +1 @@
+sepal.length=4.3,sepal.width=2.0,petal.length=1.0,petal.width=0.1
\ No newline at end of file
diff --git a/tests/zoo/inst/zoo_00.inst b/tests/zoo/inst/zoo_00.inst
new file mode 100644
index 0000000..8d6abc5
--- /dev/null
+++ b/tests/zoo/inst/zoo_00.inst
@@ -0,0 +1 @@
+f0=1,f1=0,f1=0,f1=1,f1=0,f1=0,f1=1,f1=1,f1=1,f1=1,f1=0,f1=0,f1=4,f1=0,f1=0,f1=1
\ No newline at end of file
diff --git a/tests/zoo/inst/zoo_01.inst b/tests/zoo/inst/zoo_01.inst
new file mode 100644
index 0000000..4a6df51
--- /dev/null
+++ b/tests/zoo/inst/zoo_01.inst
@@ -0,0 +1 @@
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1
\ No newline at end of file
diff --git a/tests/zoo/inst/zoo_02.inst b/tests/zoo/inst/zoo_02.inst
new file mode 100644
index 0000000..d72c0ae
--- /dev/null
+++ b/tests/zoo/inst/zoo_02.inst
@@ -0,0 +1 @@
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0
\ No newline at end of file
diff --git a/tests/zoo/inst/zoo_11.inst b/tests/zoo/inst/zoo_11.inst
new file mode 100644
index 0000000..0fa9d7c
--- /dev/null
+++ b/tests/zoo/inst/zoo_11.inst
@@ -0,0 +1 @@
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,1,0
\ No newline at end of file
diff --git a/tests/zoo/zoo.csv b/tests/zoo/zoo.csv
new file mode 100644
index 0000000..7eb9774
--- /dev/null
+++ b/tests/zoo/zoo.csv
@@ -0,0 +1,102 @@
+hair,feathers,eggs,milk,airborne,aquatic,predator,toothed,backbone,breathes,venomous,fins,legs,tail,domestic,catsize,class_type
+1,0,0,1,0,0,1,1,1,1,0,0,4,0,0,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0,fish
+1,0,0,1,0,0,1,1,1,1,0,0,4,0,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,1,1,mammal
+0,0,1,0,0,1,0,1,1,0,0,1,0,1,1,0,fish
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0,fish
+1,0,0,1,0,0,0,1,1,1,0,0,4,0,1,0,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,1,0,bird
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0,fish
+0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,invertebrate
+0,0,1,0,0,1,1,0,0,0,0,0,4,0,0,0,invertebrate
+0,0,1,0,0,1,1,0,0,0,0,0,6,0,0,0,invertebrate
+0,1,1,0,1,0,1,0,1,1,0,0,2,1,0,0,bird
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,1,fish
+0,0,0,1,0,1,1,1,1,1,0,1,0,1,0,1,mammal
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,1,0,bird
+0,1,1,0,1,1,0,0,1,1,0,0,2,1,0,0,bird
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,0,1,bird
+0,0,1,0,0,0,0,0,0,1,0,0,6,0,0,0,bug
+0,0,1,0,0,1,1,1,1,1,0,0,4,0,0,0,amphibian
+0,0,1,0,0,1,1,1,1,1,1,0,4,0,0,0,amphibian
+1,0,0,1,1,0,0,1,1,1,0,0,2,1,0,0,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,2,0,1,1,mammal
+0,0,1,0,1,0,0,0,0,1,0,0,6,0,0,0,bug
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,1,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,2,0,0,1,mammal
+0,1,1,0,1,1,1,0,1,1,0,0,2,1,0,0,bird
+0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,fish
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,1,0,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,0,mammal
+0,1,1,0,1,0,1,0,1,1,0,0,2,1,0,0,bird
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0,fish
+1,0,1,0,1,0,0,0,0,1,1,0,6,0,1,0,bug
+1,0,1,0,1,0,0,0,0,1,0,0,6,0,0,0,bug
+0,1,1,0,0,0,1,0,1,1,0,0,2,1,0,0,bird
+0,0,1,0,1,0,1,0,0,1,0,0,6,0,0,0,bug
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,0,0,bird
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+0,0,1,0,0,1,1,0,0,0,0,0,6,0,0,0,invertebrate
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,1,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,0,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,1,0,1,0,0,0,0,1,0,0,6,0,0,0,bug
+0,0,1,0,0,1,1,1,1,1,0,0,4,1,0,0,amphibian
+0,0,1,0,0,1,1,0,0,0,0,0,8,0,0,1,invertebrate
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,0,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,1,mammal
+0,1,1,0,0,0,0,0,1,1,0,0,2,1,0,1,bird
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,1,0,bird
+0,1,1,0,0,1,1,0,1,1,0,0,2,1,0,1,bird
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,0,0,bird
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,1,fish
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,0,fish
+0,0,1,0,0,0,1,1,1,1,1,0,0,1,0,0,reptile
+1,0,1,1,0,1,1,0,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,1,1,mammal
+0,0,0,1,0,1,1,1,1,1,0,1,0,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,1,1,mammal
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,1,1,mammal
+0,1,1,0,0,0,1,0,1,1,0,0,2,1,0,1,bird
+0,0,0,0,0,0,1,0,0,1,1,0,8,1,0,0,invertebrate
+0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,fish
+1,0,0,1,0,1,1,1,1,1,0,1,0,0,0,1,mammal
+1,0,0,1,0,1,1,1,1,1,0,1,2,1,0,1,mammal
+0,0,0,0,0,1,1,1,1,0,1,0,0,1,0,0,reptile
+0,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,invertebrate
+0,1,1,0,1,1,1,0,1,1,0,0,2,1,0,0,bird
+0,1,1,0,1,1,1,0,1,1,0,0,2,1,0,0,bird
+0,0,1,0,0,0,1,1,1,1,0,0,0,1,0,0,reptile
+0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,invertebrate
+0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,fish
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,0,0,bird
+1,0,0,1,0,0,0,1,1,1,0,0,2,1,0,0,mammal
+0,0,1,0,0,1,1,0,0,0,0,0,5,0,0,0,invertebrate
+0,0,1,0,0,1,1,1,1,0,1,1,0,1,0,1,fish
+0,1,1,0,1,1,0,0,1,1,0,0,2,1,0,1,bird
+0,0,1,0,0,0,0,0,0,1,0,0,6,0,0,0,bug
+0,0,1,0,0,1,0,1,1,1,0,0,4,0,0,0,amphibian
+0,0,1,0,0,0,0,0,1,1,0,0,4,1,0,1,reptile
+0,0,1,0,0,0,1,1,1,1,0,0,4,1,0,0,reptile
+0,0,1,0,0,1,1,1,1,0,0,1,0,1,0,1,fish
+1,0,0,1,1,0,0,1,1,1,0,0,2,1,0,0,mammal
+1,0,0,1,0,0,0,1,1,1,0,0,4,1,0,0,mammal
+0,1,1,0,1,0,1,0,1,1,0,0,2,1,0,1,bird
+1,0,0,1,0,0,0,1,1,1,0,0,2,1,0,1,mammal
+1,0,1,0,1,0,0,0,0,1,1,0,6,0,0,0,bug
+1,0,0,1,0,0,1,1,1,1,0,0,4,1,0,1,mammal
+0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,invertebrate
+0,1,1,0,1,0,0,0,1,1,0,0,2,1,0,0,bird
\ No newline at end of file
diff --git a/tests/zoo/zoo.dt b/tests/zoo/zoo.dt
new file mode 100644
index 0000000..a1ee642
--- /dev/null
+++ b/tests/zoo/zoo.dt
@@ -0,0 +1,85 @@
+41
+1
+I 1 2 3 6 9 10 12 14 17 18 20 21 22 24 28 30 33 35 37 38
+T 4 5 7 8 11 13 15 16 19 23 25 26 27 29 31 32 34 36 39 40 41
+4 T bug
+5 T mammal
+7 T bird
+8 T bug
+11 T mammal
+13 T fish
+15 T bird
+16 T reptile
+19 T mammal
+23 T reptile
+25 T reptile
+26 T bird
+27 T fish
+29 T amphibian
+31 T amphibian
+32 T reptile
+34 T invertebrate
+36 T invertebrate
+39 T invertebrate
+40 T bug
+41 T invertebrate
+1 f4 0 2
+1 f4 1 9
+2 f0 0 3
+2 f0 1 6
+3 f10 0 4
+3 f10 1 5
+6 f12 1 7
+6 f12 5 7
+6 f12 9 7
+6 f12 11 7
+6 f12 3 8
+6 f12 7 8
+9 f15 0 10
+9 f15 1 17
+10 f3 0 11
+10 f3 1 12
+12 f11 0 13
+12 f11 1 14
+14 f12 1 15
+14 f12 9 15
+14 f12 3 16
+14 f12 5 16
+14 f12 7 16
+14 f12 11 16
+17 f8 0 18
+17 f8 1 33
+18 f0 0 19
+18 f0 1 20
+20 f12 1 21
+20 f12 9 21
+20 f12 3 28
+20 f12 5 28
+20 f12 7 28
+20 f12 11 28
+21 f6 0 22
+21 f6 1 27
+22 f10 0 23
+22 f10 1 24
+24 f12 9 25
+24 f12 1 26
+24 f12 3 26
+24 f12 5 26
+24 f12 7 26
+24 f12 11 26
+28 f10 0 29
+28 f10 1 30
+30 f5 0 31
+30 f5 1 32
+33 f5 0 34
+33 f5 1 35
+35 f13 0 36
+35 f13 1 37
+37 f9 0 38
+37 f9 1 41
+38 f12 1 39
+38 f12 5 39
+38 f12 9 39
+38 f12 11 39
+38 f12 3 40
+38 f12 7 40
diff --git a/tests/zoo/zoo.json b/tests/zoo/zoo.json
new file mode 100644
index 0000000..506568b
--- /dev/null
+++ b/tests/zoo/zoo.json
@@ -0,0 +1,18 @@
+{
+"hair":1,
+"feathers":0,
+"eggs":0,
+"milk":1,
+"airborne":0,
+"aquatic":0,
+"predator":0,
+"toothed":1,
+"backbone":1,
+"breathes":1,
+"venomous":0,
+"fins":0,
+"legs":6,
+"tail":1,
+"domestic":0,
+"catsize":1
+}
diff --git a/tests/zoo/zoo.map b/tests/zoo/zoo.map
new file mode 100644
index 0000000..89838b1
--- /dev/null
+++ b/tests/zoo/zoo.map
@@ -0,0 +1,38 @@
+Categorical
+16
+f0 0 =1
+f0 1 =0
+f1 0 =1
+f1 1 =0
+f2 0 =1
+f2 1 =0
+f3 0 =1
+f3 1 =0
+f4 0 =1
+f4 1 =0
+f5 0 =1
+f5 1 =0
+f6 0 =1
+f6 1 =0
+f7 0 =1
+f7 1 =0
+f8 0 =1
+f8 1 =0
+f9 0 =1
+f9 1 =0
+f10 0 =1
+f10 1 =0
+f11 0 =1
+f11 1 =0
+f12 1 =2
+f12 3 =6
+f12 5 =5
+f12 7 =8
+f12 9 =0
+f12 11 =4
+f13 0 =1
+f13 1 =0
+f14 0 =1
+f14 1 =0
+f15 0 =1
+f15 1 =0
diff --git a/tests/zoo/zoo.pkl b/tests/zoo/zoo.pkl
new file mode 100644
index 0000000000000000000000000000000000000000..edfd6540dd861d1260292b0bce1efe6d6a809461
GIT binary patch
literal 2303
zcmZo*naan+00upx#o0NjiA8yOB}J*Jdhy9QiN(dK#Z!93TvC%Wi!<}{LW)vTok60R
zX_=`-QzlQ*Y@AX%MWcr^xhS(FwJ0+`Z%PkKdS+hclpc=af}G5flGLIpJuFG7#U)dE
zICB##;!{!!N;0N+GxrGQX6D5gC*~I9q!!16)lTtd>JdN|%}Gs6o8rybBLNaDPt8ov
zD2Y!iN=z=v%+HGlOS*&I&A`CW!viurEj6*Ev?#TBieC>Oh!0W|pO>Ek7U3yM%uC77
zjV~@qEJ>Z>*CPrtH8Z!Mv?#NrGCn0WxhOTUICTn=kzoJFLoA-+*Tb2dTo9j_Q;?B}
zq=+vs9%gHNW?uXhZ-E}Jy!iaml7iBb;&_njIl#uG7ROKN5zfj_%E?U9%PY+-sEjYj
zOwP_pozf%d2jV&w6(v@N7bO-HAO|c*acL4*|CAn9u&yaR?0G4PAhlB_Pw8PT&PXgs
zo#M^jI;Dp-zbFM1+KkRqdRS9RDhpDlWXQl+lc)4BhZ-79@n-O5Y@ZS|MZ=r9htXz=
zpP!$%fj5gcN6D0=9<Ickoc!|m+}y;1DIKLri3|)3xeN>p1q=)f84L^znG6gJNl+Rj
zmj?@bC=HV<VqjoMfr{ayK<Y~w7#Py=DMFG<gW6pTbq|shRwl^3AUBpVFfgPtFff4J
zRRU#$>;~~c{(y;r{0icOXb=XmbFi8W5d`T6nF|UZkQpHRK<Yta5EZE8)O}DsD1m_E
z6r3hR!O^OhoL`ixms^@sk_n029=77-#GFJ>9?MYeVal{Xj=Bs@KR-XO|NsC02NT{5
zB~y}|IoP4*PidPHG^K|ZlmXyL6O=AlLAg49N{<j~E(eKD>0tq7?J1L|cnf4mKr(fP
zbcS4pyf<U(lnliTr3~c^qYUki(j=HIQv(<w_CXR<?G%j+PH*NOE>L!hPtGsRE1BZW
z-opyw7EeJllO1lRY7cXmsTs20dKhb__-XWT<)o&S#3yHD=A=yN;VuH_eJGC|>Z>U|
zoFy4Wsl^%jAT=DYtURTMFAtpQVWrEI9&tz>PECnNlPLj}UK!rRg|Ih6+msBSj4*GG
zwka9D8DZW6ZBsJ*Kzh>%#*Vi{+msCdj971lwka6_8DZWUZBsG=Gh)3B+NP9xTX-{i
z3zSSr0tYZOSQ*$L!OOr5r6JS<`;;XA;=iG<?DZi65VFDD0a}PNutG!_VCF!$59}w*
zTf67<t(W#n5CJIZ=m0aH1uDV;r6JS<`@Y8(eUB|(+DkwLprnNZDET4de^3ro!2t;U
zz@7)ffRY*n^;v=~d_uB5s09pMFb0@bA!r|183O|YFN^`EwH&7EfEf%7J#1x(Ii;zX
zso0y*o4p;DJ7U3+2hqae0HIZ&v;>qEfYLC&7L+dnrHR!CF`a<{T^~#wW<I)oFnO3h
z7#~K%^x@*e#9{7)@nJN~J{TWHquU1y7nnS?XjphMFicf|npv9EBM=WQ@Z!r-i$JZI
QDLt%)dIow%Q%aNc0FKT|f&c&j

literal 0
HcmV?d00001

diff --git a/utils.py b/utils.py
index b342910..42b30c9 100644
--- a/utils.py
+++ b/utils.py
@@ -2,6 +2,7 @@ import base64
 import io
 import pickle
 import joblib
+import json
 
 import numpy as np
 from dash import html
@@ -43,14 +44,16 @@ def parse_contents_instance(contents, filename):
     try:
         if '.csv' in filename:
             data = decoded.decode('utf-8')
+            data = str(data).strip().split(',')
+            data = list(map(lambda i: tuple([i[0], np.float32(i[1])]), [i.split('=') for i in data]))
         elif '.txt' in filename:
-            data = decoded.decode('utf-8')       
-        elif '.json' in filename:
             data = decoded.decode('utf-8')
-        else : 
+            data = str(data).strip().split(',')
+            data = list(map(lambda i: tuple([i[0], np.float32(i[1])]), [i.split('=') for i in data]))       
+        elif '.json' in filename:
             data = decoded.decode('utf-8')
-        data = str(data).strip().split(',')
-        data = list(map(lambda i: tuple([i[0], np.float32(i[1])]), [i.split('=') for i in data]))
+            data = json.loads(data)
+            data = list(tuple(data.items()))
     except Exception as e:
         print(e)
         return html.Div([
-- 
GitLab