Repository: jupiters1117/mico (language: python)

Issue:
Feature/update doc
Done documentation.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 6d35bed..2eda812 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,3 +104,24 @@ venv.bak/
 .mypy_cache/
 
 conda_env/
+
+.idea/
+
+examples/xxx/
+
+doc/_build/
+
+doc/auto_examples/
+
+doc/generated/
+
+docs/auto_examples/
+
+docs/generated/
+
+mico/xxx/
+
+conda_env36/
+
+xxx/
+gh-pages/
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 780e0b3..0000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,984 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ChangeListManager">
-    <list default="true" id="4c701d04-f64b-46d7-b691-ae916125405f" name="Default" comment="" />
-    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
-    <option name="TRACKING_ENABLED" value="true" />
-    <option name="SHOW_DIALOG" value="false" />
-    <option name="HIGHLIGHT_CONFLICTS" value="true" />
-    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
-    <option name="LAST_RESOLUTION" value="IGNORE" />
-  </component>
-  <component name="CoverageDataManager">
-    <SUITE FILE_PATH="coverage/mico$mico_utils.coverage" NAME="mico_utils Coverage Results" MODIFIED="1572208603958" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/mico" />
-    <SUITE FILE_PATH="coverage/mico$examples.coverage" NAME="examples Coverage Results" MODIFIED="1573454482124" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/examples" />
-  </component>
-  <component name="CreatePatchCommitExecutor">
-    <option name="PATCH_PATH" value="" />
-  </component>
-  <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
-  <component name="FileEditorManager">
-    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
-      <file leaf-file-name="mico_utils.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/mico/mico_utils.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="15">
-              <caret line="111" column="4" lean-forward="false" selection-start-line="111" selection-start-column="4" selection-end-line="111" selection-end-column="4" />
-              <folding>
-                <element signature="e#227#245#0" expanded="true" />
-                <element signature="e#5952#6471#0" expanded="false" />
-                <element signature="e#6868#7546#0" expanded="false" />
-                <marker date="1573419526985" expanded="true" signature="4896:7937" ph="..." />
-              </folding>
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="base.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/neighbors/base.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="195">
-              <caret line="414" column="0" lean-forward="false" selection-start-line="414" selection-start-column="0" selection-end-line="414" selection-end-column="0" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="examples.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/examples/examples.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="-1470">
-              <caret line="197" column="16" lean-forward="false" selection-start-line="197" selection-start-column="16" selection-end-line="197" selection-end-column="16" />
-              <folding>
-                <element signature="e#52#174#0" expanded="true" />
-              </folding>
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="LICENSE" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/LICENSE">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="330">
-              <caret line="22" column="0" lean-forward="false" selection-start-line="22" selection-start-column="0" selection-end-line="22" selection-end-column="0" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="README.rst" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/README.rst">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="480">
-              <caret line="120" column="16" lean-forward="false" selection-start-line="120" selection-start-column="16" selection-end-line="120" selection-end-column="16" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="README.md" pinned="false" current-in-tab="true">
-        <entry file="file://$PROJECT_DIR$/README.md">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="0">
-              <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="__init__.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/logging/__init__.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="15">
-              <caret line="1826" column="4" lean-forward="false" selection-start-line="1826" selection-start-column="4" selection-end-line="1826" selection-end-column="4" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="requirements.txt" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/requirements.txt">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="90">
-              <caret line="6" column="0" lean-forward="false" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="mico.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/mico/mico.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="404">
-              <caret line="171" column="0" lean-forward="false" selection-start-line="171" selection-start-column="0" selection-end-line="171" selection-end-column="0" />
-              <folding>
-                <element signature="e#142#160#0" expanded="true" />
-              </folding>
-            </state>
-          </provider>
-        </entry>
-      </file>
-      <file leaf-file-name="parallel.py" pinned="false" current-in-tab="false">
-        <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/joblib/parallel.py">
-          <provider selected="true" editor-type-id="text-editor">
-            <state relative-caret-position="195">
-              <caret line="1002" column="0" lean-forward="false" selection-start-line="1002" selection-start-column="0" selection-end-line="1002" selection-end-column="0" />
-              <folding />
-            </state>
-          </provider>
-        </entry>
-      </file>
-    </leaf>
-  </component>
-  <component name="FindInProjectRecents">
-    <findStrings>
-      <find>k=</find>
-      <find>n_fea</find>
-      <find>Early</find>
-      <find>selected</find>
-      <find>n_features</find>
-      <find>removed_feature= 2</find>
-      <find>bn</find>
-      <find>xy_MI</find>
-      <find>_are_data_binned</find>
-      <find>get_mutual_information_cd</find>
-      <find>NOT B</find>
-      <find>_get_mi</find>
-      <find>MI_FS</find>
-      <find>DEBUG</find>
-      <find>print(</find>
-      <find>get_mi_vector</find>
-      <find>get_mutual_information_dd</find>
-      <find>early_stop_steps</find>
-      <find>mico</find>
-      <find>y, x</find>
-      <find>1</find>
-      <find>MI_FS.X[:, s].reshape(n, 1)</find>
-      <find>HERE</find>
-      <find>HERE 2</find>
-      <find>joint</find>
-      <find>joint.shape</find>
-      <find>num_bins</find>
-      <find>level</find>
-      <find>setup_logging</find>
-      <find>x.shape</find>
-    </findStrings>
-    <replaceStrings>
-      <replace>get_mutual_information_2d</replace>
-      <replace>np</replace>
-      <replace>get_entropy_c</replace>
-      <replace>_is_integer</replace>
-      <replace>mico</replace>
-      <replace>_init_data</replace>
-      <replace>k</replace>
-      <replace>removed_feature</replace>
-      <replace>dist_to_k_neighbors</replace>
-      <replace>MI_FS.y.flatten().reshape(n, 1)</replace>
-    </replaceStrings>
-  </component>
-  <component name="IdeDocumentHistory">
-    <option name="CHANGED_PATHS">
-      <list>
-        <option value="$PROJECT_DIR$/README.rst" />
-        <option value="$PROJECT_DIR$/mifs/version.py" />
-        <option value="$PROJECT_DIR$/mico/setup.py" />
-        <option value="$PROJECT_DIR$/Makefile" />
-        <option value="$PROJECT_DIR$/mico/version.py" />
-        <option value="$PROJECT_DIR$/setup.py" />
-        <option value="$PROJECT_DIR$/mico/__init__.py" />
-        <option value="$PROJECT_DIR$/mico/mico_utils.py" />
-        <option value="$PROJECT_DIR$/requirements.txt" />
-        <option value="$PROJECT_DIR$/mico/mico.py" />
-        <option value="$PROJECT_DIR$/examples/examples.py" />
-        <option value="$PROJECT_DIR$/LICENSE" />
-      </list>
-    </option>
-  </component>
-  <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
-  <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
-  <component name="JsGulpfileManager">
-    <detection-done>true</detection-done>
-    <sorting>DEFINITION_ORDER</sorting>
-  </component>
-  <component name="ProjectFrameBounds">
-    <option name="x" value="104" />
-    <option name="y" value="44" />
-    <option name="width" value="1496" />
-    <option name="height" value="952" />
-  </component>
-  <component name="ProjectView">
-    <navigator currentView="ProjectPane" proportions="" version="1">
-      <flattenPackages />
-      <showMembers />
-      <showModules />
-      <showLibraryContents />
-      <hideEmptyPackages />
-      <abbreviatePackageNames />
-      <autoscrollToSource />
-      <autoscrollFromSource />
-      <sortByType />
-      <manualOrder />
-      <foldersAlwaysOnTop value="true" />
-    </navigator>
-    <panes>
-      <pane id="ProjectPane">
-        <subPane>
-          <PATH>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
-            </PATH_ELEMENT>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
-            </PATH_ELEMENT>
-          </PATH>
-          <PATH>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
-            </PATH_ELEMENT>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
-            </PATH_ELEMENT>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
-            </PATH_ELEMENT>
-          </PATH>
-          <PATH>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
-            </PATH_ELEMENT>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="mico" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
-            </PATH_ELEMENT>
-            <PATH_ELEMENT>
-              <option name="myItemId" value="examples" />
-              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
-            </PATH_ELEMENT>
-          </PATH>
-        </subPane>
-      </pane>
-      <pane id="Scope" />
-      <pane id="Scratches" />
-    </panes>
-  </component>
-  <component name="PropertiesComponent">
-    <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
-    <property name="nodejs_npm_path_reset_for_default_project" value="true" />
-    <property name="WebServerToolWindowFactoryState" value="true" />
-    <property name="nodejs_interpreter_path" value="/usr/local/bin/node" />
-    <property name="js.eslint.eslintPackage" value="" />
-    <property name="js-jscs-nodeInterpreter" value="/usr/local/bin/node" />
-    <property name="SearchEverywhereHistoryKey" value="&#9;FILE&#9;file:///Users/AF45008/Research/ColinML/mico/examples/examples.py" />
-    <property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
-  </component>
-  <component name="RunManager" selected="Python.examples">
-    <configuration default="false" name="examples" type="PythonConfigurationType" factoryName="Python" temporary="true">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs>
-        <env name="PYTHONUNBUFFERED" value="1" />
-      </envs>
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/examples" />
-      <option name="IS_MODULE_SDK" value="true" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/examples/examples.py" />
-      <option name="PARAMETERS" value="mibs" />
-      <option name="SHOW_COMMAND_LINE" value="false" />
-      <method />
-    </configuration>
-    <configuration default="false" name="mico_utils" type="PythonConfigurationType" factoryName="Python" temporary="true">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs>
-        <env name="PYTHONUNBUFFERED" value="1" />
-      </envs>
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/mico" />
-      <option name="IS_MODULE_SDK" value="true" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="$PROJECT_DIR$/mico/mico_utils.py" />
-      <option name="PARAMETERS" value="" />
-      <option name="SHOW_COMMAND_LINE" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="DjangoTestsConfigurationType" factoryName="Django tests">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs>
-        <env name="PYTHONUNBUFFERED" value="1" />
-      </envs>
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="TARGET" value="" />
-      <option name="SETTINGS_FILE" value="" />
-      <option name="CUSTOM_SETTINGS" value="false" />
-      <option name="USE_OPTIONS" value="false" />
-      <option name="OPTIONS" value="" />
-      <method />
-    </configuration>
-    <configuration default="true" type="JavaScriptTestRunnerProtractor" factoryName="Protractor">
-      <config-file value="" />
-      <node-interpreter value="project" />
-      <envs />
-      <method />
-    </configuration>
-    <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
-      <method />
-    </configuration>
-    <configuration default="true" type="PyBehaveRunConfigurationType" factoryName="Behave">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="ADDITIONAL_ARGS" value="" />
-      <method />
-    </configuration>
-    <configuration default="true" type="PyLettuceRunConfigurationType" factoryName="Lettuce">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="ADDITIONAL_ARGS" value="" />
-      <method />
-    </configuration>
-    <configuration default="true" type="PythonConfigurationType" factoryName="Python">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs>
-        <env name="PYTHONUNBUFFERED" value="1" />
-      </envs>
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="PARAMETERS" value="" />
-      <option name="SHOW_COMMAND_LINE" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="RRunConfigurationType" factoryName="R">
-      <option name="SCRIPT_PATH" value="" />
-      <option name="SCRIPT_ARGS" value="" />
-      <option name="WORKING_DIRECTORY_PATH" value="" />
-      <option name="PASS_PARENT_ENVS" value="true" />
-      <envs />
-      <method />
-    </configuration>
-    <configuration default="true" type="Tox" factoryName="Tox">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <module name="mico" />
-      <method />
-    </configuration>
-    <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">
-      <node-interpreter>project</node-interpreter>
-      <node-options />
-      <gulpfile />
-      <tasks />
-      <arguments />
-      <envs />
-      <method />
-    </configuration>
-    <configuration default="true" type="js.build_tools.npm" factoryName="npm">
-      <command value="run" />
-      <scripts />
-      <node-interpreter value="project" />
-      <envs />
-      <method />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="Attests">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="CLASS_NAME" value="" />
-      <option name="METHOD_NAME" value="" />
-      <option name="FOLDER_NAME" value="" />
-      <option name="TEST_TYPE" value="TEST_SCRIPT" />
-      <option name="PATTERN" value="" />
-      <option name="USE_PATTERN" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="Doctests">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="CLASS_NAME" value="" />
-      <option name="METHOD_NAME" value="" />
-      <option name="FOLDER_NAME" value="" />
-      <option name="TEST_TYPE" value="TEST_SCRIPT" />
-      <option name="PATTERN" value="" />
-      <option name="USE_PATTERN" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="Nosetests">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="CLASS_NAME" value="" />
-      <option name="METHOD_NAME" value="" />
-      <option name="FOLDER_NAME" value="" />
-      <option name="TEST_TYPE" value="TEST_SCRIPT" />
-      <option name="PATTERN" value="" />
-      <option name="USE_PATTERN" value="false" />
-      <option name="PARAMS" value="" />
-      <option name="USE_PARAM" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="Unittests">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="CLASS_NAME" value="" />
-      <option name="METHOD_NAME" value="" />
-      <option name="FOLDER_NAME" value="" />
-      <option name="TEST_TYPE" value="TEST_SCRIPT" />
-      <option name="PATTERN" value="" />
-      <option name="USE_PATTERN" value="false" />
-      <option name="PUREUNITTEST" value="true" />
-      <option name="PARAMS" value="" />
-      <option name="USE_PARAM" value="false" />
-      <method />
-    </configuration>
-    <configuration default="true" type="tests" factoryName="py.test">
-      <option name="INTERPRETER_OPTIONS" value="" />
-      <option name="PARENT_ENVS" value="true" />
-      <envs />
-      <option name="SDK_HOME" value="" />
-      <option name="WORKING_DIRECTORY" value="" />
-      <option name="IS_MODULE_SDK" value="false" />
-      <option name="ADD_CONTENT_ROOTS" value="true" />
-      <option name="ADD_SOURCE_ROOTS" value="true" />
-      <module name="mico" />
-      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
-      <option name="SCRIPT_NAME" value="" />
-      <option name="CLASS_NAME" value="" />
-      <option name="METHOD_NAME" value="" />
-      <option name="FOLDER_NAME" value="" />
-      <option name="TEST_TYPE" value="TEST_SCRIPT" />
-      <option name="PATTERN" value="" />
-      <option name="USE_PATTERN" value="false" />
-      <option name="testToRun" value="" />
-      <option name="keywords" value="" />
-      <option name="params" value="" />
-      <option name="USE_PARAM" value="false" />
-      <option name="USE_KEYWORD" value="false" />
-      <method />
-    </configuration>
-    <list size="2">
-      <item index="0" class="java.lang.String" itemvalue="Python.examples" />
-      <item index="1" class="java.lang.String" itemvalue="Python.mico_utils" />
-    </list>
-    <recent_temporary>
-      <list size="2">
-        <item index="0" class="java.lang.String" itemvalue="Python.examples" />
-        <item index="1" class="java.lang.String" itemvalue="Python.mico_utils" />
-      </list>
-    </recent_temporary>
-  </component>
-  <component name="ShelveChangesManager" show_recycled="false">
-    <option name="remove_strategy" value="false" />
-  </component>
-  <component name="TaskManager">
-    <task active="true" id="Default" summary="Default task">
-      <changelist id="4c701d04-f64b-46d7-b691-ae916125405f" name="Default" comment="" />
-      <created>1572111155220</created>
-      <option name="number" value="Default" />
-      <option name="presentableId" value="Default" />
-      <updated>1572111155220</updated>
-    </task>
-    <servers />
-  </component>
-  <component name="ToolWindowManager">
-    <frame x="104" y="44" width="1496" height="952" extended-state="0" />
-    <editor active="true" />
-    <layout>
-      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
-      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.37441862" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Remote Host" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.19257222" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
-      <window_info id="R Packages" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="R Graphics" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
-      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
-      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32962963" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32863188" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-    </layout>
-  </component>
-  <component name="TypeScriptGeneratedFilesManager">
-    <option name="processedProjectFiles" value="true" />
-  </component>
-  <component name="VcsContentAnnotationSettings">
-    <option name="myLimit" value="2678400000" />
-  </component>
-  <component name="XDebuggerManager">
-    <breakpoint-manager>
-      <option name="time" value="6" />
-    </breakpoint-manager>
-    <watches-manager />
-  </component>
-  <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/mico/setup.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="150">
-          <caret line="10" column="9" lean-forward="false" selection-start-line="10" selection-start-column="9" selection-end-line="10" selection-end-column="9" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/README.rst">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="1800">
-          <caret line="120" column="16" lean-forward="true" selection-start-line="120" selection-start-column="16" selection-end-line="120" selection-end-column="16" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/mico_utils.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="3540">
-          <caret line="242" column="36" lean-forward="false" selection-start-line="242" selection-start-column="4" selection-end-line="242" selection-end-column="36" />
-          <folding>
-            <element signature="e#227#245#0" expanded="true" />
-            <element signature="e#5952#6471#0" expanded="false" />
-            <element signature="e#6868#7546#0" expanded="false" />
-            <marker date="1573419526985" expanded="true" signature="4896:7937" ph="..." />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="225">
-          <caret line="17" column="45" lean-forward="true" selection-start-line="17" selection-start-column="45" selection-end-line="17" selection-end-column="45" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/requires.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/SOURCES.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/top_level.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/examples/examples.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding>
-            <element signature="e#52#174#0" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/mico.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="6405">
-          <caret line="469" column="22" lean-forward="true" selection-start-line="469" selection-start-column="22" selection-end-line="469" selection-end-column="22" />
-          <folding>
-            <element signature="e#142#160#0" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/version.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="660">
-          <caret line="44" column="24" lean-forward="true" selection-start-line="44" selection-start-column="24" selection-end-line="44" selection-end-column="30" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/setup.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="150">
-          <caret line="10" column="9" lean-forward="false" selection-start-line="10" selection-start-column="9" selection-end-line="10" selection-end-column="9" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mifs/setup.py" />
-    <entry file="file://$PROJECT_DIR$/mifs/version.py" />
-    <entry file="file://$PROJECT_DIR$/mico/mi.py" />
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/numpy/distutils/misc_util.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="218">
-          <caret line="1840" column="13" lean-forward="false" selection-start-line="1840" selection-start-column="12" selection-end-line="1840" selection-end-column="13" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/Makefile">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="300">
-          <caret line="20" column="0" lean-forward="true" selection-start-line="20" selection-start-column="0" selection-end-line="20" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/feature_selection/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/feature_selection/variance_threshold.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="90">
-          <caret line="11" column="6" lean-forward="false" selection-start-line="11" selection-start-column="6" selection-end-line="11" selection-end-column="6" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$USER_HOME$/Library/Caches/PyCharm2016.3/python_stubs/917108635/numpy/core/_multiarray_umath.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="-312">
-          <caret line="5006" column="4" lean-forward="false" selection-start-line="5006" selection-start-column="4" selection-end-line="5006" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/preprocessing/data.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="-1377">
-          <caret line="489" column="6" lean-forward="false" selection-start-line="489" selection-start-column="6" selection-end-line="489" selection-end-column="6" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/datasets/samples_generator.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="15">
-          <caret line="35" column="4" lean-forward="false" selection-start-line="35" selection-start-column="4" selection-end-line="35" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/pyitlib/discrete_random_variable.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="183">
-          <caret line="2268" column="4" lean-forward="false" selection-start-line="2268" selection-start-column="4" selection-end-line="2268" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/feature_selection/base.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="15">
-          <caret line="16" column="6" lean-forward="false" selection-start-line="16" selection-start-column="6" selection-end-line="16" selection-end-column="6" />
-          <folding>
-            <element signature="e#150#189#0" expanded="false" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/codecs.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/setuptools/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="183">
-          <caret line="122" column="0" lean-forward="false" selection-start-line="122" selection-start-column="0" selection-end-line="122" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/setup.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="384">
-          <caret line="52" column="28" lean-forward="true" selection-start-line="52" selection-start-column="28" selection-end-line="52" selection-end-column="28" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/dependency_links.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/not-zip-safe">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/PKG-INFO">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/requires.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/SOURCES.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico.egg-info/top_level.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/version.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="660">
-          <caret line="44" column="24" lean-forward="false" selection-start-line="44" selection-start-column="24" selection-end-line="44" selection-end-column="30" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="225">
-          <caret line="17" column="45" lean-forward="false" selection-start-line="17" selection-start-column="45" selection-end-line="17" selection-end-column="45" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/setup.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="150">
-          <caret line="10" column="9" lean-forward="false" selection-start-line="10" selection-start-column="9" selection-end-line="10" selection-end-column="9" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/bottleneck/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$USER_HOME$/Library/Caches/PyCharm2016.3/python_stubs/917108635/bottleneck/reduce.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="3375">
-          <caret line="225" column="4" lean-forward="false" selection-start-line="225" selection-start-column="4" selection-end-line="225" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$USER_HOME$/Library/Caches/PyCharm2016.3/python_stubs/917108635/builtins.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="7320">
-          <caret line="488" column="4" lean-forward="false" selection-start-line="488" selection-start-column="4" selection-end-line="488" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/numpy/core/getlimits.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="153">
-          <caret line="380" column="0" lean-forward="false" selection-start-line="380" selection-start-column="0" selection-end-line="380" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/numpy/core/numeric.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="180">
-          <caret line="2394" column="0" lean-forward="false" selection-start-line="2394" selection-start-column="0" selection-end-line="2394" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/joblib/parallel.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="195">
-          <caret line="1002" column="0" lean-forward="false" selection-start-line="1002" selection-start-column="0" selection-end-line="1002" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/site-packages/sklearn/neighbors/base.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="195">
-          <caret line="414" column="0" lean-forward="false" selection-start-line="414" selection-start-column="0" selection-end-line="414" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/mico_utils.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="15">
-          <caret line="111" column="4" lean-forward="false" selection-start-line="111" selection-start-column="4" selection-end-line="111" selection-end-column="4" />
-          <folding>
-            <element signature="e#227#245#0" expanded="true" />
-            <element signature="e#5952#6471#0" expanded="false" />
-            <element signature="e#6868#7546#0" expanded="false" />
-            <marker date="1573419526985" expanded="true" signature="4896:7937" ph="..." />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/../mifs-master/conda_env/lib/python3.7/logging/__init__.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="15">
-          <caret line="1826" column="4" lean-forward="false" selection-start-line="1826" selection-start-column="4" selection-end-line="1826" selection-end-column="4" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/requirements.txt">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="90">
-          <caret line="6" column="0" lean-forward="false" selection-start-line="6" selection-start-column="0" selection-end-line="6" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/mico/mico.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="404">
-          <caret line="171" column="0" lean-forward="false" selection-start-line="171" selection-start-column="0" selection-end-line="171" selection-end-column="0" />
-          <folding>
-            <element signature="e#142#160#0" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/examples/examples.py">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="-1470">
-          <caret line="197" column="16" lean-forward="false" selection-start-line="197" selection-start-column="16" selection-end-line="197" selection-end-column="16" />
-          <folding>
-            <element signature="e#52#174#0" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/LICENSE 2">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="300">
-          <caret line="20" column="12" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="27" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/LICENSE">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="330">
-          <caret line="22" column="0" lean-forward="false" selection-start-line="22" selection-start-column="0" selection-end-line="22" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/README.rst">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="480">
-          <caret line="120" column="16" lean-forward="false" selection-start-line="120" selection-start-column="16" selection-end-line="120" selection-end-column="16" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/README.md">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-  </component>
-</project>
\ No newline at end of file
diff --git a/LICENSE b/LICENSE.txt
similarity index 56%
rename from LICENSE
rename to LICENSE.txt
index ca41287..ce4944f 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,27 +1,6 @@
-MIT License
+The 3-Clause BSD License
 
 Copyright (c) 2019 Kuo-Ling Huang
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-Copyright (c) 2016, Daniel Homola
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -34,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 
-* Neither the name of mifs nor the names of its
+* Neither the name of copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.
 
@@ -48,3 +27,14 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Note
+----
+
+MICO is heavily inspired from the following open source package:
+
+MIFS: Parallelized Mutual Information based Feature Selection module.
+
+- Github repo : https://github.com/danielhomola/mifs
+- License     : The 3-Clause BSD License
+- Author      : Daniel Homola
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 0fe21ca..0000000
--- a/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-## Welcome to GitHub Pages
-
-You can use the [editor on GitHub](https://github.com/jupiters1117/mico/edit/master/README.md) to maintain and preview the content for your website in Markdown files.
-
-Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
-
-### Markdown
-
-Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
-
-```markdown
-Syntax highlighted code block
-
-# Header 1
-## Header 2
-### Header 3
-
-- Bulleted
-- List
-
-1. Numbered
-2. List
-
-**Bold** and _Italic_ and `Code` text
-
-[Link](url) and ![Image](src)
-```
-
-For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
-
-### Jekyll Themes
-
-Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/jupiters1117/mico/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
-
-### Support or Contact
-
-Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we’ll help you sort it out.
diff --git a/README.rst b/README.rst
old mode 100755
new mode 100644
index 540e103..99ac390
--- a/README.rst
+++ b/README.rst
@@ -1,128 +1,137 @@
 .. -*- mode: rst -*-
+.. template: https://raw.githubusercontent.com/scikit-learn-contrib/stability-selection/master/README.md
+.. https://raw.githubusercontent.com/scikit-learn-contrib/hdbscan/master/README.rst
 
-MICO
-====
+.. image:: https://img.shields.io/pypi/l/colin-mico.svg
+    :target: https://github.com/jupiters1117/mico/master/LICENSE
+    :alt: License
 
-MICO: Mutual Information and Conic Optimization for feature selection.
+MICO: Mutual Information and Conic Optimization for feature selection
+---------------------------------------------------------------------
 
-Related blog post here_
+**MICO** is a Python package that implements a conic optimization based feature selection method with mutual information (MI) measure [1]_. The idea behind the approach is to measure the features’relevance and redundancy using MI, and then formulate a feature selection problem as a pure-binary quadratic optimization problem, which can be heuristically solved by an efficient randomization algorithm via semidefinite programming [2]_. Optimization software **Colin** [6]_ is used for solving the underlying conic optimization problems.
 
-.. _here: http://danielhomola.com/2016/01/31/mifs-parallelized-mutual-information-based-feature-selection-module/)
+This package
 
-Dependencies
+- implements three methods for feature selections:
+
+  + **MICO** : Conic Optimization approach
+  + **MIFS** : Forward Selection approach
+  + **MIBS** : Backward Selection approach
+
+- supports three different MI measures:
+
+  + **JMI** : Joint Mutual Information [3]_
+  + **JMIM** : Joint Mutual Information Maximisation [4]_
+  + **MRMR** : Max-Relevance Min-Redundancy [5]_
+
+- generates feature importance scores for all selected features.
+- provides scikit-learn compatible APIs.
+
+
+Installation
 ------------
 
-* scipy(>=0.17.0)
-* numpy(>=1.10.4)
-* scikit-learn(>=0.17.1)
-* bottleneck(>=1.1.0)
+1. Download **Colin** distribution from http://www.colinopt.org/downloads.php and unpack it into a chosen directory (`<CLNHOME>`).
+   Then install **Colin** package:
 
-How to use
-----------
+.. code-block:: bash
 
-Download, import and do as you would with any other scikit-learn method:
+    cd <CLNHOME>/python
+    pip install -r requirements.txt
+    python setup.py install
 
-* ``fit(X, y)``
-* ``transform(X)``
-* ``fit_transform(X, y)``
+2. To install **MICO** package, use:
 
-Description
------------
+.. code-block:: bash
 
-MIFS stands for Mutual Information based Feature Selection. This class contains routines for selecting features using both continuous and discrete y variables. Three selection algorithms are implemented: JMI, JMIM and MRMR.
+    pip install -r requirements.txt
+    python setup.py install
 
-This implementation tries to mimic the scikit-learn interface, so use fit, transform or fit_transform, to run the feature selection.
+or
 
-See examples/example.py for well examples and usage.
+.. code-block:: bash
 
-Docs
-----
+    pip install colin-mico
+
+To install the development version, you may use:
+
+.. code-block:: bash
 
-Parameters
-~~~~~~~~~~
+    pip install --upgrade git+https://github.com/jupiters1117/mico
 
-**method** : string, default = 'JMI'::
 
-  > Which mutual information based feature selection method to use:
-  > * 'JMI' : Joint Mutual Information [1]
-  > * 'JMIM' : Joint Mutual Information Maximisation [2]
-  > * 'MRMR' : Max-Relevance Min-Redundancy [3]
+Usage
+-----
 
-**k** : int, default = 5::
+This package provides scikit-learn compatible APIs:
 
-  > Sets the number of samples to use for the kernel density estimation with the kNN method. Kraskov et al. recommend a small integer between 3 and 10.
+* ``fit(X, y)``
+* ``transform(X)``
+* ``fit_transform(X, y)``
 
-**n_features** : int or string, default = 'auto'::
 
-  > If int, it sets the number of features that has to be selected from X. If 'auto' this is determined automatically based on the amount of mutual information the previously selected features share with y.
+Examples
+--------
 
-**categorical** : Boolean, default = True::
+The following example illustrates the use of the package:
 
-  > If True, y is assumed to be a categorical class label. If False, y is treated as a continuous. Consequently this parameter determines the method of estimation of the MI between the predictors in X and y.
+.. code-block:: python
 
-**verbose** : int, default=0::
+    import pandas as pd
+    from sklearn.datasets import load_breast_cancer
 
-  > Controls verbosity of output:
-  > * 0: no output
-  > * 1: displays selected features
-  > * 2: displays selected features and mutual information
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
 
-Attributes
-~~~~~~~~~~
+    # Perform feature selection.
+    mico = MutualInformationConicOptimization(verbose=1, categorical=True)
+    mico.fit(X, y)
 
-**n_features** : int::
+    # Populate selected features.
+    print("Selected features: {}".format(mico.get_support()))
 
-  > The number of selected features.
+    # Populate feature importance scores.
+    print("Feature importance scores: {}".format(mico.feature_importances_))
 
-**support** : array of length [number of features in X]::
+    # Call transform() on X.
+    X_transformed = mico.transform(X)
 
-  > The mask array of selected features.
 
-**ranking** : array of shape [n_features]::
+Documentation
+-------------
 
-  > The feature ranking of the selected features, with the first being the first feature selected with largest marginal MI with y, followed by the others with decreasing MI.
+User guide, examples, and API are available `here <https://jupiters1117.github.io/mico/>`_.
 
-**mi** : array of shape n_features::
 
-  > The JMIM of the selected features. Usually this a monotone decreasing array of numbers converging to 0. One can use this to estimate the number of features to select. In fact this is what n_features='auto' tries to do heuristically.
+References
+----------
 
-Examples
-~~~~~~~~
+.. [1] T Naghibi, S Hoffmann and B Pfister, "A semidefinite programming based search strategy for feature selection with mutual information measure", IEEE Transactions on Pattern Analysis and Machine Intelligence, 37(8), pp. 1529--1541, 2015. [`Pre-print <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.739.8516&rep=rep1&type=pdf>`_]
+.. [2] M Goemans and D Williamson, "Improved approximation algorithms for maximum cut and satisfiability problems using semidefinite programming", J. ACM, 42(6), pp. 1115--1145, 1995 [`Pre-print <http://www-math.mit.edu/~goemans/PAPERS/maxcut-jacm.pdf>`_]
+.. [3] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+.. [4] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+.. [5] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
+.. [6] Colin: Conic-form Linear Optimizer (www.colinopt.org).
 
-The following example illustrates the use of the package::
 
-    import pandas as pd
-    import mifs
+Credits
+-------
+
+- KuoLing Huang, 2019-presents
 
-    # load X and y
-    X = pd.read_csv('my_X_table.csv', index_col=0).values
-    y = pd.read_csv('my_y_vector.csv', index_col=0).values
 
-    # define MI_FS feature selection method
-    feat_selector = mifs.MutualInformationFeatureSelector()
+Licensing
+---------
 
-    # find all relevant features
-    feat_selector.fit(X, y)
+**MICO** is 3-clause BSD licensed.
 
-    # check selected features
-    feat_selector.support_
 
-    # check ranking of features
-    feat_selector.ranking_
+Note
+----
+
+**MICO** is heavily inspired from `MIFS: Parallelized Mutual Information based Feature Selection module <https://github.com/danielhomola/mifs>`_ by Daniel Homola.
 
-    # call transform() on X to filter it down to selected features
-    X_filtered = feat_selector.transform(X)
 
-References
-~~~~~~~~~~
-
-.. [1] H. Yang and J. Moody, "Data Visualization and Feature Selection: New
-       Algorithms for Nongaussian Data"
-       NIPS 1999
-.. [2] Bennasar M., Hicks Y., Setchi R., "Feature selection using Joint Mutual
-       Information Maximisation"
-       Expert Systems with Applications, Vol. 42, Issue 22, Dec 2015
-.. [3] H. Peng, Fulmi Long, C. Ding, "Feature selection based on mutual
-       information criteria of max-dependency, max-relevance,
-       and min-redundancy"
-       Pattern Analysis & Machine Intelligence 2005
diff --git a/README.rst.short b/README.rst.short
new file mode 100644
index 0000000..5d70189
--- /dev/null
+++ b/README.rst.short
@@ -0,0 +1,111 @@
+.. -*- mode: rst -*-
+.. template: https://raw.githubusercontent.com/scikit-learn-contrib/stability-selection/master/README.md
+.. https://raw.githubusercontent.com/scikit-learn-contrib/hdbscan/master/README.rst
+
+
+MICO: Mutual Information and Conic Optimization for feature selection
+---------------------------------------------------------------------
+
+**MICO** is a Python package that implements a conic optimization based feature selection method with mutual information (MI) measure. The idea behind the approach is to measure the features’relevance and redundancy using MI, and then formulate a feature selection problem as a pure-binary quadratic optimization problem, which can be heuristically solved by an efficient randomization algorithm via semidefinite programming. Optimization software **Colin** is used for solving the underlying conic optimization problems.
+
+This package
+
+- implements three methods for feature selections:
+
+  + **MICO** : Conic Optimization approach
+  + **MIFS** : Forward Selection approach
+  + **MIBS** : Backward Selection approach
+
+- supports three different MI measures:
+
+  + **JMI** : Joint Mutual Information
+  + **JMIM** : Joint Mutual Information Maximisation
+  + **MRMR** : Max-Relevance Min-Redundancy
+
+- generates feature importance scores for all selected features.
+- provides scikit-learn compatible APIs.
+
+
+Installation
+------------
+
+1. Download **Colin** distribution from http://www.colinopt.org/downloads.php and unpack it into a chosen directory (`<CLNHOME>`).
+   Then install **Colin** package:
+
+.. code-block:: bash
+
+    cd <CLNHOME>/python
+    pip install -r requirements.txt
+    python setup.py install
+
+
+2. To install **MICO** package, use:
+
+.. code-block:: bash
+
+    pip install -r requirements.txt
+    python setup.py install
+
+or
+
+.. code-block:: bash
+
+    python setup.py install
+
+or
+
+.. code-block:: bash
+
+    pip install colin-mico
+
+To install the development version, you may use:
+
+.. code-block:: bash
+
+    pip install --upgrade git+https://github.com/jupiters1117/mico
+
+
+Usage
+-----
+
+This package provides scikit-learn compatible APIs:
+
+* ``fit(X, y)``
+* ``transform(X)``
+* ``fit_transform(X, y)``
+
+
+Examples
+--------
+
+The following example illustrates the use of the package:
+
+.. code-block:: python
+
+    import pandas as pd
+    from sklearn.datasets import load_breast_cancer
+
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+
+    # Perform feature selection.
+    mico = MutualInformationConicOptimization(verbose=1, categorical=True)
+    mico.fit(X, y)
+
+    # Populate selected features.
+    print("Selected features: {}".format(mico.get_support()))
+
+    # Populate feature importance scores.
+    print("Feature importance scores: {}".format(mico.feature_importances_))
+
+    # Call transform() on X.
+    X_transformed = mico.transform(X)
+
+
+Documentation
+-------------
+
+User guide, examples, and API are available `here <https://jupiters1117.github.io/mico/>`_.
+
diff --git a/check.py b/check.py
new file mode 100644
index 0000000..557934f
--- /dev/null
+++ b/check.py
@@ -0,0 +1,7 @@
+import colinpy
+print(dir(colinpy))
+
+import mico
+print(dir(mico))
+import sys
+print("\n".join(sys.path))
diff --git a/doc/api.rst b/doc/api.rst
deleted file mode 100755
index 04fee1b..0000000
--- a/doc/api.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-##########
-`MIFS` API
-##########
-
-This is the full API documentation of the `MIFS` toolbox.
-
-.. _mifs_ref:
-
-MIFS methods
-============
-
-.. automodule:: mifs
-    :no-members:
-    :no-inherited-members:
-
-Classes
--------
-.. currentmodule:: mifs
-
-.. autosummary::
-   :toctree: generated/
-
-   MutualInformationFeatureSelector
diff --git a/doc/index.rst b/doc/index.rst
deleted file mode 100755
index 97d53b2..0000000
--- a/doc/index.rst
+++ /dev/null
@@ -1,29 +0,0 @@
-.. project-template documentation master file, created by
-   sphinx-quickstart on Mon Jan 18 14:44:12 2016.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-##############################
-Welcome to MIFS documentation!
-##############################
-
-Contents:
-=========
-
-.. toctree::
-   :maxdepth: 2
-   :hidden:
-   :caption: API Documentation
-
-   api
-
-See the `README <https://github.com/vighneshbirodkar/project-template/blob/master/README.md>`_
-for more information.
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/doc/Makefile b/docs/Makefile
similarity index 100%
rename from doc/Makefile
rename to docs/Makefile
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100755
index 0000000..f579230
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,35 @@
+API
+===
+
+This is the full API documentation of the **MICO**.
+
+
+.. _Conic optimization approach for feature selection:
+
+Conic optimization approach for feature selection
+-------------------------------------------------
+
+.. autoclass:: mico.MutualInformationConicOptimization
+   :inherited-members:
+   :members:
+
+.. _Backward elimination approach for feature selection:
+
+Backward elimination approach for feature selection
+---------------------------------------------------
+
+.. autoclass:: mico.MutualInformationBackwardElimination
+   :inherited-members:
+   :members:
+
+.. _Forward selection approach for feature selection:
+
+Forward selection approach for feature selection
+------------------------------------------------
+
+.. autoclass:: mico.MutualInformationForwardSelection
+   :inherited-members:
+   :members:
+
+
+
diff --git a/doc/conf.py b/docs/conf.py
similarity index 87%
rename from doc/conf.py
rename to docs/conf.py
index e39fed3..38721e9 100755
--- a/doc/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# mifs documentation build configuration file, created by
+# mico documentation build configuration file, created by
 # sphinx-quickstart on Mon Jan 18 14:44:12 2016.
 #
 # This file is execfile()d with the current directory set to its
@@ -39,17 +39,18 @@
 # ones.
 extensions = [
     'sphinx.ext.autodoc',
+    'sphinx.ext.autosummary',
     'sphinx.ext.doctest',
     'sphinx.ext.intersphinx',
     'sphinx.ext.todo',
     'numpydoc',
-    'sphinx.ext.pngmath',
     'sphinx.ext.ifconfig',
     'sphinx.ext.viewcode',
     'sphinx_gallery.gen_gallery',
-    'sphinx.ext.autosummary'
+    'sphinx.ext.mathjax'
 ]
 
+'''
 autosummary_generate = True
 
 autodoc_default_flags = ['members', 'inherited-members']
@@ -59,35 +60,55 @@
     'examples_dirs' : '../examples',
     # path where to save gallery generated examples
     'gallery_dirs'  : 'auto_examples'}
+'''
+
+numpydoc_show_class_members = False
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
+# https://sphinx-gallery.github.io/stable/configuration.html#controlling-what-output-is-captured
+sphinx_gallery_conf = {
+    'line_numbers': 'True',
+    # path to your examples scripts
+    'examples_dirs': '../examples',
+    # path where to save gallery generated examples
+    'gallery_dirs': 'auto_examples',
+    #'backreferences_dir': os.path.join('generated'),
+    # Generate the plots for the gallery
+    'plot_gallery': 'True',
+    'capture_repr': ('_repr_html_', '__repr__', '__str__'),
+    # https://sphinx-gallery.github.io/stable/configuration.html#build-pattern
+    'filename_pattern': '',
+    'ignore_pattern': r'__init__\.py',
+}
+
+
 # generate autosummary even if no references
 autosummary_generate = True
 
+
 # The suffix of source filenames.
 source_suffix = '.rst'
 
 # The encoding of source files.
 #source_encoding = 'utf-8-sig'
 
-# Generate the plots for the gallery
-plot_gallery = True
-
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
-project = u'mifs'
-copyright = u'2016, G. Lemaitre, F. Nogueira, D. Oliveira, C. Aridas'
+project = u'MICO'
+copyright = u'2019, KuoLing Huang'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-__version__ = '0.2.0.dev0'
+#__version__ = '0.1.0.dev0'
+import mico
+__version__ = mico.__version__
 version = __version__
 # The full version, including alpha/beta/rc tags.
 release = __version__
@@ -164,7 +185,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ['./_build/html/_static']
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
@@ -213,7 +234,7 @@
 #html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'mifsdoc'
+htmlhelp_basename = 'micodoc'
 
 
 # -- Options for LaTeX output ---------------------------------------------
@@ -233,8 +254,8 @@
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', 'mifs.tex', u'mifs Documentation',
-     u'D. Homola', 'manual'),
+    ('index', 'mico.tex', u'mico Documentation',
+     u'K.L. Huang', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -263,8 +284,8 @@
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'mifs', u'mifs Documentation',
-     [u'D. Homola'], 1)
+    ('index', 'mico', u'mico Documentation',
+     [u'K.L. Huang'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -277,11 +298,12 @@
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    ('index', 'mifs', u'mifs Documentation',
-     u'D. Homola', 'mifs', 'Parallelized Mutual Information based Feature Selection module.',
+    ('index', 'mico', u'mico Documentation',
+     u'K.L. Huang', 'mico', 'Mutual Information and Conic Optimization for feature selection.',
      'Miscellaneous'),
 ]
 
+'''
 def generate_example_rst(app, what, name, obj, options, lines):
     # generate empty examples files, so that we don't get
     # inclusion errors if there are no examples for a class / module
@@ -293,7 +315,9 @@ def generate_example_rst(app, what, name, obj, options, lines):
 
 
 def setup(app):
+    #pass
     app.connect('autodoc-process-docstring', generate_example_rst)
+'''
 
 # Documents to append as an appendix to all manuals.
 #texinfo_appendices = []
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100755
index 0000000..ea25385
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,65 @@
+.. project-template documentation master file, created by
+   sphinx-quickstart on Mon Jan 18 14:44:12 2016.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+
+
+Welcome to MICO's documentation!
+================================
+
+
+MICO: Mutual Information and Conic Optimization for feature selection
+---------------------------------------------------------------------
+
+**MICO** is a Python package that implements a conic optimization based feature selection method with mutual information (MI) measure [1]_. The idea behind the approach is to measure the features’relevance and redundancy using MI, and formulate a feature selection problem as a pure-binary quadratic optimization problem, which can be heuristically solved by an efficient randomization algorithm via semidefinite programming [2]_. Optimization software **Colin** [6]_ is used for solving the underlying conic optimization problems.
+
+This package
+
+- implements three methods for feature selections:
+
+  + **MICO** : :ref:`Conic optimization approach for feature selection` (main approach)
+  + **MIFS** : :ref:`Backward elimination approach for feature selection`
+  + **MIBS** : :ref:`Forward selection approach for feature selection` (less expensive)
+
+- supports three different MI measures:
+
+  + **JMI** : Joint Mutual Information [3]_
+  + **JMIM** : Joint Mutual Information Maximisation [4]_
+  + **MRMR** : Max-Relevance Min-Redundancy [5]_
+
+- generates feature importance scores for all selected features.
+- provides scikit-learn compatible APIs.
+
+Documentation Outline
+---------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   install
+   api
+   auto_examples/index
+
+.. See the `README <https://github.com/thuijskens/stability-selection/blob/master/README.md>`_ for more information.
+
+
+References
+----------
+
+.. [1] T Naghibi, S Hoffmann and B Pfister, "A semidefinite programming based search strategy for feature selection with mutual information measure", IEEE Transactions on Pattern Analysis and Machine Intelligence, 37(8), pp. 1529--1541, 2015. [`Pre-print <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.739.8516&rep=rep1&type=pdf>`_]
+.. [2] M Goemans and D Williamson, "Improved approximation algorithms for maximum cut and satisfiability problems using semidefinite programming", J. ACM, 42(6), pp. 1115--1145, 1995 [`Pre-print <http://www-math.mit.edu/~goemans/PAPERS/maxcut-jacm.pdf>`_]
+.. [3] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+.. [4] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+.. [5] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
+.. [6] Colin: Conic-form Linear Optimizer (`www.colinopt.org <www.colinopt.org>`_).
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+
diff --git a/docs/install.rst b/docs/install.rst
new file mode 100755
index 0000000..b8f06d5
--- /dev/null
+++ b/docs/install.rst
@@ -0,0 +1,41 @@
+Installation
+============
+
+1. Download **Colin** distribution from http://www.colinopt.org/downloads.php and unpack it into a chosen directory (`<CLNHOME>`).
+   Then install **Colin** package:
+
+.. code-block:: bash
+
+    cd <CLNHOME>/python
+    pip install -r requirements.txt
+    python setup.py install
+
+
+2. To install **MICO** package, use:
+
+.. code-block:: bash
+
+    pip install -r requirements.txt
+    python setup.py install
+
+or
+
+.. code-block:: bash
+
+    pip install colin-mico
+
+To install the development version, you may use:
+
+.. code-block:: bash
+
+    pip install --upgrade git+https://github.com/jupiters1117/mico
+
+
+Usage
+-----
+
+This package provides scikit-learn compatible APIs:
+
+* ``fit(X, y)``
+* ``transform(X)``
+* ``fit_transform(X, y)``
diff --git a/docs/run.sh b/docs/run.sh
new file mode 100644
index 0000000..83a0a68
--- /dev/null
+++ b/docs/run.sh
@@ -0,0 +1,3 @@
+python ../setup.py install 
+#make html
+sphinx-build -b html -d _build/doctrees .  ../gh-pages/ 
diff --git a/examples/README.rst b/examples/README.rst
new file mode 100755
index 0000000..af7af45
--- /dev/null
+++ b/examples/README.rst
@@ -0,0 +1,6 @@
+.. _general_examples:
+
+General examples
+================
+
+Introductory examples.
\ No newline at end of file
diff --git a/examples/examples.py b/examples/examples.py
deleted file mode 100755
index 0b3423e..0000000
--- a/examples/examples.py
+++ /dev/null
@@ -1,373 +0,0 @@
-"""
-Example showing the use of the mico module.
-"""
-from mico import MutualInformationForwardSelection, MutualInformationBackwardSelection, MutualInformationConicOptimization
-from sklearn.datasets import make_classification, make_regression
-import numpy as np 
-import pyitlib
-#from mico import get_entropy
-#from ..mico.mico_utils import get_entropy
-#import mico.mi
-#import sys
-import math
-#sys.path.append("..")
-from scipy.special import gamma, psi
-from sklearn.neighbors import NearestNeighbors
-import pandas as pd
-import copy
-from scipy import sparse
-
-import logging
-import argparse
-
-# Python code to generate
-# random numbers and
-# append them to a list
-import random
-
-
-def setup_logging(level):
-    logging.basicConfig(format='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p', level=level)
-
-
-# Function to generate
-# and append them
-# start = starting range,
-# end = ending range
-# num = number of
-# elements needs to be appended
-def get_rand_list(start, end, num):
-    res = []
-
-    for j in range(num):
-        res.append(random.randint(start, end))
-
-    return res
-
-
-def check_selection(selected, i, r):
-    """
-    Check FN, FP, TP ratios among the selected features.
-    """
-    # reorder selected features
-    try:
-        selected = set(selected)
-        all_f = set(range(i+r))
-        TP = len(selected.intersection(all_f))
-        FP = len(selected - all_f)
-        FN =  len(all_f - selected)
-        if (TP+FN) > 0: 
-            sens = TP/float(TP + FN)
-        else:
-            sens = np.nan
-        if (TP+FP) > 0:
-            prec =  TP/float(TP + FP)   
-        else:
-            prec = np.nan
-    except:
-        sens = np.nan
-        prec = np.nan
-    return sens, prec
-    
-
-def test_mi():
-    import numpy as np
-    from pyitlib import discrete_random_variable as drv
-
-    #X = np.array((1.0, 2.0, 1.0, 2.0, 4.12, 4.123, 6.123))
-    #print(drv.entropy(X))
-    X = np.array((1.0, 2.0, 1.0, 2.0, 4.123, 4.123, 10.123, 1.1))
-    #print(drv.entropy(X))
-    #print(pd.qcut(np.array(X), 4, retbins=True))
-    #print(pd.qcut(np.array(X), 4, retbins=True)[1])
-
-    if False:
-        # Test bin.
-        print("=" * 80)
-        print("Testing bin")
-        print("-" * 80)
-        num_bins = 3
-
-        # - Binned with pandas.
-        pd_bins = pd.cut(np.array(X), num_bins, retbins=True)[1]
-        labels = [i for i in range(num_bins)]
-        pd_data = np.array(pd.cut(X, bins=pd_bins , labels=labels, include_lowest=True))
-        print("Binned with PD.")
-        print(" - Bin  : {}".format(pd_bins))
-        print(" - Data : {}".format(pd_data))
-        #print(type(pd.cut(X, bins=bins, labels=labels, include_lowest=True)))
-
-        # - Binned with NP.
-        np_bin_res = np.histogram(X, bins=num_bins)
-        np_data = np_bin_res[0]
-        np_bins = np_bin_res[1]
-        print("Binned with NP.")
-        print(" - Bin  : {}".format(np_bins))
-        print(" - Data : {}".format(np_data))
-
-
-    # - Binned with SKL.
-    X = np.array([[ -3., 5., 15 ],
-                  [ 0.,  6., 14 ],
-                  [ 6.,  3., 11 ],
-                  [ 16., -3.,13 ],
-                  [ 12., -3.,10 ],
-                  [ 16.4, -2.,11.2 ],
-                  [ 26., 3., 211]
-                 ]
-    )
-    skl_data = bin_data(X, num_bins=3)
-    print("Binned with SKL.")
-    print(" - Num features : {}".format(X.shape[1]))
-    #print(" - Bin  : {}".format(pd_bins))
-    print(" - Data :\n {}".format(skl_data))
-
-    print("Scaled dense matrix with SKL.")
-    den_data = scale_data(X)
-    print(" - Num features : {}".format(den_data.shape[1]))
-    print(" - Data :\n {}".format(den_data))
-    print(" - Mean :\n {}".format(den_data.mean(axis=0)))
-    print(" - Std  :\n {}".format(den_data.std(axis=0)))
-
-    print("Scaled sparse matrix with SKL.")
-    csc_data = sparse.csc_matrix(X)
-    csr_data = csc_data.tocsr()
-    spa_data = scale_data(csr_data)
-    print(" - Num features : {}".format(spa_data.shape[1]))
-    print(" - Mean :\n {}".format(spa_data.mean(axis=0)))
-
-
-    print("=" * 80)
-
-    #h = np.histogram2d(X, Y, bins=bins)[0]
-    #print(X)
-
-    #print(np.array(df['qcut4']))
-    #print(drv.entropy(data))
-    #print(np.hstack(np.array([X, X])))
-
-
-
-def scale_data(X):
-    """"
-    Scale the data using SKL.
-
-    References
-    ----------
-    [1] http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html
-    """
-    from sklearn.preprocessing import MinMaxScaler, StandardScaler, MaxAbsScaler
-
-    # X must be stored in a row-wise order.
-    if sparse.issparse(X):
-        scalar = MaxAbsScaler()
-    else:
-        scalar = StandardScaler()#MinMaxScaler() # StandardScaler
-
-    return scalar.fit_transform(X)
-
-
-def bin_data(X, num_bins):
-    """"
-    Bin the data using SKL.
-    """
-    # X must be stored in a row-wise order.
-    from sklearn.preprocessing import KBinsDiscretizer
-
-    if type(num_bins) is int:
-        #bin_set = [ min(num_bins, len(array))]
-        # @todo Check unique value in each feature.
-        bin_set = [ num_bins for i in range(X.shape[1]) ]
-    else:
-        bin_set = num_bins
-
-    est = KBinsDiscretizer(n_bins=bin_set, encode='ordinal', strategy='uniform').fit(X)
-
-    return est.transform(X)
-
-
-def test_mifs():
-    # variables for dataset
-    s = 200  # Num rows
-    f = 100 # Num cols
-    i = int(.1 * f)  # Proportion of the relevant features
-    r = int(.05 * f)  # Proportion of the redundant features
-    c = 2
-
-    method = 'MRMR'
-    num_bins = 0
-    scale_data = True
-    k = max(1, int(f * 0.25))
-    verbose = 2
-    early_stop_steps = 10
-
-    print("Parameters.")
-    print(" - method      : {}".format(method))
-    print(" - num_bins    : {}".format(num_bins))
-    print(" - scale_data  : {}".format(scale_data))
-    print(" - k           : {}".format(k))
-    print(" - verbose     : {}".format(verbose))
-
-    # simulate dataset with discrete class labels in y
-    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
-                               n_redundant=r, n_clusters_per_class=c,
-                               random_state=1, shuffle=False)
-    print(X)
-    print(X.shape)
-    print(sum(y) / len(y))
-
-    # perform feature selection
-    mico = MutualInformationForwardSelection(
-        method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data, early_stop_steps=early_stop_steps)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-    #print("Selected features: {}".format(np.where(mico.get_support())[0]))
-    # print(mico.get_support())
-
-    # simulate dataset with continuous y
-    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
-                           random_state=0, shuffle=False)
-    # perform feature selection
-    mico = MutualInformationForwardSelection(
-        method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data, early_stop_steps=early_stop_steps)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-
-
-def test_mibs():
-    # variables for dataset
-    s = 200  # Num rows
-    f = 100 # Num cols
-    i = int(.1 * f)  # Proportion of the relevant features
-    r = int(.05 * f)  # Proportion of the redundant features
-    c = 2
-
-    method = 'JMI'
-    num_bins = 0
-    scale_data = True
-    k = max(1, int(f * 0.25))
-    verbose = 2
-    early_stop_steps = 10
-
-    print("Parameters.")
-    print(" - method      : {}".format(method))
-    print(" - num_bins    : {}".format(num_bins))
-    print(" - scale_data  : {}".format(scale_data))
-    print(" - k           : {}".format(k))
-    print(" - verbose     : {}".format(verbose))
-
-    # simulate dataset with discrete class labels in y
-    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
-                               n_redundant=r, n_clusters_per_class=c,
-                               random_state=1, shuffle=False)
-    print(X)
-    print(X.shape)
-    print(sum(y) / len(y))
-
-    # perform feature selection
-    mico = MutualInformationBackwardSelection(
-        method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data, early_stop_steps=early_stop_steps)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-    #print("Selected features: {}".format(np.where(mico.get_support())[0]))
-
-    # simulate dataset with continuous y
-    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
-                           random_state=0, shuffle=False)
-    # perform feature selection
-    mico = MutualInformationBackwardSelection(
-        method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data, early_stop_steps=early_stop_steps)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-
-
-def test_mico():
-    # variables for dataset
-    s = 500  # Num rows
-    f = 100 # Num cols
-    i = int(.1 * f)  # Proportion of the relevant features
-    r = int(.05 * f)  # Proportion of the redundant features
-    c = 2
-
-    method = 'JMI'
-    num_bins = 5
-    scale_data = True
-    k = max(1, int(f * 0.25))
-    verbose = 2
-
-    print("Parameters.")
-    print(" - method      : {}".format(method))
-    print(" - num_bins    : {}".format(num_bins))
-    print(" - scale_data  : {}".format(scale_data))
-    print(" - k           : {}".format(k))
-    print(" - verbose     : {}".format(verbose))
-
-    # simulate dataset with discrete class labels in y
-    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
-                               n_redundant=r, n_clusters_per_class=c,
-                               random_state=1, shuffle=False)
-    print(X)
-    print(X.shape)
-    print(sum(y) / len(y))
-
-    # perform feature selection
-    mico = MutualInformationConicOptimization(method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-    #print("Selected features: {}".format(np.where(mico.get_support())[0]))
-
-    # simulate dataset with continuous y
-    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
-                           random_state=0, shuffle=False)
-    # perform feature selection
-    mico = MutualInformationConicOptimization(method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data)
-    mico.fit(X, y)
-    # calculate precision and sensitivity
-    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
-    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
-
-
-if __name__ == '__main__':
-
-    # Register arguments.
-    parser = argparse.ArgumentParser(description='Process MICO.')
-    parser.add_argument('job', type=str)
-    args = parser.parse_args()
-
-    # Greeting.
-    setup_logging('INFO')
-    logging.info("Started MICO.")
-    logging.info(" - Job            : {0}".format(args.job))
-
-    try:
-        if args.job == "mifs":
-            test_mifs()
-        elif args.job == "mibs":
-            test_mibs()
-        elif args.job == "mico":
-            test_mico()
-        else:
-            logging.info("<ERR>: Unknown command [{0}].".format(args.job))
-            logging.info("     : Available options are:")
-            logging.info("     :  - [mifs] MI based forward selection")
-            logging.info("     :  - [mibs] MI based backward selection")
-            logging.info("     :  - [mico] MI based features selection using coinc optimization")
-
-    except Exception as e:
-        # Handle general exception.
-        logging.info("<ERR>: Captured unknown error exception.")
-        logging.info("<ERR>:  - Type    : {0}".format(type(e)))
-        logging.info("<ERR>:  - Message : {0}".format(e))
-    finally:
-        # Done.
-        logging.info("Terminated MICO.")
diff --git a/examples/examples_mibe.py b/examples/examples_mibe.py
new file mode 100755
index 0000000..73f5f9c
--- /dev/null
+++ b/examples/examples_mibe.py
@@ -0,0 +1,68 @@
+"""
+Backward elimination approach for feature selection
+===================================================
+
+An introductory example that demonstrates how to perform feature selection using :class:`mico.MutualInformationBackwardElimination`.
+"""
+from mico import MutualInformationBackwardElimination
+import pandas as pd
+from sklearn.datasets import load_breast_cancer, load_diabetes
+
+
+def test_mibe_classification():
+
+    print("=" * 80)
+    print("Start classification example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+
+    # Perform feature selection.
+    mibe = MutualInformationBackwardElimination(verbose=2, categorical=True, n_features=7)
+    mibe.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mibe.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mibe.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mibe.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+def test_mibe_regression():
+
+    print("=" * 80)
+    print("Start regression example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_diabetes()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+    print(X)
+    print(y)
+
+    # Perform feature selection.
+    mibe = MutualInformationBackwardElimination(verbose=2, num_bins=10, categorical=False, n_features=5)
+    mibe.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mibe.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mibe.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mibe.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+if __name__ == '__main__':
+    test_mibe_classification()
+    test_mibe_regression()
diff --git a/examples/examples_mico.py b/examples/examples_mico.py
new file mode 100755
index 0000000..fe0962b
--- /dev/null
+++ b/examples/examples_mico.py
@@ -0,0 +1,68 @@
+"""
+Conic optimization approach for feature selection
+=================================================
+
+An introductory example that demonstrates how to perform feature selection using :class:`mico.MutualInformationConicOptimization`.
+"""
+from mico import MutualInformationConicOptimization
+import pandas as pd
+from sklearn.datasets import load_breast_cancer, load_diabetes
+
+
+def test_mico_classification():
+
+    print("=" * 80)
+    print("Start classification example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+
+    # Perform feature selection.
+    mico = MutualInformationConicOptimization(verbose=2, categorical=True, n_features=7)
+    mico.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mico.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mico.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mico.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+def test_mico_regression():
+
+    print("=" * 80)
+    print("Start regression example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_diabetes()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+    print(X)
+    print(y)
+
+    # Perform feature selection.
+    mico = MutualInformationConicOptimization(verbose=2, num_bins=0, categorical=False, n_features=5)
+    mico.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mico.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mico.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mico.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+if __name__ == '__main__':
+    test_mico_classification()
+    test_mico_regression()
diff --git a/examples/examples_mifs.py b/examples/examples_mifs.py
new file mode 100755
index 0000000..71e1fb3
--- /dev/null
+++ b/examples/examples_mifs.py
@@ -0,0 +1,68 @@
+"""
+Forward selection approach for feature selection
+================================================
+
+An introductory example that demonstrates how to perform feature selection using :class:`mico.MutualInformationForwardSelection`.
+"""
+from mico import MutualInformationForwardSelection
+import pandas as pd
+from sklearn.datasets import load_breast_cancer, load_diabetes
+
+
+def test_mifs_classification():
+
+    print("=" * 80)
+    print("Start classification example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+
+    # Perform feature selection.
+    mifs = MutualInformationForwardSelection(verbose=2, categorical=True, n_features=7)
+    mifs.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mifs.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mifs.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mifs.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+def test_mifs_regression():
+
+    print("=" * 80)
+    print("Start regression example.")
+    print("=" * 80)
+
+    # Prepare data.
+    data = load_diabetes()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+    print(X)
+    print(y)
+
+    # Perform feature selection.
+    mifs = MutualInformationForwardSelection(verbose=2, num_bins=10, categorical=False, n_features=5)
+    mifs.fit(X, y)
+
+    print("-" * 80)
+    print("Populate results.")
+    # Populate selected features.
+    print(" - Selected features: \n{}".format(mifs.get_support()))
+    # Populate feature importance scores.
+    print(" - Feature importance scores: \n{}".format(mifs.feature_importances_))
+    # Call transform() on X.
+    X_transformed = mifs.transform(X)
+    print(" - X_transformed: \n{}".format(X_transformed))
+
+
+if __name__ == '__main__':
+    test_mifs_classification()
+    test_mifs_regression()
\ No newline at end of file
diff --git a/mico/__init__.py b/mico/__init__.py
index ae9ddbb..ff96ea4 100755
--- a/mico/__init__.py
+++ b/mico/__init__.py
@@ -1,12 +1,20 @@
-"""Parallelized Mutual Information based Feature Selection module.
+"""
+MICO: Mutual Information and Conic Optimization for feature selection.
 
-``mifs`` is a Parallelized Mutual Information based Feature
-Selection module.
+Github repo : https://github.com/jupiters1117/mico
+Author      : KuoLing Huang <jupiters1117@gmail.com>
+License     : BSD 3 clause
 
-"""
 
-from .mico import MutualInformationForwardSelection, MutualInformationBackwardSelection, MutualInformationConicOptimization
-#from .mico_utils import get_entropy
+Note
+----
+MICO is heavily inspired from MIFS by Daniel Homola:
+
+Github repo : https://github.com/danielhomola/mifs
+Author      : Daniel Homola <dani.homola@gmail.com>
+License     : BSD 3 clause
+"""
+from .mico import MutualInformationForwardSelection,  MutualInformationBackwardElimination, MutualInformationConicOptimization
 from .version import _check_module_dependencies, __version__
 
 _check_module_dependencies()
@@ -15,14 +23,13 @@
 # flushed if the version of certain modules changes (eg nibabel, as it
 # does not respect the backward compatibility in some of its internal
 # structures
-# This  is used in nilearn._utils.cache_mixin
 CHECK_CACHE_VERSION = True
 
 # list all available submodules and version
 __all__ = \
 [
     'MutualInformationForwardSelection',
-    'MutualInformationConicOptimization',
+    ' MutualInformationBackwardElimination',
     'MutualInformationConicOptimization',
     '__version__'
 ]
diff --git a/mico/mico.py b/mico/mico.py
index 7e5c1d5..e022fef 100755
--- a/mico/mico.py
+++ b/mico/mico.py
@@ -1,11 +1,7 @@
 """
-Parallelized Mutual Information based Feature Selection module.
-
-Author: Daniel Homola <dani.homola@gmail.com>
-License: BSD 3 clause
+This module contains a scikit-learn compatible implementation of mutual information based feature selection methods.
 """
 
-import numpy as np
 from scipy import signal
 from sklearn.utils import check_X_y
 from sklearn.preprocessing import StandardScaler
@@ -15,21 +11,62 @@
 import bottleneck as bn
 from . import mico_utils
 #from sklearn.feature_selection import VarianceThreshold
-from scipy import sparse
+from scipy import sparse, stats
+from scipy.stats import rankdata
 import copy
 from abc import ABCMeta, abstractmethod
+import math
 
+DEBUG = False
 
-"""
-Methods for calculating Mutual Information in an embarrassingly parallel way.
 
-Author: Daniel Homola <dani.homola@gmail.com>
-License: BSD 3 clause
-"""
+###############################################################################
+# IO                                                                          #
+###############################################################################
+import errno, os, logging
+
+
+def setup_logging(level, filename=None):
+    logging.basicConfig(filename=filename, format='%(message)s', level=level)
+    # logging.basicConfig(filename=filename, format='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p', level=level)
+
+
+def append_message(full_msg, curr_msg, check=True, new_line=True):
+    if check:
+        return (full_msg + (curr_msg + "\n" if new_line else curr_msg)) if curr_msg not in full_msg else full_msg
+    else:
+        return full_msg + (curr_msg + "\n" if new_line else curr_msg)
 
+
+def make_dir(path):
+    """Private function for creating the output directory.
+    @ref https://stackoverflow.com/questions/20666764/python-logging-how-to-ensure-logfile-directory-is-created
+    """
+    try:
+        os.makedirs(path, exist_ok=True)  # Python>3.2
+    except TypeError:
+        try:
+            os.makedirs(path)
+        except OSError as exc:  # Python >2.5
+            if exc.errno == errno.EEXIST and os.path.isdir(path):
+                pass
+            else:
+                raise FileNotFoundError("Failed to make directory")
+
+
+###############################################################################
+# Time stamp                                                                  #
+###############################################################################
+from time import gmtime, strftime
+
+def get_time_stamp():
+    return strftime("%Y-%m-%d-%H-%M-%S", gmtime())
+
+
+###############################################################################
+# MI.                                                                         #
+###############################################################################
 import numpy as np
-from scipy.special import gamma, psi
-from sklearn.neighbors import NearestNeighbors
 #from sklearn.externals.joblib import Parallel, delayed
 from joblib import Parallel, delayed
 
@@ -59,14 +96,17 @@ def _clean_up_MI(MI, use_nan_for_invalid_mi):
         return np.nan if use_nan_for_invalid_mi else 0.0
 
 
-def get_first_mi_vector(MI_FS, k, are_data_binned):
+def get_first_mi_vector(MI_FS, k, are_data_binned, n_jobs=1):
     """
     Calculates the Mututal Information between each feature in X and y.
 
     This function is for when |S| = 0. We select the first feature in S.
     """
     n, p = MI_FS.X.shape
-    MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_first_mi)(i, k, MI_FS, are_data_binned) for i in range(p))
+    if n_jobs <= 1:
+        MIs = [_get_first_mi(i, k, MI_FS, are_data_binned) for i in range(p)]
+    else:
+        MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_first_mi)(i, k, MI_FS, are_data_binned) for i in range(p))
     return MIs
 
 
@@ -74,6 +114,7 @@ def _get_first_mi(i, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
     n, p = MI_FS.X.shape
     x = MI_FS.X[:, i].reshape(n, 1)
     y = MI_FS.y.reshape(n, 1)
+    #y = MI_FS.y.flatten().reshape(n, 1)
 
     if are_data_binned:
         if MI_FS.categorical:
@@ -90,14 +131,17 @@ def _get_first_mi(i, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
     return _clean_up_MI(MI, use_nan_for_invalid_mi)
 
 
-def get_mi_vector(MI_FS, k, F, s, are_data_binned):
+def get_mi_vector(MI_FS, k, F, s, are_data_binned, n_jobs=1):
     """
     Calculates the Mututal Information between each feature in F and s.
 
     This function is for when |S| > 1. s is the previously selected feature.
-    We exploite the fact that this step is embarrassingly parallel.
+    We exploit the fact that this step is embarrassingly parallel.
     """
-    MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_mi)(f, s, k, MI_FS, are_data_binned) for f in F)
+    if n_jobs <= 1:
+        MIs = [_get_mi(f, s, k, MI_FS, are_data_binned) for f in F]
+    else:
+        MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_mi)(f, s, k, MI_FS, are_data_binned) for f in F)
     return MIs
 
 
@@ -118,12 +162,9 @@ def _get_mi(f, s, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
         # JMI & JMIM
         if s != f:
             # Off-diagonal.
-            y = MI_FS.y.flatten().reshape(n, 1)
+            y = MI_FS.y.reshape(n, 1)
+            #y = MI_FS.y.flatten().reshape(n, 1)
             joint = MI_FS.X[:, (s, f)]
-            #print(joint)
-            #print("joint.shape", joint.shape)
-            #print("y.shape", y.shape)
-
             if are_data_binned:
                 # Encoding.
                 joint = mico_utils.encode_discrete_x(joint).reshape(n, 1)
@@ -133,18 +174,14 @@ def _get_mi(f, s, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
                     MI = mico_utils.get_mutual_information_cd(y, joint, k)
             else:
                 if MI_FS.categorical:
-                    #print("HERE")
-                    #print("joint.shape", joint.shape)
-                    #print("y", y)
                     MI = mico_utils.get_mutual_information_cd(joint, y, k)
                 else:
-                    #print("HERE 2")
                     MI = mico_utils.get_mutual_information_cc(joint, y, k)
         else:
             # Diagonal.
-            x = MI_FS.y.flatten().reshape(n, 1)
+            x = MI_FS.X[:, f].reshape(n, 1)
             y = MI_FS.y.reshape(n, 1)
-
+            #y = MI_FS.y.flatten().reshape(n)
             if are_data_binned:
                 if MI_FS.categorical:
                     MI = mico_utils.get_mutual_information_dd(x, y)
@@ -168,7 +205,8 @@ def _get_mi(f, s, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
         else:
             # Diagonal-- Did use information from y here.
             x = MI_FS.X[:, f].reshape(n, 1)
-            y = MI_FS.y.flatten().reshape(n)
+            y = MI_FS.y.reshape(n, 1)
+            #y = MI_FS.y.flatten().reshape(n)
             if are_data_binned:
                 if MI_FS.categorical:
                     MI = mico_utils.get_mutual_information_dd(x, y)
@@ -184,18 +222,23 @@ def _get_mi(f, s, k, MI_FS, are_data_binned, use_nan_for_invalid_mi=True):
     return _clean_up_MI(MI, use_nan_for_invalid_mi)
 
 
-def get_mico_vector(MI_FS, k, F, s, offdiagonal_param, are_data_binned):
+def get_mico_vector(MI_FS, k, F, s, offdiagonal_param, are_data_binned, Nx=None, n_jobs=1):
     """
     Calculates the Mututal Information between each feature in F and s.
 
     This function is for when |S| > 1. s is the previously selected feature.
-    We exploite the fact that this step is embarrassingly parallel.
+    We exploit the fact that this step is embarrassingly parallel.
     """
-    MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_mico)(f, s, k, MI_FS, offdiagonal_param, are_data_binned) for f in F)
+    #print("Start {}".format(s))
+    if n_jobs <= 1:
+        MIs = [_get_mico(f, s, k, MI_FS, offdiagonal_param, are_data_binned, Nx=Nx) for f in F]
+    else:
+        MIs = Parallel(n_jobs=MI_FS.n_jobs)(delayed(_get_mico)(f, s, k, MI_FS, offdiagonal_param, are_data_binned, Nx=Nx) for f in F)
+    #print("Done {}".format(s))
     return MIs
 
 
-def _get_mico(f, s, k, MI_FS, offdiagonal_param, are_data_binned, use_nan_for_invalid_mi=False):
+def _get_mico(f, s, k, MI_FS, offdiagonal_param, are_data_binned, use_nan_for_invalid_mi=False, Nx=None):
     """
     A Semidefinite Programming Based Search Strategy for Feature Selection with Mutual Information Measure
     Tofigh Naghibi , Sarah Hoffmann and Beat Pfister
@@ -208,64 +251,100 @@ def _get_mico(f, s, k, MI_FS, offdiagonal_param, are_data_binned, use_nan_for_in
     :return:
     """
     n, p = MI_FS.X.shape
+    clean_up = True
 
     if MI_FS.method in ['JMI', 'JMIM']:
-        # JMI & JMIM
-        if s != f:
-            # Off-diagonal -- -1/2(P-1) I(X_{.s}; X_{.k}; y).
-            x1 = MI_FS.y.flatten().reshape(n, 1)
-            x2 = MI_FS.X[:, f].reshape(n, 1)
-            y = MI_FS.y.reshape(n)
-            print("x1.shape", x1.shape)
-            print("x2.shape", x2.shape)
-            print("y.shape", y.shape)
-            if are_data_binned:
-                if MI_FS.categorical:
-                    print("1")
-                    MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ddd", k) * offdiagonal_param
+        if False:
+            # Three-way interaction info.
+            # JMI & JMIM
+            if s != f:
+                # Off-diagonal -- -1/2(P-1) I(X_{.s}; X_{.k}; y).
+                x1 = MI_FS.X[:, f].reshape(n, 1)
+                x2 = MI_FS.X[:, s].reshape(n, 1)
+                y = MI_FS.y.reshape(n, 1)
+                clean_up = False # No need to clean-up as the 3-way interaction info can be negative.
+                if are_data_binned:
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ddd", k) * offdiagonal_param
+                    else:
+                        MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ddc", k) * offdiagonal_param
                 else:
-                    print("2")
-                    MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ccd", k) * offdiagonal_param
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ccd", k) * offdiagonal_param
+                    else:
+                        MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ccc", k) * offdiagonal_param
             else:
-                if MI_FS.categorical:
-                    print("3")
-                    MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ddd", k) * offdiagonal_param
+                # Diagonal -- I(X_{.s}; y).
+                x = MI_FS.X[:, f].reshape(n, 1)
+                y = MI_FS.y.reshape(n, 1)
+                #y = MI_FS.y.flatten().reshape(n)
+                if are_data_binned:
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_dd(x, y)
+                    else:
+                        MI = mico_utils.get_mutual_information_cd(y, x, k)
+                        #MI = mico_utils.get_mutual_information_cd(y.reshape(n, 1), x.reshape(n), k)
                 else:
-                    print("4")
-                    MI = mico_utils.get_interaction_information_3way(x1, x2, y, "ddc", k) * offdiagonal_param
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_cd(x, y, k, Nx)
+                    else:
+                        MI = mico_utils.get_mutual_information_cc(x, y, k)
         else:
-            # Diagonal -- I(X_{.s}; y).
-            x = MI_FS.y.flatten().reshape(n, 1)
-            y = MI_FS.y.reshape(n)
-            if are_data_binned:
-                if MI_FS.categorical:
-                    MI = mico_utils.get_mutual_information_dd(x, y)
+            # MI
+            # JMI & JMIM
+            if s != f:
+                # Off-diagonal.
+                y = MI_FS.y.reshape(n, 1)
+                #y = MI_FS.y.flatten().reshape(n, 1)
+                joint = MI_FS.X[:, (s, f)]
+                if are_data_binned:
+                    # Encoding.
+                    joint = mico_utils.encode_discrete_x(joint).reshape(n, 1)
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_dd(joint, y)
+                    else:
+                        MI = mico_utils.get_mutual_information_cd(y, joint, k)
                 else:
-                    MI = mico_utils.get_mutual_information_cd(y.reshape(n, 1), x.reshape(n), k)
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_cd(joint, y, k, Nx)
+                    else:
+                        MI = mico_utils.get_mutual_information_cc(joint, y, k)
             else:
-                if MI_FS.categorical:
-                    MI = mico_utils.get_mutual_information_cd(x, y, k)
+                # Diagonal.
+                x = MI_FS.X[:, f].reshape(n, 1)
+                y = MI_FS.y.reshape(n, 1)
+                #y = MI_FS.y.flatten().reshape(n)
+                if are_data_binned:
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_dd(x, y)
+                    else:
+                        MI = mico_utils.get_mutual_information_cd(y, x, k)
                 else:
-                    MI = mico_utils.get_mutual_information_cc(x, y, k)
+                    if MI_FS.categorical:
+                        MI = mico_utils.get_mutual_information_cd(x, y, k, Nx)
+                    else:
+                        MI = mico_utils.get_mutual_information_cc(x, y, k)
     else:
         # MRMR
         if s != f:
             # Off-diagonal -- Did not use any information  from y here.
-            x1 = MI_FS.y.flatten().reshape(n, 1)
-            x2 = MI_FS.X[:, f].reshape(n, 1)
+            x1 = MI_FS.X[:, f].reshape(n, 1)
+            x2 = MI_FS.X[:, s].reshape(n, 1)
             if are_data_binned:
                 MI = mico_utils.get_mutual_information_dd(x1, x2) * offdiagonal_param
             else:
                 MI = mico_utils.get_mutual_information_cc(x1, x2, k) * offdiagonal_param
         else:
             # Diagonal -- Did use information from y here.
-            x = MI_FS.y.flatten().reshape(n, 1)
-            y = MI_FS.y.reshape(n)
+            x = MI_FS.X[:, f].reshape(n, 1)
+            y = MI_FS.y.reshape(n, 1)
+            #y = MI_FS.y.flatten().reshape(n)
             if are_data_binned:
                 if MI_FS.categorical:
                     MI = mico_utils.get_mutual_information_dd(x, y)
                 else:
-                    MI = mico_utils.get_mutual_information_cd(y.reshape(n, 1), x.reshape(n), k)
+                    MI = mico_utils.get_mutual_information_cd(y, x, k, Nx)
+                    #MI = mico_utils.get_mutual_information_cd(y.reshape(n, 1), x.reshape(n), k)
             else:
                 if MI_FS.categorical:
                     MI = mico_utils.get_mutual_information_cd(x, y, k)
@@ -273,132 +352,82 @@ def _get_mico(f, s, k, MI_FS, offdiagonal_param, are_data_binned, use_nan_for_in
                     MI = mico_utils.get_mutual_information_cc(x, y, k)
 
     # MI must be non-negative
-    MI = _clean_up_MI(MI, use_nan_for_invalid_mi)
-    if s == f:
-        MI = max(1.E-4, MI)
+    if clean_up:
+        MI = _clean_up_MI(MI, use_nan_for_invalid_mi)
+        if s == f:
+            MI = max(1.E-4, MI)
+
     return MI
 
 
 class MutualInformationBase(BaseEstimator, SelectorMixin, metaclass=ABCMeta):
     """
-    MI_FS stands for Mutual Information based Feature Selection.
-    This class contains routines for selecting features using both
-    continuous and discrete y variables. Three selection algorithms are
-    implemented: JMI, JMIM and MRMR.
+    Base class for feature selection implementation.
+
+    This base class contains routines for selecting features implementation.
 
-    This implementation tries to mimic the scikit-learn interface, so use fit,
-    transform or fit_transform, to run the feature selection.
 
     Parameters
     ----------
 
-    method : string, default = 'JMI'
-        Which mutual information based feature selection method to use:
-        - 'JMI' : Joint Mutual Information [1]
-        - 'JMIM' : Joint Mutual Information Maximisation [2]
-        - 'MRMR' : Max-Relevance Min-Redundancy [3]
+    method : string, optional (default='JMI')
 
-    k : int, default = 5
-        Sets the number of samples to use for the kernel density estimation
-        with the kNN method. Kraskov et al. recommend a small integer between
-        3 and 10.
+        A string that specifies the mutual information measure. Possible options are:
 
-    n_features : int or string, default = 'auto'
-        If int, it sets the number of features that has to be selected from X.
-        If 'auto' this is determined automatically based on the amount of
-        mutual information the previously selected features share with y.
+        - **JMI** : Joint Mutual Information [1]_
+        - **JMIM** : Joint Mutual Information Maximisation [2]_
+        - **MRMR** : Max-Relevance Min-Redundancy [3]_
 
-    categorical : Boolean, default = True
-        If True, y is assumed to be a categorical class label. If False, y is
-        treated as a continuous. Consequently this parameter determines the
-        method of estimation of the MI between the predictors in X and y.
+    k : int, optional (default=5)
 
-    n_jobs : int, optional (default=1)
-        The number of threads to open if possible.
+        An integer to specify number of samples for the kernel density estimation using KNN. A small integer between 3 and 10 is recommended. Note that this parameter is applicable only if `num_bins` is set to 0.
 
-    verbose : int, default=0
-        Controls verbosity of output:
-        - 0: no output
-        - 1: displays selected features
-        - 2: displays selected features and mutual information
+    n_features : int or string, optional (default='auto')
 
-    num_bins : int, default=0
-        Number of bins for binning the data.
+        A positive integer number that specifies the number of features to be selected.
+        If 'auto' is used, then this number will be determined automatically.
 
+    categorical : boolean, optional (default=True)
 
-    early_stop_steps : int, default=10
-        Number of steps for early stopping.
+        If `categorical` is `True`, then the dependent variable is assumed to be a categorical class label. Otherwise, the dependent variable is assumed to be a continuous variable.
 
-    Attributes
-    ----------
+    n_jobs : int, optional (default=1)
 
-    n_features_ : int
-        The number of selected features.
+        The number of threads allowed for the computations. If `n_jobs` :math:`<= 0`, then it will be determined internally.
 
-    support_ : array of length X.shape[1]
-        The mask array of selected features.
+    verbose : int, optional (default=0)
 
-    ranking_ : array of shape n_features
-        The feature ranking of the selected features, with the first being
-        the first feature selected with largest marginal MI with y, followed by
-        the others with decreasing MI.
+        An integer number to specify the verbosity level. Available options are:
 
-    mi_ : array of shape n_features
-        The JMIM of the selected features. Usually this a monotone decreasing
-        array of numbers converging to 0. One can use this to estimate the
-        number of features to select. In fact this is what n_features='auto''
-        tries to do heuristically.
+        - 0: Disable output.
+        - 1: Display summary results.
+        - 2: Displays all results.
 
-    Examples
-    --------
+    scale_data : boolean, optional (default=True)
 
-    import pandas as pd
-    import mifs
-
-    # load X and y
-    X = pd.read_csv('my_X_table.csv', index_col=0).values
-    y = pd.read_csv('my_y_vector.csv', index_col=0).values
-
-    # define MI_FS feature selection method
-    feat_selector = mifs.MutualInformationFeatureSelector()
+        An boolean flag that specifies if the input data input shall be scaled first.
 
-    # find all relevant features
-    feat_selector.fit(X, y)
+    num_bins : int, optional (default=0)
 
-    # check selected features
-    feat_selector.support_
+        An integer number to specify the number of bins allowed for data binning (bucketing). If :math:`0`, then data binning will be disabled, and KNN will be used for kernal density estimation.
 
-    # check ranking of features
-    feat_selector.ranking_
-
-    # call transform() on X to filter it down to selected features
-    X_filtered = feat_selector.transform(X)
 
     References
     ----------
 
-    [1] H. Yang and J. Moody, "Data Visualization and Feature Selection: New
-        Algorithms for Nongaussian Data"
-        NIPS 1999
-    [2] Bennasar M., Hicks Y., Setchi R., "Feature selection using Joint Mutual
-        Information Maximisation"
-        Expert Systems with Applications, Vol. 42, Issue 22, Dec 2015
-    [3] H. Peng, Fulmi Long, C. Ding, "Feature selection based on mutual
-        information criteria of max-dependency, max-relevance,
-        and min-redundancy"
-        Pattern Analysis & Machine Intelligence 2005
+    .. [1] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+    .. [2] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+    .. [3] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
     """
-
     def __init__(self,
                  method='JMI',
                  k=5,
                  n_features='auto',
                  categorical=True,
-                 n_jobs=1,
+                 n_jobs=0,
                  verbose=0,
                  scale_data=True,
-                 num_bins=0,
-                 early_stop_steps=10):
+                 num_bins=0):
         self.method = method
         self.k = k
         self.n_features = n_features
@@ -408,15 +437,23 @@ def __init__(self,
         self.scale_data = scale_data
         self.num_bins = num_bins
         self._support_mask = None
-        self.early_stop_steps = early_stop_steps
         # Check if n_jobs is negative
-        if self.n_jobs < 0:
+        if self.n_jobs <= 0:
             self.n_jobs = cpu_count()
 
-        # Attributes.
-        self.n_features_ = 0
-        self.ranking_ = []
-        self.mi_ = []
+        # Logger.
+        #make_dir("./log/")
+        #instance = get_time_stamp()
+        #filename = "./log/{}.log".format(instance)
+        filename = None
+
+        if self.verbose == 0:
+            lv = logging.CRITICAL
+        elif self.verbose == 1:
+            lv = logging.INFO
+        else:
+            lv = logging.DEBUG
+        setup_logging(level=lv, filename=filename)
 
     def _get_support_mask(self):
         if self._support_mask is None:
@@ -480,13 +517,13 @@ def _init_data(self, X, y):
 
         # Bin the data if needed.
         if self.num_bins > 0:
-            print("Binning data.")
+            logging.info("Started binning data.")
             X = self._bin_data(X, self.num_bins)
         else:
             self.num_bins = 0
         # Scale the data if needed.
         if self.scale_data:
-            print("Scaling data.")
+            logging.info("Started scaling data.")
             X = self._scale_data(X)
 
         # sanity checks
@@ -505,12 +542,12 @@ def _init_data(self, X, y):
         if not isinstance(self.categorical, bool):
             raise ValueError('Categorical must be Boolean.')
         if self.categorical and np.unique(y).shape[0] > 5:
-            print ('Are you sure y is categorical? It has more than 5 levels.')
+            logging.warning('Are you sure y is categorical? It has more than 5 levels.')
         if not self.categorical and self._is_integer(y):
-            print ('Are you sure y is continuous? It seems to be discrete.')
+            logging.warning('Are you sure y is continuous? It seems to be discrete.')
         if self._is_integer(X):
-            print ('The values of X seem to be discrete. MI_FS will treat them'
-                   'as continuous.')
+            logging.warning('The values of X seem to be discrete. MI_FS will treat them as continuous.')
+
         return X, y
 
     def _add_remove(self, S, F, i):
@@ -530,131 +567,160 @@ def _remove(self, S, i):
         S.remove(i)
         return S
 
-    def _print_results(self, S, MIs):
-        out = ''
-        if self.n_features == 'auto':
-            out += 'Auto selected feature #' + str(len(S)) + ' : ' + str(S[-1])
-        else:
-            out += ('Selected feature #' + str(len(S)) + ' / ' +
-                    str(self.n_features) + ' : ' + str(S[-1]))
+    def _calc_xQx(self, Q, curr_soln):
+        score = 0
+        n = Q.shape[0]
+        for i in range(n):
+            if curr_soln[i] > 0:
+                for j in range(n):
+                    if curr_soln[j] > 0 and not np.isnan(Q[i, j]):
+                        score += Q[i, j]
+
+        return score
+
+    def _convert_idx_array_to_mask_array(self, idx_array, n):
+        mask_array = np.zeros(n, dtype=int)
+        mask_array[idx_array] = 1
+        return mask_array
+
+    def _convert_mask_array_to_idx_array(self, mask_array):
+        return np.where(mask_array)
+
+    def _generate_feature_importances(self, best_soln, Q, num_features):
+        feature_importances = []
+        require_clean_run = False
+        best_soln_binary = self._convert_idx_array_to_mask_array(best_soln, num_features)
+        best_score = self._calc_xQx(Q, best_soln_binary)
+
+        for i in range(len(best_soln_binary)):
+            if best_soln_binary[i] == 1:
+                # Remove the feature and recalculate the score.
+                best_soln_binary[i] = 0
+                curr_score = self._calc_xQx(Q, best_soln_binary)
+                diff_score = best_score - curr_score
+                best_soln_binary[i] = 1
+                if diff_score < 0:
+                    logging.info("Warning: Feature {} has negative score {1}".format(i, diff_score))
+                    best_soln_binary[i] = 0
+                    require_clean_run = True
+                feature_importances.append(diff_score)
+            else:
+                feature_importances.append(0.0)
+
+        if require_clean_run:
+            best_soln = self._convert_mask_array_to_idx_array(best_soln_binary)
+            best_score = self._calc_xQx(Q, best_soln_binary)
 
-        if self.verbose > 1:
-            out += ', ' + self.method + ' : ' + str(MIs[-1])
-        print (out)
+        # Normalize and rank.
+        if sum(feature_importances) <= 0:
+            raise ValueError("Failed: all features have zero importance score. Try using a smaller value for parameter k.")
+        feature_importances = feature_importances / sum(feature_importances)
+        feature_raking = list(map(int, rankdata(-feature_importances)))
+
+        return best_soln, best_score, feature_importances, feature_raking
 
 
 class MutualInformationForwardSelection(MutualInformationBase):
     """
-    MI_FS stands for Mutual Information based Feature Selection.
-    This class contains routines for selecting features using both
-    continuous and discrete y variables. Three selection algorithms are
-    implemented: JMI, JMIM and MRMR.
+    Forward selection approach for feature selection.
+
+    This class implements a forward selection approach for feature selection with mutual information (MI) measure.
 
-    This implementation tries to mimic the scikit-learn interface, so use fit,
-    transform or fit_transform, to run the feature selection.
 
     Parameters
     ----------
 
-    method : string, default = 'JMI'
-        Which mutual information based feature selection method to use:
-        - 'JMI' : Joint Mutual Information [1]
-        - 'JMIM' : Joint Mutual Information Maximisation [2]
-        - 'MRMR' : Max-Relevance Min-Redundancy [3]
+    method : string, optional (default='JMI')
+
+        A string that specifies the mutual information measure. Possible options are:
+
+        - **JMI** : Joint Mutual Information [1]_
+        - **JMIM** : Joint Mutual Information Maximisation [2]_
+        - **MRMR** : Max-Relevance Min-Redundancy [3]_
+
+    k : int, optional (default=5)
+
+        An integer to specify number of samples for the kernel density estimation using KNN. A small integer between 3 and 10 is recommended. Note that this parameter is applicable only if `num_bins` is set to 0.
 
-    k : int, default = 5
-        Sets the number of samples to use for the kernel density estimation
-        with the kNN method. Kraskov et al. recommend a small integer between
-        3 and 10.
+    n_features : int or string, optional (default='auto')
 
-    n_features : int or string, default = 'auto'
-        If int, it sets the number of features that has to be selected from X.
-        If 'auto' this is determined automatically based on the amount of
-        mutual information the previously selected features share with y.
+        A positive integer number that specifies the number of features to be selected.
+        If 'auto' is used, then this number will be determined automatically.
 
-    categorical : Boolean, default = True
-        If True, y is assumed to be a categorical class label. If False, y is
-        treated as a continuous. Consequently this parameter determines the
-        method of estimation of the MI between the predictors in X and y.
+    categorical : boolean, optional (default=True)
+
+        If `categorical` is `True`, then the dependent variable is assumed to be a categorical class label. Otherwise, the dependent variable is assumed to be a continuous variable.
 
     n_jobs : int, optional (default=1)
-        The number of threads to open if possible.
 
-    verbose : int, default=0
-        Controls verbosity of output:
-        - 0: no output
-        - 1: displays selected features
-        - 2: displays selected features and mutual information
+        The number of threads allowed for the computations. If `n_jobs` :math:`<= 0`, then it will be determined internally.
+
+    verbose : int, optional (default=0)
+
+        An integer number to specify the verbosity level. Available options are:
+
+        - 0: Disable output.
+        - 1: Display summary results.
+        - 2: Displays all results.
+
+    scale_data : boolean, optional (default=True)
+
+        An boolean flag that specifies if the input data input shall be scaled first.
+
+    num_bins : int, optional (default=0)
+
+        An integer number to specify the number of bins allowed for data binning (bucketing). If :math:`0`, then data binning will be disabled, and KNN will be used for kernal density estimation.
+
+    early_stop_steps : int, optional (default=10)
+
+         An positive integer to specify the iteration limit allowed without improvement on the MI measure.
 
-    num_bins : int, default=0
-        Number of bins for binning the data.
 
     Attributes
     ----------
 
     n_features_ : int
+
         The number of selected features.
 
-    support_ : array of length X.shape[1]
-        The mask array of selected features.
+    feature_importances_ : array of shape n_features
+
+         The feature importance scores of the selected features.
 
     ranking_ : array of shape n_features
-        The feature ranking of the selected features, with the first being
-        the first feature selected with largest marginal MI with y, followed by
+
+        The feature ranking of the selected features, with the first being the first feature selected with largest marginal MI with y, followed by
         the others with decreasing MI.
 
     mi_ : array of shape n_features
-        The JMIM of the selected features. Usually this a monotone decreasing
-        array of numbers converging to 0. One can use this to estimate the
-        number of features to select. In fact this is what n_features='auto''
-        tries to do heuristically.
-
-    Examples
-    --------
-
-    import pandas as pd
-    import mifs
 
-    # load X and y
-    X = pd.read_csv('my_X_table.csv', index_col=0).values
-    y = pd.read_csv('my_y_vector.csv', index_col=0).values
+        The MI measure of the selected features. Usually this a monotone decreasing array of numbers converging to 0. One can use this to estimate the
+        number of features to select. In fact this is what n_features='auto' tries to do heuristically.
 
-    # define MI_FS feature selection method
-    feat_selector = mifs.MutualInformationFeatureSelector()
 
-    # find all relevant features
-    feat_selector.fit(X, y)
+    Examples
+    --------
 
-    # check selected features
-    feat_selector.support_
+    This implementation follows the scikit-learn API convention:
 
-    # check ranking of features
-    feat_selector.ranking_
+     - ``fit(X, y)``
+     - ``transform(X)``
+     - ``fit_transform(X, y)``
 
-    # call transform() on X to filter it down to selected features
-    X_filtered = feat_selector.transform(X)
 
     References
     ----------
 
-    [1] H. Yang and J. Moody, "Data Visualization and Feature Selection: New
-        Algorithms for Nongaussian Data"
-        NIPS 1999
-    [2] Bennasar M., Hicks Y., Setchi R., "Feature selection using Joint Mutual
-        Information Maximisation"
-        Expert Systems with Applications, Vol. 42, Issue 22, Dec 2015
-    [3] H. Peng, Fulmi Long, C. Ding, "Feature selection based on mutual
-        information criteria of max-dependency, max-relevance,
-        and min-redundancy"
-        Pattern Analysis & Machine Intelligence 2005
+    .. [1] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+    .. [2] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+    .. [3] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
     """
-
     def __init__(self,
                  method='JMI',
                  k=5,
                  n_features='auto',
                  categorical=True,
-                 n_jobs=1,
+                 n_jobs=0,
                  verbose=0,
                  scale_data=True,
                  num_bins=0,
@@ -662,8 +728,14 @@ def __init__(self,
         # Call base constructor.
         super(MutualInformationForwardSelection, self).__init__(
             method, k, n_features, categorical,
-            n_jobs, verbose, scale_data, num_bins,
-            early_stop_steps)
+            n_jobs, verbose, scale_data, num_bins)
+        self.early_stop_steps = early_stop_steps
+
+        # Attributes.
+        self.n_features_ = 0
+        self.feature_importances_ = []
+        self.ranking_ = []
+        self.mi_ = []
 
     def fit(self, X, y):
         """
@@ -679,6 +751,7 @@ def fit(self, X, y):
         """
         self.X, y = self._init_data(X, y)
         n, p = X.shape
+        num_features = p
         self.y = y.reshape((n, 1))
 
         # list of selected features
@@ -696,28 +769,33 @@ def fit(self, X, y):
         feature_mi_matrix[:] = np.nan
         S_mi = []
 
-        # ---------------------------------------------------------------------
-        # FIND FIRST FEATURE
-        # ---------------------------------------------------------------------
-        xy_MI = np.array(get_first_mi_vector(self, self.k, self._are_data_binned()))
+        # Populate information.
+        self._print_init_result()
+
+        #-------------------------------------------------------------------#
+        # Find first feature                                                #
+        #-------------------------------------------------------------------#
+        xy_MI = np.array(get_first_mi_vector(self, self.k, self._are_data_binned(), n_jobs=1))
         xy_MI = _replace_vec_nan_with_zero(xy_MI)
-        print("xy_MI", xy_MI)
+        if sum(xy_MI) <= 0:
+            logging.warning("Warning: The first MI are all zeros. Try using a different value of k.")
 
         # choose the best, add it to S, remove it from F
-        S, F = self._add_remove(S, F, bn.nanargmax(xy_MI))
-        S_mi.append(bn.nanmax(xy_MI))
+        selected = bn.nanargmax(xy_MI)
+        S, F = self._add_remove(S, F, selected)
+        S_mi.append(selected)
 
-        # notify user
-        if self.verbose > 0:
-            self._print_results(S, S_mi)
+        # Populate information.
+        self._print_curr_result(S, selected, S_mi)
+        self.ranking_.append(selected)
 
-        # ---------------------------------------------------------------------
-        # FIND SUBSEQUENT FEATURES
-        # ---------------------------------------------------------------------
+        #-------------------------------------------------------------------#
+        # Find Subsequent features                                          #
+        #-------------------------------------------------------------------#
         while len(S) < n_features:
             # loop through the remaining unselected features and calculate MI
             s = len(S) - 1
-            feature_mi_matrix[s, F] = get_mi_vector(self, self.k, F, S[-1], self._are_data_binned())
+            feature_mi_matrix[s, F] = get_mi_vector(self, self.k, F, S[-1], self._are_data_binned(), n_jobs=1)
 
             # make decision based on the chosen FS algorithm
             fmm = feature_mi_matrix[:len(S), F]
@@ -738,16 +816,14 @@ def fit(self, X, y):
                 selected = F[bn.nanargmax(MRMR)]
                 S_mi.append(bn.nanmax(MRMR))
 
-            # record the JMIM of the newly selected feature and add it to S
-            #if self.method != 'MRMR':
-            #    S_mi.append(bn.nanmax(bn.nanmin(fmm, axis=0)))
+            # record the newly selected feature and add it to S.
             S, F = self._add_remove(S, F, selected)
+            self.ranking_.append(selected)
 
-            # notify user
-            if self.verbose > 0:
-                self._print_results(S, S_mi)
+            # Populate information.
+            self._print_curr_result(S, selected, S_mi)
 
-            # if n_features == 'auto', let's check the S_mi to stop
+            # Perform early stops.
             if self.n_features == 'auto' and \
                     (self.early_stop_steps > 0 and len(S) > self.early_stop_steps):
                 # smooth the 1st derivative of the MI values of previously sel
@@ -755,140 +831,181 @@ def fit(self, X, y):
                 # does the mean of the last 5 converge to 0?
                 if np.abs(np.mean(MI_dd[-5:])) < 1e-3:
                     break
+        S = sorted(S)
+
+        #-------------------------------------------------------------------#
+        # Calculate final MIs                                               #
+        #-------------------------------------------------------------------#
+        logging.info("Started calculating final MI matrix.")
+        num_features_S = len(S)
+        feature_mi_matrix = np.zeros((p, p))
+        feature_mi_matrix[:] = np.nan
+        if True:
+            # Parallelize the outer loop - faster.
+            feature_mi_matrix_partial_tri = Parallel(n_jobs=self.n_jobs)(delayed(get_mi_vector)(
+                self, self.k, S[i:], s, self._are_data_binned(), n_jobs=1) for i, s in enumerate(S))
+            # Get the MI matrix.
+            for i in range(num_features_S):
+                for j in range(i, num_features_S):
+                    feature_mi_matrix[S[i], S[j]] = feature_mi_matrix_partial_tri[i][j - i]
+                    if i != j:
+                        feature_mi_matrix[S[j], S[i]] = feature_mi_matrix[S[i], S[j]]
+        else:
+            # Parallelize the inner loop - slower.
+            all_S = list(range(p))
+            for s in all_S:
+                feature_mi_matrix[s, all_S] = get_mi_vector(self, self.k, all_S, s, self._are_data_binned(), n_jobs=self.n_jobs)
+            # Ensure the MI matrix is symmetric.
+            feature_mi_matrix = mico_utils.make_mat_sym(feature_mi_matrix)
+
+        #-------------------------------------------------------------------#
+        # Generate feature importance scores.                               #
+        #-------------------------------------------------------------------#
+        S, best_score, feature_importances, feature_raking = \
+            self._generate_feature_importances(best_soln=S, Q=feature_mi_matrix, num_features=num_features)
 
-        # ---------------------------------------------------------------------
-        # SAVE RESULTS
-        # ---------------------------------------------------------------------
+        #-------------------------------------------------------------------#
+        # Save results                                                      #
+        #-------------------------------------------------------------------#
+        num_features_sel = n_features
         self.n_features_ = len(S)
+        self.feature_importances_ = feature_importances
         self._support_mask = np.zeros(p, dtype=np.bool)
         self._support_mask[S] = True
-        self.ranking_ = S
         self.mi_ = S_mi
 
+        self._print_end_result(num_features_sel=num_features_sel, num_features=num_features, best_soln=S)
+
         return self
 
+    def _print_init_result(self):
+        logging.info("Started MIFS.")
+        logging.info(" - Method        : {}".format(self.method))
+        logging.info(" - Num. threads  : {}".format(self.n_jobs))
+        logging.info(" - Num. features : {}".format(self.n_features))
+        logging.info("{0:>5}{1:>5}{2:15}".format("Iter", "Sel", "    Current MI"))
+
+    def _print_curr_result(self, S, selected, MIs):
+        logging.info("{0:>5}{1:>5}{2:+14.6E}".format(len(S), selected, MIs[-1]))
+
+    def _print_end_result(self, num_features_sel, num_features, best_soln):
+        logging.info("Done MIFS.")
+        logging.info(" - Total feat.   : {}".format(num_features))
+        logging.info(" - Target feat.  : {}".format(num_features_sel))
+        logging.info(" - Actual feat.  : {}".format(len(best_soln)))
 
-class MutualInformationBackwardSelection(MutualInformationBase):
+
+class MutualInformationBackwardElimination(MutualInformationBase):
     """
-    MI_FS stands for Mutual Information based Feature Selection.
-    This class contains routines for selecting features using both
-    continuous and discrete y variables. Three selection algorithms are
-    implemented: JMI, JMIM and MRMR.
+    Backward elimination approach for feature selection.
+
+    This class implements a backward selection approach for feature selection with mutual information (MI) measure.
 
-    This implementation tries to mimic the scikit-learn interface, so use fit,
-    transform or fit_transform, to run the feature selection.
 
     Parameters
     ----------
 
-    method : string, default = 'JMI'
-        Which mutual information based feature selection method to use:
-        - 'JMI' : Joint Mutual Information [1]
-        - 'JMIM' : Joint Mutual Information Maximisation [2]
-        - 'MRMR' : Max-Relevance Min-Redundancy [3]
+    method : string, optional (default='JMI')
+
+        A string that specifies the mutual information measure. Possible options are:
+
+        - **JMI** : Joint Mutual Information [1]_
+        - **JMIM** : Joint Mutual Information Maximisation [2]_
+        - **MRMR** : Max-Relevance Min-Redundancy [3]_
 
-    k : int, default = 5
-        Sets the number of samples to use for the kernel density estimation
-        with the kNN method. Kraskov et al. recommend a small integer between
-        3 and 10.
+    k : int, optional (default=5)
 
-    n_features : int or string, default = 'auto'
-        If int, it sets the number of features that has to be selected from X.
-        If 'auto' this is determined automatically based on the amount of
-        mutual information the previously selected features share with y.
+        An integer to specify number of samples for the kernel density estimation using KNN. A small integer between 3 and 10 is recommended. Note that this parameter is applicable only if `num_bins` is set to 0.
 
-    categorical : Boolean, default = True
-        If True, y is assumed to be a categorical class label. If False, y is
-        treated as a continuous. Consequently this parameter determines the
-        method of estimation of the MI between the predictors in X and y.
+    n_features : int or string, optional (default='auto')
+
+        A positive integer number that specifies the number of features to be selected.
+        If 'auto' is used, then this number will be determined automatically.
+
+    categorical : boolean, optional (default=True)
+
+        If `categorical` is `True`, then the dependent variable is assumed to be a categorical class label. Otherwise, the dependent variable is assumed to be a continuous variable.
 
     n_jobs : int, optional (default=1)
-        The number of threads to open if possible.
 
-    verbose : int, default=0
-        Controls verbosity of output:
-        - 0: no output
-        - 1: displays selected features
-        - 2: displays selected features and mutual information
+        The number of threads allowed for the computations. If `n_jobs` :math:`<= 0`, then it will be determined internally.
+
+    verbose : int, optional (default=0)
+
+        An integer number to specify the verbosity level. Available options are:
+
+        - 0: Disable output.
+        - 1: Display summary results.
+        - 2: Displays all results.
+
+    scale_data : boolean, optional (default=True)
+
+        An boolean flag that specifies if the input data input shall be scaled first.
+
+    num_bins : int, optional (default=0)
+
+        An integer number to specify the number of bins allowed for data binning (bucketing). If :math:`0`, then data binning will be disabled, and KNN will be used for kernal density estimation.
 
-    num_bins : int, default=0
-        Number of bins for binning the data.
 
     Attributes
     ----------
 
     n_features_ : int
+
         The number of selected features.
 
-    support_ : array of length X.shape[1]
-        The mask array of selected features.
+    feature_importances_ : array of shape n_features
+
+         The feature importance scores of the selected features.
 
     ranking_ : array of shape n_features
-        The feature ranking of the selected features, with the first being
-        the first feature selected with largest marginal MI with y, followed by
+
+        The feature ranking of the selected features, with the first being the first feature selected with largest marginal MI with y, followed by
         the others with decreasing MI.
 
     mi_ : array of shape n_features
-        The JMIM of the selected features. Usually this a monotone decreasing
-        array of numbers converging to 0. One can use this to estimate the
-        number of features to select. In fact this is what n_features='auto''
-        tries to do heuristically.
-
-    Examples
-    --------
-
-    import pandas as pd
-    import mifs
 
-    # load X and y
-    X = pd.read_csv('my_X_table.csv', index_col=0).values
-    y = pd.read_csv('my_y_vector.csv', index_col=0).values
+        The MI measure of the selected features. Usually this a monotone decreasing array of numbers converging to 0. One can use this to estimate the
+        number of features to select. In fact this is what n_features='auto' tries to do heuristically.
 
-    # define MI_FS feature selection method
-    feat_selector = mifs.MutualInformationFeatureSelector()
 
-    # find all relevant features
-    feat_selector.fit(X, y)
+    Examples
+    --------
 
-    # check selected features
-    feat_selector.support_
+    This implementation follows the scikit-learn API convention:
 
-    # check ranking of features
-    feat_selector.ranking_
+     - ``fit(X, y)``
+     - ``transform(X)``
+     - ``fit_transform(X, y)``
 
-    # call transform() on X to filter it down to selected features
-    X_filtered = feat_selector.transform(X)
 
     References
     ----------
 
-    [1] H. Yang and J. Moody, "Data Visualization and Feature Selection: New
-        Algorithms for Nongaussian Data"
-        NIPS 1999
-    [2] Bennasar M., Hicks Y., Setchi R., "Feature selection using Joint Mutual
-        Information Maximisation"
-        Expert Systems with Applications, Vol. 42, Issue 22, Dec 2015
-    [3] H. Peng, Fulmi Long, C. Ding, "Feature selection based on mutual
-        information criteria of max-dependency, max-relevance,
-        and min-redundancy"
-        Pattern Analysis & Machine Intelligence 2005
+    .. [1] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+    .. [2] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+    .. [3] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
     """
-
     def __init__(self,
                  method='JMI',
                  k=5,
                  n_features='auto',
                  categorical=True,
-                 n_jobs=1,
+                 n_jobs=0,
                  verbose=0,
                  scale_data=True,
-                 num_bins=0,
-                 early_stop_steps=10):
+                 num_bins=0):
         # Call base constructor.
-        super(MutualInformationBackwardSelection, self).__init__(
+        super(MutualInformationBackwardElimination, self).__init__(
             method, k, n_features, categorical,
-            n_jobs, verbose, scale_data, num_bins,
-            early_stop_steps)
+            n_jobs, verbose, scale_data, num_bins)
+
+        # Deprecated.
+        self.early_stop_steps = 0
+
+        # Attributes.
+        self.n_features_ = 0
+        self.feature_importances_ = []
 
     def fit(self, X, y):
         """
@@ -902,10 +1019,10 @@ def fit(self, X, y):
         y : array-like, shape = [n_samples]
             The target values.
         """
-        print("BGN")
         self.X, y = self._init_data(X, y)
         n, p = X.shape
-        self.y = y.reshape((n, 1))
+        num_features = p
+        self.y = y.reshape(n, 1)
 
         # list of selected features
         S = list(range(p))
@@ -915,30 +1032,43 @@ def fit(self, X, y):
             n_features = int(p * 0.2)
         else:
             n_features = min(p, self.n_features)
-        print("n_features = ", n_features)
 
         feature_mi_matrix = np.zeros((p, p))
         feature_mi_matrix[:] = np.nan
         S_mi = []
+        # Populate information.
+        self._print_init_result()
 
-        # ---------------------------------------------------------------------
-        # CALCULATE FULL MIs
-        # ---------------------------------------------------------------------
+        #-------------------------------------------------------------------#
+        # Calculate full MIs                                                #
+        #-------------------------------------------------------------------#
+        logging.info("Started calculating full MI matrix.")
         if self.method == 'MRMR':
-            xy_MI = np.array(get_first_mi_vector(self, self.k, self._are_data_binned()))
+            xy_MI = np.array(get_first_mi_vector(self, self.k, self._are_data_binned(), n_jobs=1))
             xy_MI = _replace_vec_nan_with_zero(xy_MI)
-            print("xy_MI", xy_MI)
 
-        for s in S:
-            feature_mi_matrix[s, S] = get_mi_vector(self, self.k, S, s, self._are_data_binned())
-        # Ensure the MI matrix is symmetric.
-        feature_mi_matrix = mico_utils.make_mat_sym(feature_mi_matrix)
-        print(feature_mi_matrix)
+        if True:
+            # Parallelize the outer loop - faster.
+            feature_mi_matrix_tri = Parallel(n_jobs=self.n_jobs)(delayed(get_mi_vector)(
+                self, self.k, list(range(i, num_features, 1)), s, self._are_data_binned(), n_jobs=1) for i, s in enumerate(S))
+            for i, s in enumerate(S):
+                feature_mi_matrix[s, list(range(i, num_features, 1))] = feature_mi_matrix_tri[s]
+            # Ensure the MI matrix is symmetric.
+            for i in range(num_features):
+                for j in range(i + 1, num_features):
+                    feature_mi_matrix[j, i] = feature_mi_matrix[i, j]
+        else:
+            # Parallelize the inner loop - slower.
+            for s in S:
+                feature_mi_matrix[s, S] = get_mi_vector(self, self.k, S, s, self._are_data_binned(), n_jobs=self.n_jobs)
+            # Ensure the MI matrix is symmetric.
+            feature_mi_matrix = mico_utils.make_mat_sym(feature_mi_matrix)
 
-        # ---------------------------------------------------------------------
-        # FIND SUBSEQUENT FEATURES
-        # ---------------------------------------------------------------------
-        while len(S) >= n_features:
+        #-------------------------------------------------------------------#
+        # Find subsequent features                                          #
+        #-------------------------------------------------------------------#
+        logging.info("Started backward selection.")
+        while len(S) >= n_features + 1:
             fmm = np.zeros((len(S) - 1, len(S)))
 
             # make decision based on the chosen FS algorithm
@@ -948,170 +1078,173 @@ def fit(self, X, y):
                 fmm[:, i] = feature_mi_matrix[S_keep, s_remove]
                 if bn.allnan(fmm[:, i]):
                     fmm[:, i] = [0 for _ in range(fmm.shape[0])]
-                    print("Warning: feature [{0}] has all 0 MI.".format(i))
+                    logging.warning("Feature [{0}] has all 0 MI.".format(i))
 
             if self.method == 'JMI':
                 values = bn.nansum(fmm, axis=0)
-                #removed_feature = S[bn.nanargmax(values)]
                 removed_feature = S[bn.nanargmin(values)]
-                #S_mi.append(bn.nanmax(values))
                 S_mi.append(bn.nanmin(values))
             elif self.method == 'JMIM':
                 values = bn.nanmin(fmm, axis=0)
                 if bn.allnan(values):
                     break
-                #removed_feature = S[bn.nanargmax(values)]
                 removed_feature = S[bn.nanargmin(values)]
-                #S_mi.append(bn.nanmax(values))
                 S_mi.append(bn.nanmin(values))
             elif self.method == 'MRMR':
                 if bn.allnan(bn.nanmean(fmm, axis=0)):
                     break
                 values = xy_MI[S] - bn.nanmean(fmm, axis=0)
-                #removed_feature = S[bn.nanargmax(values)]
-                #S_mi.append(bn.nanmax(values))
                 removed_feature = S[bn.nanargmin(values)]
                 S_mi.append(bn.nanmin(values))
-            print("values=", values)
-            print("removed_feature=", removed_feature)
 
             # Update removed_feature feature list.
             S = self._remove(S, removed_feature)
-            print(S)
 
-            # notify user
-            if self.verbose > 0:
-                self._print_results(S, S_mi)
+            # Populate information.
+            self._print_curr_result(S, removed_feature, S_mi)
 
             # if n_features == 'auto', let's check the S_mi to stop
-            if self.n_features == 'auto' and \
+            # Deprecated.
+            if False and self.n_features == 'auto' and \
                     (self.early_stop_steps > 0 and p - len(S) > self.early_stop_steps):
                 # smooth the 1st derivative of the MI values of previously sel
                 MI_dd = signal.savgol_filter(S_mi[1:], 9, 2, 1)
                 # does the mean of the last 5 converge to 0?
                 if np.abs(np.mean(MI_dd[-5:])) < 1e-3:
-                    print("Early stop.")
+                    logging.info("Early stopping criteria reached.")
                     break
+        S = sorted(S)
+
+        #-------------------------------------------------------------------#
+        # Generate feature importance scores.                               #
+        #-------------------------------------------------------------------#
+        S, best_score, feature_importances, feature_raking = \
+            self._generate_feature_importances(best_soln=S, Q=feature_mi_matrix, num_features=num_features)
 
-        # ---------------------------------------------------------------------
-        # SAVE RESULTS
-        # ---------------------------------------------------------------------
+        #-------------------------------------------------------------------#
+        # Save results                                                      #
+        #-------------------------------------------------------------------#
+        num_features_sel = n_features
         self.n_features_ = len(S)
+        self.feature_importances_ = feature_importances
         self._support_mask = np.zeros(p, dtype=np.bool)
         self._support_mask[S] = True
-        self.ranking_ = S
-        self.mi_ = S_mi
+        #self.mi_ = S_mi
+
+        self._print_end_result(num_features_sel=num_features_sel, num_features=num_features, best_soln=S)
 
         return self
 
+    def _print_init_result(self):
+        logging.info("Started MIBS.")
+        logging.info(" - Method        : {}".format(self.method))
+        logging.info(" - Num. threads  : {}".format(self.n_jobs))
+        logging.info(" - Num. features : {}".format(self.n_features))
+        logging.info("{0:>5}{1:>5}{2:15}".format("Iter", "Rmv", "    Current MI"))
+
+    def _print_curr_result(self, S, selected, MIs):
+        logging.info("{0:>5}{1:>5}{2:+14.6E}".format(len(S), selected, MIs[-1]))
+
+    def _print_end_result(self, num_features_sel, num_features, best_soln):
+        logging.info("Done MIBS.")
+        logging.info(" - Total feat.   : {}".format(num_features))
+        logging.info(" - Target feat.  : {}".format(num_features_sel))
+        logging.info(" - Actual feat.  : {}".format(len(best_soln)))
+
 
 class MutualInformationConicOptimization(MutualInformationBase):
     """
-    MI_FS stands for Mutual Information based Feature Selection.
-    This class contains routines for selecting features using both
-    continuous and discrete y variables. Three selection algorithms are
-    implemented: JMI, JMIM and MRMR.
+    Conic optimization approach for feature selection.
+
+    This class implements a conic optimization based feature selection method with mutual information (MI) measure [1]_. This idea is to formulate the selection problem as a pure-binary quadratic optimization problem, which can be heuristically solved by an efficient randomization algorithm via semidefinite programming [2]_. Optimization software **Colin** [6]_ is used for solving the underlying conic optimization problems.
 
-    This implementation tries to mimic the scikit-learn interface, so use fit,
-    transform or fit_transform, to run the feature selection.
 
     Parameters
     ----------
 
-    method : string, default = 'JMI'
-        Which mutual information based feature selection method to use:
-        - 'JMI' : Joint Mutual Information [1]
-        - 'JMIM' : Joint Mutual Information Maximisation [2]
-        - 'MRMR' : Max-Relevance Min-Redundancy [3]
+    method : string, optional (default='JMI')
+
+        A string that specifies the mutual information measure. Possible options are:
+
+        - **JMI** : Joint Mutual Information [3]_
+        - **JMIM** : Joint Mutual Information Maximisation [4]_
+        - **MRMR** : Max-Relevance Min-Redundancy [5]_
+
+    k : int, optional (default=5)
 
-    k : int, default = 5
-        Sets the number of samples to use for the kernel density estimation
-        with the kNN method. Kraskov et al. recommend a small integer between
-        3 and 10.
+        An integer to specify number of samples for the kernel density estimation using KNN. A small integer between
+        3 and 10 is recommended. Note that this parameter is applicable only if `num_bins` is set to 0.
 
-    n_features : int or string, default = 'auto'
-        If int, it sets the number of features that has to be selected from X.
-        If 'auto' this is determined automatically based on the amount of
-        mutual information the previously selected features share with y.
+    n_features : int or string, optional (default='auto')
 
-    categorical : Boolean, default = True
-        If True, y is assumed to be a categorical class label. If False, y is
-        treated as a continuous. Consequently this parameter determines the
-        method of estimation of the MI between the predictors in X and y.
+        A positive integer number that specifies the number of features to be selected.
+        If 'auto' is used, then this number will be determined automatically.
+
+    categorical : boolean, optional (default=True)
+
+        If `categorical` is `True`, then the dependent variable is assumed to be a categorical class label. Otherwise, the dependent variable is assumed to be a continuous variable.
 
     n_jobs : int, optional (default=1)
-        The number of threads to open if possible.
 
-    verbose : int, default=0
-        Controls verbosity of output:
-        - 0: no output
-        - 1: displays selected features
-        - 2: displays selected features and mutual information
+        The number of threads allowed for the computations. If `n_jobs` :math:`<= 0`, then it will be determined internally.
+
+    verbose : int, optional (default=0)
+
+        An integer number to specify the verbosity level. Available options are:
+
+        - 0: Disable output.
+        - 1: Display summary results.
+        - 2: Displays all results.
+
+    scale_data : boolean, optional (default=True)
+
+        An boolean flag that specifies if the input data input shall be scaled first.
+
+    num_bins : int, optional (default=0)
+
+        An integer number to specify the number of bins allowed for data binning (bucketing). If :math:`0`, then data binning will be disabled, and KNN will be used for kernal density estimation.
+
+
+    random_state : int, optional (default=0)
+
+        An integer number that specifies the random seed number.
+
+    max_roundings : int, optional (default=0)
+
+        An positive integer to specify the number of iterations allowed for the rounding solution process in [2]_. If `max_roundings` is 0, then this number will be determined internally.
 
-    num_bins : int, default=0
-        Number of bins for binning the data.
 
     Attributes
     ----------
 
     n_features_ : int
+
         The number of selected features.
 
-    support_ : array of length X.shape[1]
-        The mask array of selected features.
+    feature_importances_ : array of shape n_features
 
-    ranking_ : array of shape n_features
-        The feature ranking of the selected features, with the first being
-        the first feature selected with largest marginal MI with y, followed by
-        the others with decreasing MI.
+        The feature importance scores of the selected features.
 
-    mi_ : array of shape n_features
-        The JMIM of the selected features. Usually this a monotone decreasing
-        array of numbers converging to 0. One can use this to estimate the
-        number of features to select. In fact this is what n_features='auto''
-        tries to do heuristically.
 
     Examples
     --------
 
-    import pandas as pd
-    import mifs
-
-    # load X and y
-    X = pd.read_csv('my_X_table.csv', index_col=0).values
-    y = pd.read_csv('my_y_vector.csv', index_col=0).values
-
-    # define MI_FS feature selection method
-    feat_selector = mifs.MutualInformationFeatureSelector()
+    This implementation follows the scikit-learn API convention:
 
-    # find all relevant features
-    feat_selector.fit(X, y)
+    - ``fit(X, y)``
+    - ``transform(X)``
+    - ``fit_transform(X, y)``
 
-    # check selected features
-    feat_selector.support_
-
-    # check ranking of features
-    feat_selector.ranking_
-
-    # call transform() on X to filter it down to selected features
-    X_filtered = feat_selector.transform(X)
 
     References
     ----------
 
-    [1] H. Yang and J. Moody, "Data Visualization and Feature Selection: New
-        Algorithms for Nongaussian Data"
-        NIPS 1999
-    [2] Bennasar M., Hicks Y., Setchi R., "Feature selection using Joint Mutual
-        Information Maximisation"
-        Expert Systems with Applications, Vol. 42, Issue 22, Dec 2015
-    [3] H. Peng, Fulmi Long, C. Ding, "Feature selection based on mutual
-        information criteria of max-dependency, max-relevance,
-        and min-redundancy"
-        Pattern Analysis & Machine Intelligence 2005
-    [4] T. Naghibi , S. Hoffmann, and B. Pfister, "A Semidefinite Programming Based Search
-        Strategy for Feature Selection with Mutual Information Measure"
-        IEEE Transactions on Pattern Analysis and Machine Intelligence, 37(8), 2015, pp 1529--1541.
+    .. [1] T Naghibi, S Hoffmann and B Pfister, "A semidefinite programming based search strategy for feature selection with mutual information measure", IEEE Transactions on Pattern Analysis and Machine Intelligence, 37(8), pp. 1529--1541, 2015. [`Pre-print <http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.739.8516&rep=rep1&type=pdf>`_]
+    .. [2] M Goemans and D Williamson, "Improved approximation algorithms for maximum cut and satisfiability problems using semidefinite programming", J. ACM, 42(6), pp. 1115--1145, 1995 [`Pre-print <http://www-math.mit.edu/~goemans/PAPERS/maxcut-jacm.pdf>`_]
+    .. [3] H Yang and J Moody, "Data Visualization and Feature Selection: New Algorithms for Nongaussian Data", NIPS 1999. [`Pre-print <https://papers.nips.cc/paper/1779-data-visualization-and-feature-selection-new-algorithms-for-nongaussian-data.pdf>`_]
+    .. [4] M Bennasar, Y Hicks, abd R Setchi, "Feature selection using Joint Mutual Information Maximisation", Expert Systems with Applications, 42(22), pp. 8520--8532, 2015 [`pre-print <https://core.ac.uk/download/pdf/82448198.pdf>`_]
+    .. [5] H Peng, F Long, and C Ding, "Feature selection based on mutual information criteria of max-dependency, max-relevance, and min-redundancy", IEEE Transactions on Pattern Analysis and Machine Intelligence, 27(8), pp. 1226--1238, 2005. [`Pre-print <http://ranger.uta.edu/~chqding/papers/mRMR_PAMI.pdf>`_]
+    .. [6] Colin: Conic-form Linear Optimizer (`www.colinopt.org <www.colinopt.org>`_).
     """
 
     def __init__(self,
@@ -1119,18 +1252,26 @@ def __init__(self,
                  k=5,
                  n_features='auto',
                  categorical=True,
-                 n_jobs=1,
+                 n_jobs=0,
                  verbose=0,
                  scale_data=True,
-                 num_bins=0):
+                 num_bins=0,
+                 random_state=0,
+                 max_roundings=0):
         # Call base constructor.
         super(MutualInformationConicOptimization, self).__init__(
             method, k, n_features, categorical,
             n_jobs, verbose, scale_data, num_bins)
+        self.random_state = random_state
+        self.max_roundings = max_roundings
+
+        # Attributes.
+        self.n_features_ = 0
+        self.feature_importances_ = []
 
     def fit(self, X, y):
         """
-        Fits the MI_FS feature selection with the chosen MI_FS method.
+        Fits the feature selection with conic optimization approach.
 
         Parameters
         ----------
@@ -1140,117 +1281,464 @@ def fit(self, X, y):
         y : array-like, shape = [n_samples]
             The target values.
         """
+        # Colin
+        import colinpy
+        #from colinpy import *
+        from colinpy import ClnModel, ClnError
 
         #-------------------------------------------------------------------#
         # Initialize the parameters.                                        #
         #-------------------------------------------------------------------#
         self.X, y = self._init_data(X, y)
-        n, p = X.shape
-        self.y = y.reshape((n, 1))
+        n, num_features = X.shape
+        self.y = y.reshape(n, 1)
 
-        # list of selected features
-        S = list(range(p))
+        # list of features
+        S = list(range(num_features))
 
         #-------------------------------------------------------------------#
         # Create the MI matrix.                                             #
         #-------------------------------------------------------------------#
-        if self.n_features != 'auto':
-            feature_mi_matrix = np.zeros((self.n_features, p))
-        else:
-            feature_mi_matrix = np.zeros((n, p))
-        feature_mi_matrix[:] = 0.0
+        # Notation:
+        # - N : num_features
+        # - P : num_features_sel
+        Q = np.zeros((num_features, num_features))
+        Q[:] = 0.0
 
-        # Calculate max features
+        # Calculate number of the selected features and the parameter.
         if self.n_features == 'auto':
-            n_features = p
+            num_features_sel = min(1000, max(1, num_features * 0.2))
         else:
-            n_features = min(p, self.n_features)
-        offdiagonal_param = -0.5 / (n_features - 1)
-        print("offdiagonal_param", offdiagonal_param)
-        print("S", S)
-        print("self._are_data_binned())", self._are_data_binned())
-        for s in S:
-            feature_mi_matrix[s, S] = get_mico_vector(self, self.k, S, s, offdiagonal_param, self._are_data_binned())
+            num_features_sel = min(num_features, self.n_features)
+        offdiagonal_param = -0.5 / (num_features_sel - 1)
+
+        # Calculate the number of rounding iterations.
+        if self.max_roundings <= 0:
+            self.max_roundings = num_features_sel
+
+        # Populate info.
+        self._print_init_result(num_features_sel, num_features, offdiagonal_param)
+
+        # Calculate the MI matrix.
+        logging.info("Started calculating full MI matrix.")
+        if self.categorical:
+            Nx = []
+            classes = np.unique(y)
+            sum_y = {}
+            for yi in classes:
+                sum_y[yi] = np.sum(y == yi)
+            for yi in y.flatten():
+                Nx.append(sum_y[yi])
+
+        if True:
+            # Parallelize the outer loop - faster.
+            Qtri = Parallel(n_jobs=self.n_jobs)(delayed(get_mico_vector)(
+                self, self.k, list(range(i, num_features, 1)), s, offdiagonal_param, self._are_data_binned(), Nx if self.categorical else None, 1
+            ) for i, s in enumerate(S))
+
+            for i, s in enumerate(S):
+                Q[s, list(range(i, num_features, 1))] = Qtri[s]
+        else:
+            # Parallelize the outer loop - slower.
+            for i, s in enumerate(S):
+                S2 = list(range(i, num_features, 1))
+                Q[s, S2] = get_mico_vector(self, self.k, S2, s, offdiagonal_param, self._are_data_binned(), Nx if self.categorical else None, self.n_jobs)
 
         # Ensure the MI matrix is symmetric.
-        feature_mi_matrix = mico_utils.make_mat_sym(feature_mi_matrix)
-        print(feature_mi_matrix)
+        for i in range(num_features):
+            for j in range(i + 1, num_features):
+                Q[j, i] = Q[i, j]
 
-        raise ValueError("STOP")
+        # Calculate Q^T e.
+        QTe = np.zeros(num_features)
+        for s in S:
+            QTe[s] = Q[s, :].sum()
+            assert(QTe[s] == Q[:, s].sum())
 
+        # Create Q^u matrix.
+        # The first row/column is the auxiliary variable.
+        Qu = np.zeros((num_features + 1, num_features + 1))
+        Qu[0, 0] = 0.0
+        Qu[1:(num_features + 1), 1:(num_features + 1)] = Q
+        Qu[0, 1:(num_features + 1)] = QTe
+        Qu[1:(num_features + 1), 0] = QTe
 
-        # ---------------------------------------------------------------------
-        # FIND FIRST FEATURE
-        # ---------------------------------------------------------------------
+        #-------------------------------------------------------------------#
+        # Input optimization model.                                         #
+        #                                                                   #
+        # See                                                               #
+        #   A Semidefinite Programming Based Search Strategy for Feature    #
+        #   Selection with Mutual Information Measure, Eq (26)              #
+        #-------------------------------------------------------------------#
+        logging.info("Started creating semidefinite optimization model.")
+        def map_ij_to_k(i, j, size):
+            return i * size + j
+
+        def map_k_to_ij(k, size):
+            return int(k/size), int(k%size)
+
+        # Cone information.
+        semidefinite_cone = list(range((num_features + 1) * (num_features + 1)))
+
+        # Constraint 1.
+        row1_idx = []
+        row1_val = []
+        for i in range(1, num_features + 1):
+            for j in range(1, num_features + 1):
+                row1_idx += [map_ij_to_k(i, j, num_features + 1)]
+                row1_val += [1.0]
+        row1_rhs = (2.0 * num_features_sel - num_features) ** 2
+
+        # Constraint 2.
+        row2_idx = []
+        row2_val = []
+        for i in range(1, num_features + 1):
+            row2_idx += [map_ij_to_k(0, i, num_features + 1)]
+            row2_val += [1.0]
+        for i in range(1, num_features + 1):
+            row2_idx += [map_ij_to_k(i, 0, num_features + 1)]
+            row2_val += [1.0]
+        row2_rhs = 2.0 * (2.0 * num_features_sel - num_features)
+
+        # Model.
+        CLN_INFINITY = ClnModel.get_infinity()
+        model = ClnModel()
+
+        try:
+            # Step 1. Create a model and change the parameters.
+            # Create an empty model.
+            model.create_mdl()
+
+            # Set parameters.
+            model.set_ipa("Model/Verbose", 3 if self.verbose >= 2 else self.verbose)
+            model.set_ipa("Model/Presolver/PresolveLv", 1)
+            model.set_ipa("Ips/Solver/NumThreads", self.n_jobs)
+            model.set_ipa("Ips/Solver/Type", 2)
+
+            # Step 2. Input model.
+            # Change to maximization problem.
+            model.set_max_obj_sense()
+
+            # Add variables.
+            # One semidefinite block.
+            for i in range(0, num_features + 1):
+                for j in range(0, num_features + 1):
+                    model.add_col(-CLN_INFINITY, CLN_INFINITY, Qu[i, j], [], [])
+
+            # Add constraints.
+            # - Cnstraint 1:
+            #   \sum_{i,j}^N Y_{ij} = (2P - N)^2.
+            model.add_row(row1_rhs, row1_rhs, row1_idx, row1_val)
+
+            # - Constraint 2:
+            #   \sum_{i}^N Y_{i0} + \sum_{i}^N Y_{0i} = 2 * (2P - N).
+            model.add_row(row2_rhs, row2_rhs, row2_idx, row2_val)
+
+            # - Constraint 3:
+            #   diag(Y) = e.
+            for i in range(0, num_features + 1):
+                model.add_row(1.0, 1.0, [map_ij_to_k(i, i, num_features + 1)], [1.0])
+
+            # - Add semidefinite conic constraint.
+            model.add_semi_definite_cone(semidefinite_cone)
+
+            # Step 3. Solve the problem and populate the result.
+            # Solve the problem.
+            model.solve_prob()
+            if not model.has_solution():
+                logging.warning("Resolving the problem again.")
+                model.set_ipa("Ips/Solver/Type", 3)
+                model.solve_prob()
+
+        except ClnError as e:
+            logging.error("Received Colin exception.")
+            logging.error(" - Explanation   : {}".format(e.message))
+            logging.error(" - Code          : {}".format(e.code))
+        except Exception as e:
+            logging.error("Received exception.")
+            logging.error(" - Explanation   : {}".format(e))
+        finally:
+            # Step 4. Display the result and free the model.
+            if self.verbose >= 1:
+                model.display_results()
+
+            # Retrieve result code, optimization status, and solution status.
+            result = int(model.get_solver_result())
+            opt_status = model.get_solver_opt_status()
+            sol_status = model.get_solver_sol_status()
+            has_solution = model.has_solution()
+
+            # Note: Non-optimal status (ENM_OPT_STATUS_OPTIMAL).
+            make_dir("./log/")
+            if opt_status != 1:
+                if has_solution:
+                    # TODO Support SDP in LP/MPS output.
+                    # Can continue.
+                    logging.warning("Colin failed to solve the problem to optimality, but at least a primal solution is available.")
+                    instance = get_time_stamp()
+                    filename = "./log/warn_code_{0}_time_{1}.lp".format(opt_status, instance)
+                    #model.write_prob(filename)
+                else:
+                    # Cannot continue.
+                    instance = get_time_stamp()
+                    filename = "./log/err_code_{0}_time_{1}.lp".format(opt_status, instance)
+                    #model.write_prob(filename)
+
+                    msg = "Colin failed to solve the problem to optimality. Terminated."
+                    logging.error(msg)
+                    raise ValueError(msg)
+
+                logging.info(" - Result        : {0} ({1})".format(model.explain_result(result), result))
+                logging.info(" - Opt status    : {0} ({1})".format(model.explain_opt_status(opt_status), opt_status))
+                logging.info(" - Sol status    : {0} ({1})".format(model.explain_sol_status(sol_status), sol_status))
+                logging.info(" - Has solution  : {0}".format(has_solution))
+            else:
+                logging.info("Colin solved the problem to optimality.")
+
+            if has_solution:
+                prim_soln = model.get_prim_soln()
+
+            model.free_mdl()
+
+        # Create covariance matrix.
+        logging.info("Started generating covariance matrix.")
+        mean_vec = np.zeros(num_features + 1)
+        cov_mat = []
+        for i in range(0, num_features + 1):
+            for j in range(0, num_features + 1):
+                cov_mat.append(prim_soln[map_ij_to_k(i, j, num_features + 1)])
+        #cov_mat = list(map(lambda x : round(x, 4), cov_mat))
+        #print(cov_mat)
+
+        # Perturbation.
+        for k in range(0, (num_features + 1) * (num_features + 1)):
+            i, j = map_k_to_ij(k, num_features + 1)
+            if i == j:
+                # Add eps to diagonal.
+                pass
+                cov_mat[k] += 1.E-4
+
+        cov_mat = np.matrix(cov_mat).reshape((num_features + 1), (num_features + 1))
+        #print("cov_mat", cov_mat)
+
+        # Rounding solution.
+        best_diff = math.inf
+        best_score = -math.inf
+        best_soln = []
+        pdf = stats.multivariate_normal(mean_vec, cov_mat)
+        num_roundings = 0
+
+        if DEBUG:
+            print("Primal soln: {}".format(prim_soln))
+            dbg_opt_val = 0
+            for i in range(0, num_features + 1):
+                for j in range(0, num_features + 1):
+                    dbg_opt_val += prim_soln[i * (num_features + 1) + j] * Qu[i, j]
+            print("Obj: {}", dbg_opt_val)
+            for i in range(0, num_features + 1):
+                print(Qu[i, 0:(num_features + 1)])
+
+        logging.info("Started rounding process.")
+        logging.info(" - Max roundings : {}".format(self.max_roundings))
+        logging.info("{0:>5}{1:>5}{2:15}".format("Iter", "Diff", "      ObjValue"))
+        seed = self.random_state
+        while num_roundings < self.max_roundings:
+            sampled_pt = pdf.rvs(1, random_state=seed)
+            seed += 1
+            ref_pt = sampled_pt[0]
+            if abs(ref_pt) < 0.1:
+                #print("ref_pt", ref_pt)
+                continue
+
+            # Solution rounding.
+            if ref_pt >= 0:
+                curr_soln = [i for i, e in enumerate(sampled_pt[1:len(sampled_pt)]) if e >= 0]
+            else:
+                curr_soln = [i for i, e in enumerate(sampled_pt[1:len(sampled_pt)]) if e <= 0]
+            curr_soln_binary = self._convert_idx_array_to_mask_array(curr_soln, num_features)
+
+            msg = ""
+            if True:
+                # Calculate feature difference.
+                curr_features_sel = np.sum(curr_soln_binary)
+                curr_diff = abs(curr_features_sel - num_features_sel)
+                curr_score = self._calc_xQx(Q, curr_soln_binary)
+                # Update statistics.
+                if curr_diff < best_diff:
+                    best_diff = curr_diff
+                    best_score = curr_score
+                    best_soln = curr_soln
+                    msg += "*"
+                elif curr_diff == best_diff and best_diff == 0:
+                    # Check objective value.
+                    # Update statistics.
+                    if curr_score > best_score:
+                        best_score = curr_score
+                        best_soln = curr_soln
+                        msg += "**"
 
+            else:
+                # Calculate the score.
+                curr_score = self._calc_xQx(Q, curr_soln_binary)
 
+                # Update statistics.
+                if curr_score > best_score or best_score == -math.inf:
+                    best_score = curr_score
+                    best_soln = curr_soln
+                    msg += "*"
 
-        # ---------------------------------------------------------------------
-        # FIND FIRST FEATURE
-        # ---------------------------------------------------------------------
-        xy_MI = np.array(get_first_mi_vector(self, self.k, self._are_data_binned()))
+            # Populate information.
+            num_roundings += 1
+            logging.info("{0:>5}{1:>5}{2:+14.6E} {3}".format(num_roundings, int(curr_diff), curr_score, msg))
 
-        # choose the best, add it to S, remove it from F
-        S, F = self._add_remove(S, F, bn.nanargmax(xy_MI))
-        S_mi.append(bn.nanmax(xy_MI))
+        #-------------------------------------------------------------------#
+        # Generate feature importance scores.                               #
+        #-------------------------------------------------------------------#
+        best_soln, best_score, feature_importances, feature_raking = \
+            self._generate_feature_importances(best_soln, Q, num_features)
 
-        # notify user
-        if self.verbose > 0:
-            self._print_results(S, S_mi)
+        #-------------------------------------------------------------------#
+        # Save results                                                      #
+        #-------------------------------------------------------------------#
+        self.n_features_ = len(best_soln)
+        self.feature_importances_ = feature_importances
+        self._support_mask = np.zeros(num_features, dtype=np.bool)
+        self._support_mask[best_soln] = True
 
-        # ---------------------------------------------------------------------
-        # FIND SUBSEQUENT FEATURES
-        # ---------------------------------------------------------------------
-        if self.n_features == 'auto':
-            n_features = np.inf
-        else:
-            n_features = self.n_features
+        self._print_end_result(num_features_sel, num_features, best_soln)
 
-        while len(S) < n_features:
-            # loop through the remaining unselected features and calculate MI
-            s = len(S) - 1
-            feature_mi_matrix[s, F] = get_mi_vector(self, self.k, F, S[-1], self._are_data_binned())
+        return self
 
-            # make decision based on the chosen FS algorithm
-            fmm = feature_mi_matrix[:len(S), F]
-            if self.method == 'JMI':
-                selected = F[bn.nanargmax(bn.nansum(fmm, axis=0))]
-            elif self.method == 'JMIM':
-                if bn.allnan(bn.nanmin(fmm, axis=0)):
-                    break
-                selected = F[bn.nanargmax(bn.nanmin(fmm, axis=0))]
-            elif self.method == 'MRMR':
-                if bn.allnan(bn.nanmean(fmm, axis=0)):
-                    break
-                MRMR = xy_MI[F] - bn.nanmean(fmm, axis=0)
-                selected = F[bn.nanargmax(MRMR)]
-                S_mi.append(bn.nanmax(MRMR))
+    def _print_init_result(self, num_features_sel, num_features, offdiagonal_param):
+        logging.info("Started MICO.")
+        logging.info(" - Method        : {}".format(self.method))
+        logging.info(" - Num. threads  : {}".format(self.n_jobs))
+        logging.info(" - Tot. features : {}".format(num_features))
+        logging.info(" - Sel. features : {}".format(num_features_sel))
+        logging.info(" - Off-diag param: {}".format(offdiagonal_param))
 
-            # record the JMIM of the newly selected feature and add it to S
-            if self.method != 'MRMR':
-                S_mi.append(bn.nanmax(bn.nanmin(fmm, axis=0)))
-            S, F = self._add_remove(S, F, selected)
+    def _print_end_result(self, num_features_sel, num_features, best_soln):
+        logging.info("Done MICO.")
+        logging.info(" - Total feat.   : {}".format(num_features))
+        logging.info(" - Target feat.  : {}".format(num_features_sel))
+        logging.info(" - Actual feat.  : {}".format(len(best_soln)))
 
-            # notify user
-            if self.verbose > 0:
-                self._print_results(S, S_mi)
 
-            # if n_features == 'auto', let's check the S_mi to stop
-            if self.n_features == 'auto' and len(S) > 10:
-                # smooth the 1st derivative of the MI values of previously sel
-                MI_dd = signal.savgol_filter(S_mi[1:], 9, 2, 1)
-                # does the mean of the last 5 converge to 0?
-                if np.abs(np.mean(MI_dd[-5:])) < 1e-3:
-                    break
+def test_mi():
+    import pandas as pd
+    import numpy as np
+    from pyitlib import discrete_random_variable as drv
+
+    #X = np.array((1.0, 2.0, 1.0, 2.0, 4.12, 4.123, 6.123))
+    #print(drv.entropy(X))
+    X = np.array((1.0, 2.0, 1.0, 2.0, 4.123, 4.123, 10.123, 1.1))
+    #print(drv.entropy(X))
+    #print(pd.qcut(np.array(X), 4, retbins=True))
+    #print(pd.qcut(np.array(X), 4, retbins=True)[1])
+
+    if False:
+        # Test bin.
+        print("=" * 80)
+        print("Testing bin")
+        print("-" * 80)
+        num_bins = 3
+
+        # - Binned with pandas.
+        pd_bins = pd.cut(np.array(X), num_bins, retbins=True)[1]
+        labels = [i for i in range(num_bins)]
+        pd_data = np.array(pd.cut(X, bins=pd_bins , labels=labels, include_lowest=True))
+        print("Binned with PD.")
+        print(" - Bin  : {}".format(pd_bins))
+        print(" - Data : {}".format(pd_data))
+        #print(type(pd.cut(X, bins=bins, labels=labels, include_lowest=True)))
+
+        # - Binned with NP.
+        np_bin_res = np.histogram(X, bins=num_bins)
+        np_data = np_bin_res[0]
+        np_bins = np_bin_res[1]
+        print("Binned with NP.")
+        print(" - Bin  : {}".format(np_bins))
+        print(" - Data : {}".format(np_data))
+
+
+    # - Binned with SKL.
+    X = np.array([[ -3., 5., 15 ],
+                  [ 0.,  6., 14 ],
+                  [ 6.,  3., 11 ],
+                  [ 16., -3.,13 ],
+                  [ 12., -3.,10 ],
+                  [ 16.4, -2.,11.2 ],
+                  [ 26., 3., 211]
+                 ]
+    )
+    skl_data = bin_data(X, num_bins=3)
+    print("Binned with SKL.")
+    print(" - Num features : {}".format(X.shape[1]))
+    #print(" - Bin  : {}".format(pd_bins))
+    print(" - Data :\n {}".format(skl_data))
+
+    print("Scaled dense matrix with SKL.")
+    den_data = scale_data(X)
+    print(" - Num features : {}".format(den_data.shape[1]))
+    print(" - Data :\n {}".format(den_data))
+    print(" - Mean :\n {}".format(den_data.mean(axis=0)))
+    print(" - Std  :\n {}".format(den_data.std(axis=0)))
+
+    print("Scaled sparse matrix with SKL.")
+    csc_data = sparse.csc_matrix(X)
+    csr_data = csc_data.tocsr()
+    spa_data = scale_data(csr_data)
+    print(" - Num features : {}".format(spa_data.shape[1]))
+    print(" - Mean :\n {}".format(spa_data.mean(axis=0)))
+
+
+    print("=" * 80)
+
+    #h = np.histogram2d(X, Y, bins=bins)[0]
+    #print(X)
+
+    #print(np.array(df['qcut4']))
+    #print(drv.entropy(data))
+    #print(np.hstack(np.array([X, X])))
+
+
+def scale_data(X):
+    """"
+    Scale the data using SKL.
+
+    References
+    ----------
+    [1] http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html
+    """
+    from sklearn.preprocessing import MinMaxScaler, StandardScaler, MaxAbsScaler
 
-        # ---------------------------------------------------------------------
-        # SAVE RESULTS
-        # ---------------------------------------------------------------------
+    # X must be stored in a row-wise order.
+    if sparse.issparse(X):
+        scalar = MaxAbsScaler()
+    else:
+        scalar = StandardScaler()#MinMaxScaler() # StandardScaler
 
-        self.n_features_ = len(S)
-        self._support_mask = np.zeros(p, dtype=np.bool)
-        self._support_mask[S] = True
-        self.ranking_ = S
-        self.mi_ = S_mi
+    return scalar.fit_transform(X)
 
-        return self
+
+def bin_data(X, num_bins):
+    """"
+    Bin the data using SKL.
+    """
+    # X must be stored in a row-wise order.
+    from sklearn.preprocessing import KBinsDiscretizer
+
+    if type(num_bins) is int:
+        #bin_set = [ min(num_bins, len(array))]
+        # @todo Check unique value in each feature.
+        bin_set = [ num_bins for i in range(X.shape[1]) ]
+    else:
+        bin_set = num_bins
+
+    est = KBinsDiscretizer(n_bins=bin_set, encode='ordinal', strategy='uniform').fit(X)
+
+    return est.transform(X)
+
+
+if __name__ == '__main__':
+    test_mi()
\ No newline at end of file
diff --git a/mico/mico_utils.py b/mico/mico_utils.py
index 3807509..aeddfd6 100644
--- a/mico/mico_utils.py
+++ b/mico/mico_utils.py
@@ -20,6 +20,22 @@
 DEBUG = False
 
 
+import random
+# Function to generate
+# and append them
+# start = starting range,
+# end = ending range
+# num = number of
+# elements needs to be appended
+def get_rand_list(start, end, num):
+    res = []
+
+    for j in range(num):
+        res.append(random.randint(start, end))
+
+    return res
+
+
 def make_mat_sym(mat):
     return (mat.transpose() + mat) / 2.0
 
@@ -134,9 +150,6 @@ def get_mutual_information_cc(x, y, k=5):
     -------
         get_mutual_information((X, Y))
     '''
-    #if len(variables) < 2:
-    #    raise AttributeError(
-    #            "Mutual information must involve at least 2 variables")
     all_vars = np.hstack([x, y]) # Stacked all features.
     # I(A, B) = -\sum_{a, b} P(a, b) / (P(a)P(b)) = H(A) + H(B) − H(A,B), where
     # H(A) = -\sum_{a}P(a)log_2 P(a).
@@ -145,7 +158,7 @@ def get_mutual_information_cc(x, y, k=5):
     return res
 
 
-def get_mutual_information_cd(x, y, k):
+def get_mutual_information_cd(x, y, k, Nx=None):
     """
     Calculates the mutual information between a continuous vector x and a
     discrete class vector y.
@@ -158,10 +171,7 @@ def get_mutual_information_cd(x, y, k):
     Brian C. Ross, 2014, PLOS ONE
     Mutual Information between Discrete and Continuous Data Sets
     """
-    #if y.shape[1] > 1:
-    #    y = encode_discrete_x(y)
-    #else:
-    #    y = y.flatten()
+    #print("BGN KNN")
 
     n = x.shape[0]
     classes = np.unique(y)
@@ -170,14 +180,28 @@ def get_mutual_information_cd(x, y, k):
     dist_to_k_neighbors = np.empty(n)
 
     # number of points within each point's class
-    Nx = []
-    for yi in y:
-        Nx.append(np.sum(y == yi))
-
+    if Nx is None:
+        Nx = []
+        if False:
+            for yi in y:
+                #print(np.sum(y == yi))
+                Nx.append(np.sum(y == yi))
+        else:
+            sum_y = {}
+            for yi in classes:
+                sum_y[yi] = np.sum(y == yi)
+            for yi in y.flatten():
+                Nx.append(sum_y[yi])
+
+    #print(">> KNN")
     # find the distance of the kth in-class point
     # See https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors.kneighbors
     for c in classes:
         mask = np.where(y == c)[0]
+        #print(x[mask, :].shape[0])
+        if x[mask, :].shape[0] <= k:
+            continue
+            #raise ValueError("Parameter `k` is too large: current value = {0}, max possible value = {1}.".format(k, x[mask, :].shape[0]-1))
         knn.fit(x[mask, :])
         # Return the distance to the k nearest neighbors from each x of interest.
         dist = knn.kneighbors()[0]
@@ -189,11 +213,13 @@ def get_mutual_information_cd(x, y, k):
         #print(bn.nanmax(dist, axis=1), dist[:, -1])
     #print("dist_to_k_neighbors=", dist_to_k_neighbors)
 
+    #print(">> FIT-KNN")
     # find the number of points within the distance of the kth in-class point
     knn.fit(x)
     # Note: this is not supported in SKL, but it will still generate the correct result (turn DEBUG to test the implementation).
     m = knn.radius_neighbors(radius=dist_to_k_neighbors, return_distance=False)
     #print(m)
+    #print("DONE KNN")
 
     if DEBUG:
         print("Warning: Debug mode.")
@@ -218,6 +244,7 @@ def get_mutual_information_cd(x, y, k):
     MI = psi(n) - np.mean(psi(Nx)) + psi(k) - np.mean(psi(m_size))
     #print("MI = ", MI)
     #raise ValueError("STOP")
+    #print(">> MI")
 
     return MI
 
@@ -295,35 +322,42 @@ def get_interaction_information_3way(x1, x2, y, var_type, k=5):
     Optionally, the following keyword argument can be specified:
       k = number of nearest neighbors for density estimation
     Example: get_mutual_information((X, Y)), get_mutual_information((X, Y, Z), k=5)
+    Note
+    ====
+    Symmetric, but may be negative/
+
     '''
 
     # I(x1; x2; y) = I(x1, x2; y) - I(x1, y) - I(x2; y)
     #              = H(x1, x2) + H(x2, y) + H(x1, y) - H(x1) - H(x2) - H(y) - H(x1, x2, y).
     if var_type == "ccd":
         # Discrete y. All continuous X.
-        I_x1x2_y = get_mutual_information_cd(np.hstack([x1, x2]), y, k)
-        I_x1_y = get_mutual_information_cd(x1, y, k)
-        I_x2_y = get_mutual_information_cd(x2, y, k)
+        I_x1x2_y = max(0.0, get_mutual_information_cd(np.hstack([x1, x2]), y, k))
+        #print("I_x1x2_y", I_x1x2_y)
+        #print("np.hstack([x1, x2])", np.hstack([x1, x2]))
+        I_x1_y = max(0.0, get_mutual_information_cd(x1, y, k))
+        I_x2_y = max(0.0, get_mutual_information_cd(x2, y, k))
+        #print(I_x1x2_y - I_x1_y - I_x2_y)
         return I_x1x2_y - I_x1_y - I_x2_y
     elif var_type == "ccc":
         # Continuous y. All continuous X.
-        I_x1x2_y = get_mutual_information_cc(np.hstack([x1, x2]), y, k)
-        I_x1_y = get_mutual_information_cc(x1, y, k)
-        I_x2_y = get_mutual_information_cc(x2, y, k)
+        I_x1x2_y = max(0.0, get_mutual_information_cc(np.hstack([x1, x2]), y, k))
+        I_x1_y = max(0.0, get_mutual_information_cc(x1, y, k))
+        I_x2_y = max(0.0, get_mutual_information_cc(x2, y, k))
         return I_x1x2_y - I_x1_y - I_x2_y
     if var_type == "ddd":
         # Discrete y. All discrete X.
         x_encoded = encode_discrete_x(np.hstack([x1, x2]))
-        I_x1x2_y = get_mutual_information_dd(x_encoded, y)
-        I_x1_y = get_mutual_information_dd(x1, y)
-        I_x2_y = get_mutual_information_dd(x2, y)
+        I_x1x2_y = max(0.0, get_mutual_information_dd(x_encoded, y))
+        I_x1_y = max(0.0, get_mutual_information_dd(x1, y))
+        I_x2_y = max(0.0, get_mutual_information_dd(x2, y))
         return I_x1x2_y - I_x1_y - I_x2_y
     elif var_type == "ddc":
         # Continuous y. All discrete X.
         x_encoded = encode_discrete_x(np.hstack([x1, x2]))
-        I_x1x2_y = get_mutual_information_cd(y, x_encoded, k)
-        I_x1_y = get_mutual_information_cd(y, x1, k)
-        I_x2_y = get_mutual_information_cd(y, x2, k)
+        I_x1x2_y = max(0.0, get_mutual_information_cd(y, x_encoded, k))
+        I_x1_y = max(0.0, get_mutual_information_cd(y, x1, k))
+        I_x2_y = max(0.0, get_mutual_information_cd(y, x2, k))
         return I_x1x2_y - I_x1_y - I_x2_y
     else:
         raise ValueError("Unknown supported var_type {0} in get_interaction_information_3way".format(var_type))
diff --git a/mico/setup.py b/mico/setup.py
deleted file mode 100755
index 1abddb8..0000000
--- a/mico/setup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def configuration(parent_package='', top_path=None):
-    from numpy.distutils.misc_util import Configuration
-
-    config = Configuration('mico', parent_package, top_path)
-
-    return config
-
-if __name__ == "__main__":
-    from numpy.distutils.core import setup
-
-    config = configuration(top_path='').todict()
-    setup(**config)
diff --git a/mico/version.py b/mico/version.py
index 54eb6d8..d2a0b82 100755
--- a/mico/version.py
+++ b/mico/version.py
@@ -1,5 +1,18 @@
 """
-``mifs`` is a Parallelized Mutual Information based Feature Selection module.
+MICO: Mutual Information and Conic Optimization for feature selection.
+
+Github repo : https://github.com/jupiters1117/mico
+Author      : KuoLing Huang <jupiters1117@gmail.com>
+License     : BSD 3 clause
+
+
+Note
+----
+MICO is heavily inspired from MIFS by Daniel Homola:
+
+Github repo : https://github.com/danielhomola/mifs
+Author      : Daniel Homola <dani.homola@gmail.com>
+License     : BSD 3 clause
 """
 # Based on NiLearn package
 # License: simplified BSD
@@ -20,7 +33,7 @@
 # Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer.
 # 'X.Y.dev0' is the canonical version of 'X.Y.dev'
 #
-__version__ = '0.1.0.dev'
+__version__ = '0.1.0'
 
 _MIFS_DATASET_INSTALL_MSG = 'See %s for installation information.' % (
     'https://github.com/glemaitre/mico')
@@ -29,14 +42,14 @@
 #   in some meaningful order (more => less 'core').  We avoid using
 #   collections.OrderedDict to preserve Python 2.6 compatibility.
 REQUIRED_MODULE_METADATA = (
-    ('numpy', {
-        'min_version': '1.17.0',
-        'required_at_installation': True,
-        'install_info': _MIFS_DATASET_INSTALL_MSG}),
     ('scipy', {
         'min_version': '1.3.0',
         'required_at_installation': True,
         'install_info': _MIFS_DATASET_INSTALL_MSG}),
+    ('numpy', {
+        'min_version': '1.17.0',
+        'required_at_installation': True,
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
     ('sklearn', {
         'min_version': '0.20.0',
         'required_at_installation': True,
@@ -44,7 +57,24 @@
     ('joblib', {
         'min_version': '0.14.0',
         'required_at_installation': True,
-        'install_info': _MIFS_DATASET_INSTALL_MSG}))
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
+    ('psutil', {
+        'min_version': '5.6.0',
+        'required_at_installation': True,
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
+    ('pyitlib', {
+        'min_version': '0.2.0',
+        'required_at_installation': True,
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
+    ('bottleneck', {
+        'min_version': '1.0.0',
+        'required_at_installation': True,
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
+    ('colinpy', {
+        'min_version': '0.8.0',
+        'required_at_installation': True,
+        'install_info': _MIFS_DATASET_INSTALL_MSG}),
+)
 
 def _import_module_with_version_check(module_name, minimum_version,
                                       install_info=None):
@@ -57,22 +87,18 @@ def _import_module_with_version_check(module_name, minimum_version,
     except ImportError as exc:
         user_friendly_info = ('Module "{0}" could not be found. {1}').format(
            module_name,
-            install_info or 'Please install it properly to use'
-            ' imbalanced-learn.')
+            install_info or 'Please install it properly to use mico.')
         exc.args += (user_friendly_info,)
         raise
 
     # Avoid choking on modules with no __version__ attribute
     module_version = getattr(module, '__version__', '0.0.0')
 
-    version_too_old = (not LooseVersion(module_version) >=
-                       LooseVersion(minimum_version))
-
-    if version_too_old:
+    if module_version != "0.0.0" and not LooseVersion(module_version) >= LooseVersion(minimum_version):
         message = (
             'A {module_name} version of at least {minimum_version} '
-            'is required to use imbalanced-learn. {module_version} was '
-            'found. Please upgrade {module_name}').format(
+            'is required to use mico. {module_version} was '
+            'found. Please upgrade {module_name}.').format(
                module_name=module_name,
                 minimum_version=minimum_version,
                 module_version=module_version)
@@ -82,20 +108,23 @@ def _import_module_with_version_check(module_name, minimum_version,
     return module
 
 
-def _check_module_dependencies(is_imbalanced_dataset_installing=False):
-    """Throw an exception if imbalanced-learn dependencies are not installed.
+def _check_module_dependencies(is_installing=False):
+    """
+    Throw an exception if imbalanced-learn dependencies are not installed.
+
     Parameters
     ----------
-    is_imbalanced_dataset_installing: boolean
+    is_installing: boolean
         if True, only error on missing packages that cannot be auto-installed.
         if False, error on any missing package.
+
     Throws
-    -------
+    ------
     ImportError
     """
 
     for (module_name, module_metadata) in REQUIRED_MODULE_METADATA:
-        if not (is_imbalanced_dataset_installing and
+        if not (is_installing and
                 not module_metadata['required_at_installation']):
             # Skip check only when installing and it's a module that
             # will be auto-installed.
diff --git a/requirements.txt b/requirements.txt
index eafbc48..046ac3f 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
 scipy>=1.3.1
 numpy>=1.17.3
-scikit-learn>=0.21
+scikit-learn>=0.20.0
 joblib>=0.14.0
 psutil>=5.6.3
 pyitlib>=0.2.2
diff --git a/requirements_doc.txt b/requirements_doc.txt
new file mode 100755
index 0000000..c9f2558
--- /dev/null
+++ b/requirements_doc.txt
@@ -0,0 +1,8 @@
+Sphinx==1.8.5
+sphinx_bootstrap_theme
+sphinx_rtd_theme
+sphinxcontrib-bibtex==0.4.2
+#breathe
+numpydoc
+sphinx-gallery
+twine
diff --git a/run_dist.sh b/run_dist.sh
new file mode 100644
index 0000000..853f136
--- /dev/null
+++ b/run_dist.sh
@@ -0,0 +1,2 @@
+python3 setup.py sdist bdist_wheel
+twine upload dist/*
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..99a6ff4
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,12 @@
+
+[versioneer]
+VCS = git
+style = pep440
+versionfile_source = mico/version.py
+versionfile_build = mico/version.py
+tag_prefix=
+parentdir_prefix = 
+
+[metadata]
+description-file = README.rst
+license_file = LICENSE.txt
diff --git a/setup.py b/setup.py
index 2d1a946..40b01c8 100755
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,19 @@
 #! /usr/bin/env python
 """
-    MICO: Mutual Information and Conic Optimization for feature selection.
+MICO: Mutual Information and Conic Optimization for feature selection.
+
+Github repo : https://github.com/jupiters1117/mico
+Author      : KuoLing Huang <jupiters1117@gmail.com>
+License     : BSD 3 clause
+
+
+Note
+----
+MICO is heavily inspired from MIFS by Daniel Homola:
+
+Github repo : https://github.com/danielhomola/mifs
+Author      : Daniel Homola <dani.homola@gmail.com>
+License     : BSD 3 clause
 """
 
 import sys
@@ -11,28 +24,21 @@
 
 
 def load_requirements():
-    requirements = [
-        "scipy>=1.3.1",
-        "numpy>=1.17.3",
-        "scikit-learn>=0.21",
-        "joblib>=0.14.0",
-        "psutil>=5.6.3",
-        "pyitlib>=0.2.2"
-    ]
+    with open('requirements.txt') as f:
+        requirements = [l.strip() for l in f.readlines() if l]
     return requirements
 
 
 def load_version():
-    """Executes mico/version.py in a globals dictionary and
-    return it.
+    """
+    Executes mico/version.py in a globals dictionary and return it.
     """
     # load all vars into globals, otherwise
     #   the later function call using global vars doesn't work.
     globals_dict = {}
-    with codecs.open(os.path.join('mico', 'version.py'),
-                     encoding='utf-8-sig') as fp:
+    with codecs.open(os.path.join('mico', 'version.py'), encoding='utf-8-sig') as fp:
         exec(fp.read(), globals_dict)
-
+    print(globals_dict)
     return globals_dict
 
 
@@ -46,24 +52,24 @@ def is_installing():
 os.chdir(os.path.dirname(os.path.abspath(__file__)))
 
 _VERSION_GLOBALS = load_version()
-DISTNAME = 'mico'
+DISTNAME = 'colin-mico'
 DESCRIPTION = 'MICO: Mutual Information and Conic Optimization for feature selection.'
-with codecs.open('README.rst', encoding='utf-8-sig') as f:
+with codecs.open('README.rst.short') as f:
     LONG_DESCRIPTION = f.read()
 MAINTAINER = 'KuoLing Huang'
-MAINTAINER_EMAIL = 'colinopt.org@gmail.com'
-URL = 'https://github.com/glemaitre/mico'
-LICENSE = 'new BSD'
-DOWNLOAD_URL = 'https://github.com/glemaitre/mico' # To update
+MAINTAINER_EMAIL = 'jupiters1117@gmail.com'
+URL = 'https://github.com/jupiters1117/mico'
+LICENSE = 'BSD'
+DOWNLOAD_URL = 'https://github.com/jupiters1117/mico'
 VERSION = _VERSION_GLOBALS['__version__']
-REQUIREMENTS = load_requirements()
+INSTALL_REQUIRES = load_requirements()
 
 
 if __name__ == "__main__":
 
     if is_installing():
         module_check_fn = _VERSION_GLOBALS['_check_module_dependencies']
-        module_check_fn(is_imbalanced_dataset_installing=True)
+        module_check_fn(is_installing=True)
 
     install_requires = \
         ['%s>=%s' % (mod, meta['min_version'])
@@ -84,7 +90,7 @@ def is_installing():
         classifiers=[
             'Intended Audience :: Science/Research',
             'Intended Audience :: Developers',
-            'License :: OSI Approved',
+            'License :: OSI Approved :: BSD License',
             # 'Programming Language :: C',
             'Programming Language :: Python',
             'Topic :: Software Development',
@@ -95,7 +101,8 @@ def is_installing():
             'Operating System :: MacOS',
             # 'Programming Language :: Python :: 2.7',
             'Programming Language :: Python :: 3.6',
+            'Programming Language :: Python :: 3.7',
         ],
         packages=find_packages(),
-        install_requires=REQUIREMENTS
+        install_requires=INSTALL_REQUIRES
     )


Test patch:
diff --git a/tests/main_test.py b/tests/main_test.py
new file mode 100755
index 0000000..e54f326
--- /dev/null
+++ b/tests/main_test.py
@@ -0,0 +1,398 @@
+"""
+MICO: Mutual Information and Conic Optimization for feature selection.
+
+Github repo : https://github.com/jupiters1117/mico
+Author      : KuoLing Huang <jupiters1117@gmail.com>
+License     : BSD 3 clause
+
+
+Note
+----
+MICO is heavily inspired from MIFS by Daniel Homola:
+
+Github repo : https://github.com/danielhomola/mifs
+Author      : Daniel Homola <dani.homola@gmail.com>
+License     : BSD 3 clause
+"""
+from mico import MutualInformationForwardSelection,  MutualInformationBackwardElimination, MutualInformationConicOptimization
+from sklearn.datasets import make_classification, make_regression
+import numpy as np 
+import logging
+import argparse
+
+
+def check_selection(selected, i, r):
+    """
+    Check FN, FP, TP ratios among the selected features.
+    """
+    # reorder selected features
+    try:
+        selected = set(selected)
+        all_f = set(range(i+r))
+        TP = len(selected.intersection(all_f))
+        FP = len(selected - all_f)
+        FN =  len(all_f - selected)
+        if (TP+FN) > 0: 
+            sens = TP/float(TP + FN)
+        else:
+            sens = np.nan
+        if (TP+FP) > 0:
+            prec =  TP/float(TP + FP)   
+        else:
+            prec = np.nan
+    except:
+        sens = np.nan
+        prec = np.nan
+    return sens, prec
+
+
+def test_mifs():
+    # variables for dataset
+    s = 200  # Num rows
+    f = 100 # Num cols
+    i = int(.1 * f)  # Proportion of the relevant features
+    r = int(.05 * f)  # Proportion of the redundant features
+    c = 2
+
+    method = 'JMIM'
+    num_bins = 0
+    scale_data = False
+    k = max(1, int(f * 0.25))
+    verbose = 2
+    early_stop_steps = 10
+    n_jobs = 4
+
+    print("Parameters.")
+    print(" - method      : {}".format(method))
+    print(" - num_bins    : {}".format(num_bins))
+    print(" - scale_data  : {}".format(scale_data))
+    print(" - k           : {}".format(k))
+    print(" - verbose     : {}".format(verbose))
+    print(" - n_jobs      : {}".format(n_jobs))
+
+    # simulate dataset with discrete class labels in y
+    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
+                               n_redundant=r, n_clusters_per_class=c,
+                               random_state=1, shuffle=False)
+    #print(X)
+    #print(X.shape)
+    #print(sum(y) / len(y))
+
+    # perform feature selection
+    mifs = MutualInformationForwardSelection(
+        method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, early_stop_steps=early_stop_steps)
+    mifs.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mifs.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+    #print("Selected features: {}".format(np.where(mico.get_support())[0]))
+    #print(mifs.get_support())
+    #print(mifs.feature_importances_)
+    #print(mifs.ranking_)
+
+
+    # simulate dataset with continuous y
+    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
+                           random_state=0, shuffle=False)
+    # perform feature selection
+    mico = MutualInformationForwardSelection(
+        method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, early_stop_steps=early_stop_steps)
+    mico.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+
+
+def test_mibe():
+    # variables for dataset
+    s = 200  # Num rows
+    f = 100 # Num cols
+    i = int(.1 * f)  # Proportion of the relevant features
+    r = int(.05 * f)  # Proportion of the redundant features
+    c = 2
+    n_jobs = 4
+
+    method = 'JMIM'
+    num_bins = 0
+    scale_data = False
+    k = max(1, int(f * 0.25))
+    verbose = 2
+    early_stop_steps = 0
+
+    print("Parameters.")
+    print(" - method      : {}".format(method))
+    print(" - num_bins    : {}".format(num_bins))
+    print(" - scale_data  : {}".format(scale_data))
+    print(" - k           : {}".format(k))
+    print(" - verbose     : {}".format(verbose))
+    print(" - n_jobs      : {}".format(n_jobs))
+
+    # simulate dataset with discrete class labels in y
+    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
+                               n_redundant=r, n_clusters_per_class=c,
+                               random_state=1, shuffle=False)
+    #print(X)
+    #print(X.shape)
+    #print(sum(y) / len(y))
+
+    # perform feature selection
+    mibe =  MutualInformationBackwardElimination(
+        method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, early_stop_steps=early_stop_steps)
+    mibe.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mibe.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+    #print("Selected features: {}".format(np.where(mibe.get_support())[0]))
+    #print(mibe.get_support())
+    #print(mibe.feature_importances_)
+
+    # simulate dataset with continuous y
+    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
+                           random_state=0, shuffle=False)
+    # perform feature selection
+    mico =  MutualInformationBackwardElimination(
+        method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, early_stop_steps=early_stop_steps)
+    mico.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+
+
+def test_mico():
+    # variables for dataset
+    s = 200  # Num rows
+    f = 100 # Num cols
+    i = int(.1 * f)  # Proportion of the relevant features
+    r = int(.05 * f)  # Proportion of the redundant features
+    c = 2 # Classes
+
+    method = 'JMIM'
+    num_bins = 0
+    scale_data = False
+    k = max(1, int(f * 0.25))
+    verbose = 2
+    n_features = int(f * 0.20)
+    #n_features = 8#int(f / 2)
+    max_rounds = 0
+    n_jobs = 4
+
+    print("Parameters.")
+    print(" - method      : {}".format(method))
+    print(" - num_bins    : {}".format(num_bins))
+    print(" - scale_data  : {}".format(scale_data))
+    print(" - k           : {}".format(k))
+    print(" - verbose     : {}".format(verbose))
+    print(" - n_features  : {}".format(n_features))
+    print(" - max_rounds  : {}".format(max_rounds))
+    print(" - n_jobs      : {}".format(n_jobs))
+
+    # simulate dataset with discrete class labels in y
+    X, y = make_classification(n_samples=s, n_features=f, n_informative=i,
+                               n_redundant=r, n_clusters_per_class=c,
+                               random_state=1, shuffle=False)
+    #print(X)
+    #print(X.shape)
+    #print(sum(y) / len(y))
+
+    # perform feature selection
+    mico = MutualInformationConicOptimization(
+        method=method, verbose=verbose, k=k, categorical=True, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, n_features=n_features, max_roundings=max_rounds)
+    mico.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+    #print("Selected features: {}".format(np.where(mico.get_support())[0]))
+    #print(mico.feature_importances_)
+
+    # simulate dataset with continuous y
+    X, y = make_regression(n_samples=s, n_features=f, n_informative=i,
+                           random_state=0, shuffle=False)
+    # perform feature selection
+    mico = MutualInformationConicOptimization(
+        method=method, verbose=verbose, k=k, categorical=False, num_bins=num_bins, scale_data=scale_data,
+        n_jobs=n_jobs, n_features=n_features, max_roundings=max_rounds)
+    mico.fit(X, y)
+    # calculate precision and sensitivity
+    sens, prec = check_selection(np.where(mico.get_support())[0], i, r)
+    print('Sensitivity: ' + str(sens) + '    Precision: ' + str(prec))
+
+
+def test_colin():
+    """
+    /**
+     *  Description
+     *  -----------
+     *
+     *  Semidefinite optimization (row-wise input).
+     *
+     *  Model
+     *  -----
+     *
+     *  Minimize
+     *  obj: 2 x0 + 1 x1 + 1 x2 + 2 x3
+     *       + 3 x4 + 1 x6 + 2 x8 + 1 x10 + 3 x12
+     *  Subject To
+     *    c1 : 3 x0 + 1 x1 + 1 x2 + 3 x3 + 1 x13 = 1
+     *    c2 : 3 x4 + 1 x6 + 4 x8 + 1 x10 + 5 x12
+     *       + 1 x14 = 2
+     *  Bounds
+     *    x0 free
+     *    x1 free
+     *    x2 free
+     *    x3 free
+     *    x4 free
+     *    x5 free
+     *    x6 free
+     *    x7 free
+     *    x8 free
+     *    x9 free
+     *    x10 free
+     *    x11 free
+     *    x12 free
+     *    x13 >= 0
+     *    x14 >= 0
+     *  Semidefinites
+     *    s1: [x0, x1,
+     *         x2, x3] >= 0
+     *    s2: [x4, x5, x6,
+     *         x7, x8, x9,
+     *         x10, x11, x12] >= 0 (second semidefinite block)
+     *  End
+     */
+    """
+    # Colin
+    #from colinpy import *
+    from colinpy import ClnModel, ClnError
+
+    row1_idx = [ 0,   1,   2,   3,   13  ]
+    row1_val = [ 3.0, 1.0, 1.0, 3.0, 1.0 ]
+    row2_idx = [ 4,   6,   8,   10,  12,   14 ]
+    row2_val = [ 3.0, 1.0, 4.0, 1.0, 5.0, 1.0 ]
+    semidefinite_cone1 = [ 0, 1, 2, 3 ]
+    semidefinite_cone2 = [ 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
+
+    CLN_INFINITY = ClnModel.get_infinity()
+    model = ClnModel()
+
+    try:
+        # Step 1. Create a model and change the parameters.
+        # Create an empty model.
+        model.create_mdl()
+        # Set verbose level.
+        model.set_ipa("Model/Verbose", 3)
+
+        # Step 2. Input model.
+        # Change to minimization problem.
+        model.set_min_obj_sense()
+        # Add variables.
+        # First block.
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 2.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 1.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 1.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 2.0, [], [])
+        # Second block.
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 3.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 0.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 1.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 0.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 2.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 0.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 1.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 0.0, [], [])
+        model.add_col(-CLN_INFINITY, CLN_INFINITY, 3.0, [], [])
+        # Linear variables.
+        model.add_col(0.0, CLN_INFINITY, 0.0, [], [])
+        model.add_col(0.0, CLN_INFINITY, 0.0, [], [])
+
+        # Add constraints.
+        # Note that the nonzero elements are inputted in a row-wise order here.
+        model.add_row(1.0, 1.0, row1_idx, row1_val)
+        model.add_row(2.0, 2.0, row2_idx, row2_val)
+
+        # Add semidefinite conic constraints.
+        model.add_semi_definite_cone(semidefinite_cone1)
+        model.add_semi_definite_cone(semidefinite_cone2)
+
+        # Step 3. Solve the problem and populate the result.
+        # Solve the problem.
+        model.solve_prob()
+
+    except ClnError as e:
+        print("Received Colin exception.")
+        print(" - Explanation   : {}".format(e.message))
+        print(" - Code          : {}".format(e.code))
+    except Exception as e:
+        print("Received exception.")
+        print(" - Explanation   : {}".format(e))
+    finally:
+        # Step 4. Display the result and free the model.
+        model.display_results()
+        model.free_mdl()
+
+
+def test_iris():
+    import pandas as pd
+    from sklearn.datasets import load_breast_cancer
+
+    # Prepare data.
+    data = load_breast_cancer()
+    y = data.target
+    X = pd.DataFrame(data.data, columns=data.feature_names)
+    print(X)
+    print(y)
+    print(X.shape)
+
+    # Perform feature selection.
+    mico = MutualInformationConicOptimization(verbose=2, categorical=True, n_jobs=1)
+    mico.fit(X, y)
+
+    # Populate selected features.
+    print("Selected features: {}".format(mico.get_support()))
+
+    # Populate feature importance scores.
+    print("Feature importance scores: {}".format(mico.feature_importances_))
+
+    # Call transform() on X.
+    X_transformed = mico.transform(X)
+    print("X_transformed", X_transformed)
+
+    print(mico.get_params())
+
+if __name__ == '__main__':
+
+    # Register arguments.
+    parser = argparse.ArgumentParser(description='Process MICO.')
+    parser.add_argument('job', type=str)
+    args = parser.parse_args()
+
+    try:
+        if args.job == "mifs":
+            test_mifs()
+        elif args.job == "mibe":
+            test_mibe()
+        elif args.job == "mico":
+            test_mico()
+        elif args.job == "colin":
+            test_colin()
+        elif args.job == "iris":
+            test_iris()
+        else:
+            print("<ERR>: Unknown command [{0}].".format(args.job))
+            print("     : Available options are:")
+            print("     :  - [mifs] MI based forward selection")
+            print("     :  - [mibe] MI based backward selection")
+            print("     :  - [mico] MI based features selection using coinc optimization")
+
+    except Exception as e:
+        # Handle general exception.
+        print("<ERR>: Captured unknown error exception.")
+        print("<ERR>:  - Type    : {0}".format(type(e)))
+        print("<ERR>:  - Message : {0}".format(e))
+    finally:
+        # Done.
+        pass