Full Code of Mogeng/IOHMM for AI

master e01c5d48babe cached
178 files
1.1 MB
482.9k tokens
255 symbols
1 requests
Download .txt
Showing preview only (1,209K chars total). Download the full file or copy to clipboard to get everything.
Repository: Mogeng/IOHMM
Branch: master
Commit: e01c5d48babe
Files: 178
Total size: 1.1 MB

Directory structure:
gitextract_fm43qu58/

├── .gitignore
├── .travis.yml
├── IOHMM/
│   ├── IOHMM.py
│   ├── __init__.py
│   ├── forward_backward.py
│   └── linear_models.py
├── LICENCE
├── README.md
├── examples/
│   ├── data/
│   │   └── speed.csv
│   ├── models/
│   │   ├── SemiSupervisedIOHMM/
│   │   │   ├── config.json
│   │   │   ├── model.json
│   │   │   ├── model_emissions/
│   │   │   │   ├── state_0/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   ├── state_1/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   ├── state_2/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   └── state_3/
│   │   │   │       └── emission_0/
│   │   │   │           ├── coef.npy
│   │   │   │           ├── dispersion.npy
│   │   │   │           └── stderr.npy
│   │   │   ├── model_initial/
│   │   │   │   ├── coef.npy
│   │   │   │   └── stderr.npy
│   │   │   └── model_transition/
│   │   │       ├── state_0/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       ├── state_1/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       ├── state_2/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       └── state_3/
│   │   │           ├── coef.npy
│   │   │           └── stderr.npy
│   │   ├── SupervisedIOHMM/
│   │   │   ├── config.json
│   │   │   ├── model.json
│   │   │   ├── model_emissions/
│   │   │   │   ├── state_0/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   └── state_1/
│   │   │   │       └── emission_0/
│   │   │   │           ├── coef.npy
│   │   │   │           ├── dispersion.npy
│   │   │   │           └── stderr.npy
│   │   │   ├── model_initial/
│   │   │   │   ├── coef.npy
│   │   │   │   └── stderr.npy
│   │   │   └── model_transition/
│   │   │       ├── state_0/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       └── state_1/
│   │   │           ├── coef.npy
│   │   │           └── stderr.npy
│   │   └── UnSupervisedIOHMM/
│   │       ├── config.json
│   │       ├── model.json
│   │       ├── model_emissions/
│   │       │   ├── state_0/
│   │       │   │   ├── emission_0/
│   │       │   │   │   ├── coef.npy
│   │       │   │   │   ├── dispersion.npy
│   │       │   │   │   └── stderr.npy
│   │       │   │   └── emission_1/
│   │       │   │       ├── classes.npy
│   │       │   │       ├── coef.npy
│   │       │   │       └── stderr.npy
│   │       │   └── state_1/
│   │       │       ├── emission_0/
│   │       │       │   ├── coef.npy
│   │       │       │   ├── dispersion.npy
│   │       │       │   └── stderr.npy
│   │       │       └── emission_1/
│   │       │           ├── classes.npy
│   │       │           ├── coef.npy
│   │       │           └── stderr.npy
│   │       ├── model_initial/
│   │       │   ├── coef.npy
│   │       │   └── stderr.npy
│   │       └── model_transition/
│   │           ├── state_0/
│   │           │   ├── coef.npy
│   │           │   └── stderr.npy
│   │           └── state_1/
│   │               ├── coef.npy
│   │               └── stderr.npy
│   └── notebooks/
│       ├── SemiSupervisedIOHMM.ipynb
│       ├── SupervisedIOHMM.ipynb
│       └── UnSupervisedIOHMM.ipynb
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests/
    ├── IOHMM_models/
    │   ├── SemiSupervisedIOHMM/
    │   │   ├── model.json
    │   │   ├── model_emissions/
    │   │   │   ├── state_0/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   ├── state_1/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   ├── state_2/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   └── state_3/
    │   │   │       └── emission_0/
    │   │   │           ├── coef.npy
    │   │   │           ├── dispersion.npy
    │   │   │           └── stderr.npy
    │   │   ├── model_initial/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── model_transition/
    │   │       ├── state_0/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       ├── state_1/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       ├── state_2/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       └── state_3/
    │   │           ├── coef.npy
    │   │           └── stderr.npy
    │   ├── SupervisedIOHMM/
    │   │   ├── model.json
    │   │   ├── model_emissions/
    │   │   │   ├── state_0/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   └── state_1/
    │   │   │       └── emission_0/
    │   │   │           ├── coef.npy
    │   │   │           ├── dispersion.npy
    │   │   │           └── stderr.npy
    │   │   ├── model_initial/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── model_transition/
    │   │       ├── state_0/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       └── state_1/
    │   │           ├── coef.npy
    │   │           └── stderr.npy
    │   └── UnSupervisedIOHMM/
    │       ├── model.json
    │       ├── model_emissions/
    │       │   ├── state_0/
    │       │   │   ├── emission_0/
    │       │   │   │   ├── coef.npy
    │       │   │   │   ├── dispersion.npy
    │       │   │   │   └── stderr.npy
    │       │   │   └── emission_1/
    │       │   │       ├── classes.npy
    │       │   │       ├── coef.npy
    │       │   │       └── stderr.npy
    │       │   └── state_1/
    │       │       ├── emission_0/
    │       │       │   ├── coef.npy
    │       │       │   ├── dispersion.npy
    │       │       │   └── stderr.npy
    │       │       └── emission_1/
    │       │           ├── classes.npy
    │       │           ├── coef.npy
    │       │           └── stderr.npy
    │       ├── model_initial/
    │       │   ├── coef.npy
    │       │   └── stderr.npy
    │       └── model_transition/
    │           ├── state_0/
    │           │   ├── coef.npy
    │           │   └── stderr.npy
    │           └── state_1/
    │               ├── coef.npy
    │               └── stderr.npy
    ├── __init__.py
    ├── linear_models/
    │   ├── CrossentropyMNL/
    │   │   ├── Binary/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── Multinomial/
    │   │       ├── coef.npy
    │   │       └── stderr.npy
    │   ├── DiscreteMNL/
    │   │   ├── Binary/
    │   │   │   ├── classes.npy
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── Multinomial/
    │   │       ├── classes.npy
    │   │       ├── coef.npy
    │   │       └── stderr.npy
    │   ├── GLM/
    │   │   ├── Binomial/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── Gamma/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── Gaussian/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── InverseGaussian/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   ├── inv_gaussian.csv
    │   │   │   └── stderr.npy
    │   │   ├── NegativeBinomial/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   └── Poisson/
    │   │       ├── coef.npy
    │   │       ├── dispersion.npy
    │   │       ├── family.p
    │   │       └── stderr.npy
    │   └── OLS/
    │       ├── MultivariateOLS/
    │       │   ├── coef.npy
    │       │   ├── dispersion.npy
    │       │   └── stderr.npy
    │       └── UnivariateOLS/
    │           ├── coef.npy
    │           ├── dispersion.npy
    │           └── stderr.npy
    ├── test_CrossentropyMNL.py
    ├── test_DiscreteMNL.py
    ├── test_GLM.py
    ├── test_HMM_utils.py
    ├── test_OLS.py
    ├── test_SemiSupervisedIOHMM.py
    ├── test_SupervisedIOHMM.py
    └── test_UnSupervisedIOHMM.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Apple
.DS_Store

# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz


# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
# ipython/
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
.venv/
venv/
ENV/

v# Misc data files
*.h5
*.omx

# Auxilarly files
*.pyc

# Visual Studio
*.vscode
# used for vscode nosetest debugging
nose_stub.py
.noseids

# Emacs
*~


# metastore
metastore_db/*
examples/notebooks/metastore_db/*





================================================
FILE: .travis.yml
================================================
language: python
python:
  - "3.7"
  - "3.8"
  - "3.9"

# command to install dependencies
install:
 - pip install -r requirements.txt --upgrade
 - pip install flake8
 - pip install coveralls

# command to execute test suite
script:
 - flake8 .
 - nosetests --with-coverage --cover-package=IOHMM

# Send results of tests to coveralls
after_success:
 - coveralls


================================================
FILE: IOHMM/IOHMM.py
================================================
'''
This module implements IOHMM models:

(1) UnSupervisedIOHMM:
    Standard IOHMM with no ground truth label of hidden states.

(2) SemiSupervisedIOHMM:
    With a little ground truth labels of hidden states, use these labels to
    direct the learning process during EM.

(3) SupervisedIOHMM:
    With some ground truth labels of hidden states,
    use only ground truth labels to train. There are no iterations of EM.

The structure of the code is inspired by
depmixS4: An R Package for Hidden Markov Models:
https://cran.r-project.org/web/packages/depmixS4/vignettes/depmixS4.pdf

Features:
1. Can take a list of dataframes each representing a sequence.
2. Forward Backward algorithm fully vectorized.
3. Support json-serialization of the model so that model can be saved and loaded easily.
'''

from __future__ import division
from __future__ import absolute_import
from builtins import range
from builtins import object
from copy import deepcopy
import logging
import os
import warnings


import numpy as np


from .forward_backward import forward_backward
from .linear_models import (GLM, OLS, DiscreteMNL, CrossEntropyMNL)


warnings.simplefilter("ignore")
np.random.seed(0)
EPS = np.finfo(float).eps


class LinearModelLoader(object):
    """
    The map from data_type of a linear model
    ('GLM', 'OLS', 'DiscreteMNL', 'CrossEntropyMNL')
    to the correct class.
    """
    GLM = GLM
    OLS = OLS
    DiscreteMNL = DiscreteMNL
    CrossEntropyMNL = CrossEntropyMNL


class BaseIOHMM(object):
    """
    Base class for IOHMM models. Should not be directly called.
    Intended for subclassing.
    """

    def __init__(self, num_states=2):
        """
        Constructor
        Parameters
        ----------
        num_states: the number of hidden states
        """
        self.num_states = num_states
        self.trained = False

    def set_models(self, model_emissions,
                   model_initial=CrossEntropyMNL(),
                   model_transition=CrossEntropyMNL(), trained=False):
        """
        Set the initial probability model, transition probability models,
        and emission models.
        (1) model_initial: a linear model
        (2) model_transitions: a list of linear models, one for each hidden state.
        (3) model_emissions: a list of list of linear models,
                             the outer list is for each hidden state,
                             the inner list is for each emission model.
        Parameters
        ----------
        trained: a boolean indicating whether the models are already trained.
                 If the models are already trained, set the models directly,
                 otherwise initialize from empty linear models.
        if trained models, then the parameters are:
            model_initial: a linear model
            model_transitions: a list of linear models, one for each hidden state.
            model_emissions: a list of list of linear models,
                             the outer list is for each hidden state,
                             the inner list is for each emission model.
        otherwise:
            model_initial: the initial probability model (simply indicates its type)
            model_transition: the transition probability model (simply indicates its type)
            model_emissions: list of linear models, one for each emission.

        Notes
        -------
        Initial model and transition model must be CrossEntropyMNL models
        """
        if trained:
            self.model_initial = model_initial
            self.model_transition = model_transition
            self.model_emissions = model_emissions
            self.trained = True
        else:
            self.model_initial = model_initial
            self.model_transition = [deepcopy(model_initial) for _ in range(self.num_states)]
            self.model_emissions = [deepcopy(model_emissions) for _ in range(self.num_states)]

    def set_inputs(self, covariates_initial, covariates_transition, covariates_emissions):
        """
        Set input covariates for initial, transition and emission models
        Parameters
        ----------
        covariates_initial: list of strings,
                            indicates the field names in the dataframe
                            to use as the independent variables.
        covariates_transition: list of strings,
                               indicates the field names in the dataframe
                               to use as the independent variables.
        covariates_emissions: list of list of strings, each outer list is for one emission model
                              and each inner list of strings
                              indicates the field names in the dataframe
                              to use as the independent variables.
        """
        self.covariates_initial = covariates_initial
        self.covariates_transition = covariates_transition
        self.covariates_emissions = covariates_emissions

    def set_outputs(self, responses_emissions):
        """
        Set output covariates for emission models
        Parameters
        ----------
        responses_emissions: list of list of strings, each outer list is for one emission
                             and each inner list of strings
                             indicates the field names in the dataframe
                             to use as the dependent variables.
        Notes
        ----------
        Emission model such as Multivariate OLS, CrossEntropyMNL
        will have multiple strings (columns) in the inner list.
        """
        self.responses_emissions = responses_emissions
        self.num_emissions = len(responses_emissions)

    def set_data(self, dfs):
        """
        Set data for the model
        Parameters
        ----------
        dfs: a list of dataframes, each df represents a sequence.
        Notes
        ----------
        The column names of each df must contains the covariates and response fields
        specified above.
        """
        raise NotImplementedError

    def _initialize(self, with_randomness=True):
        """
        Initialize
        (1) log_gammas: list of arrays, the state posterior probability for each sequence
        (2) log_epsilons: list of arrays, the state posterior 'transition' probability
            (joint probability of two consecutive points) for each sequence
        based on the ground truth labels supplied.
        (3) log likelihood as negative inifinity
        (4) inp_initials: list of arrays of shape (1, len(covariates_initial)),
                          the independent variables for the initial activity of each sequence.
        (5) inp_initials_all_sequences: array of shape(len(sequences), len(covariates_initial)),
                                        the concatenation of inp_initials.
        (6) inp_transitions: list of arrays of shape (df.shape[0]-1, len(covariates_transition)),
                             the independent variables for the transition activity of each sequence.
                             (Note that the -1 is for the first activity,
                             there is no transition, it is used for initial independent variable.)
        (7) inp_transitions_all_sequences: array of shape
                                           (sum(df.shape[0]-1 for df in dfs),
                                           len(covariates_transition)),
                                           the concatenation of inp_transitions.
        (8) inp_emissions: list of list of arrays of shape
                           (df.shape[0], len(covariates_emission[i])).
                           The outer list is for each df (sequence).
                           The inner list is for each emission model.
        (9) inp_emissions_all_sequences: list of array of shape
                                         (sum(df.shape[0] for df in dfs),
                                         len(covariates_emission[i])).
                                         The list is for each emission model.
                                         This is the concatenation of all sequences
                                         for each emission model.
        (10) out_emissions: list of list of arrays of shape
                            (df.shape[0], len(response_emission[i])).
                            The outer list is for each df (sequence).
                            The inner list is for each emission model.
        (11) out_emissions_all_sequences: list of array of shape
                                          (sum(df.shape[0] for df in dfs),
                                          len(response_emission[i])).
                                          The list is for each emission model.
                                          This is the concatenation of all sequences
                                          for each emission model.


        Parameters
        ----------
        with_randomness: After initializing log_gammas and log_epsilons,
                         there might be some states that no sample is associated with it.
                         In this case, should we add some random posterior probability to it,
                         so as to start the EM iterations?

                         For UnsupervisedIOHMM and SemiSupervisedIOHMM this is set to True,
                         since we want to use EM iterations to figure out the true posterior.

                         For SupervisedIOHMM, this is set to False,
                         since we only want to use labeled data for training.
        """
        def _initialize_log_gamma(df, log_state):
            """
            Initialize posterior probability for a dataframe and the log_state provided.
            Parameters
            ----------
            df: The dataframe for a sequence, actually we only need its length.
            log_state: a dictionary (int -> array of shape (num_states, )).
                       The log_state[t] is the ground truth hidden state array of time stamp t.
                       log_state[t][k] is 0 and log_state[t][~k] is -np.Infinity
                       if the hidden state of timestamp t is k.
            Returns:
            ----------
            log_gamma: array of shape (df.shape[0], num_states).
                       The posterior probability of each timestamp.
                       log_gamma[t][k] is 0 and log_gamma[t][k] is -np.Infinity
                       if the hidden state of timestamp t is k.
                       If at time stamp t there is no ground truth,
                       log_gamma[t] will be all -np.Infinity.
            """
            log_gamma = np.log(np.zeros((df.shape[0], self.num_states)))
            for time_stamp in log_state:
                log_gamma[time_stamp, :] = log_state[time_stamp]
            return log_gamma

        def _initialize_log_epsilon(df, log_state):
            """
            Initialize posterior joint probability of two consecutive timestamp
            for a dataframe and the log_state provided.
            Parameters
            ----------
            df: The dataframe for a sequence, actually we only need its length.
            log_state: a dictionary (int -> array of shape (num_states, )).
                       The log_state[i] is the ground truth hidden state array of time stamp i.
                       log_state[i][k] is 0 and log_state[i][~k] is -np.Infinity
                       if the hidden state of timestamp i is k.
            Returns:
            ----------
            log_epsilon: array of shape (df.shape[0] - 1, num_states, num_states).
                         The posterior joint probability of two consecutive points.
                         log_epsilon[t][k][j] is 0 and log_epsilon[t][~k][~j] is -np.Infinity
                         if the hidden state of timestamp t is k and
                         hidden state of timestamp t+1 is j.
                         If at time stamp t or t+1 there is no ground truth,
                         log_epsilon[t] will be all -np.Infinity.

            """
            log_epsilon = np.log(np.zeros((df.shape[0] - 1, self.num_states, self.num_states)))
            for time_stamp in log_state:
                if time_stamp + 1 in log_state:
                    # actually should find the index of 1
                    st = int(np.argmax(log_state[time_stamp]))
                    log_epsilon[time_stamp, st, :] = log_state[time_stamp + 1]
            return log_epsilon

        # initialize log_gammas
        self.log_gammas = [_initialize_log_gamma(df, log_state)
                           for df, log_state in self.dfs_logStates]
        # initialize log_epsilons
        self.log_epsilons = [_initialize_log_epsilon(df, log_state)
                             for df, log_state in self.dfs_logStates]
        if with_randomness:
            for st in range(self.num_states):
                if np.exp(np.hstack([lg[:, st] for lg in self.log_gammas])).sum() < EPS:
                    # there is no any sample associated with this state
                    for lg in self.log_gammas:
                        lg[:, st] = np.random.rand(lg.shape[0])
            for st in range(self.num_states):
                if np.exp(np.vstack([le[:, st, :] for le in self.log_epsilons])).sum() < EPS:
                    # there is no any sample associated with this state
                    for le in self.log_epsilons:
                        le[:, st, :] = np.random.rand(le.shape[0], self.num_states)

        # initialize log_likelihood
        self.log_likelihoods = [-np.Infinity for _ in range(self.num_seqs)]
        self.log_likelihood = -np.Infinity

        # initialize input/output covariates
        self.inp_initials = [np.array(df[self.covariates_initial].iloc[0]).reshape(
            1, -1).astype('float64') for df, log_state in self.dfs_logStates]
        self.inp_initials_all_sequences = np.vstack(self.inp_initials)

        self.inp_transitions = [np.array(df[self.covariates_transition].iloc[1:]).astype(
            'float64') for df, log_state in self.dfs_logStates]
        self.inp_transitions_all_sequences = np.vstack(self.inp_transitions)

        self.inp_emissions = [[np.array(df[cov]).astype('float64') for
                               cov in self.covariates_emissions]
                              for df, log_state in self.dfs_logStates]
        self.inp_emissions_all_sequences = [np.vstack([seq[emis] for
                                                       seq in self.inp_emissions]) for
                                            emis in range(self.num_emissions)]
        self.out_emissions = [[np.array(df[res]) for
                               res in self.responses_emissions]
                              for df, log_state in self.dfs_logStates]

        self.out_emissions_all_sequences = [np.vstack([seq[emis] for
                                                       seq in self.out_emissions]) for
                                            emis in range(self.num_emissions)]

    def E_step(self):
        """
        The Expectation step, Update
        (1) log_gammas: list of arrays, state posterior probability for each sequence
        (2) log_epsilons: list of arrays, state posterior 'transition' probability
            (joint probability of two consecutive points) for each sequence
        (3) log likelihood
        based on the model coefficients from last iteration,
        with respect to the ground truth hidden states if any.
        """
        self.log_gammas = []
        self.log_epsilons = []
        self.log_likelihoods = []
        for seq in range(self.num_seqs):
            n_records = self.dfs_logStates[seq][0].shape[0]
            # initial probability
            log_prob_initial = self.model_initial.predict_log_proba(
                self.inp_initials[seq]).reshape(self.num_states,)
            # transition probability
            log_prob_transition = np.zeros((n_records - 1, self.num_states, self.num_states))
            for st in range(self.num_states):
                log_prob_transition[:, st, :] = self.model_transition[st].predict_log_proba(
                    self.inp_transitions[seq])
            assert log_prob_transition.shape == (n_records - 1, self.num_states, self.num_states)
            # emission probability
            log_Ey = np.zeros((n_records, self.num_states))
            for emis in range(self.num_emissions):
                model_collection = [models[emis] for models in self.model_emissions]
                log_Ey += np.vstack([model.loglike_per_sample(
                    np.array(self.inp_emissions[seq][emis]).astype('float64'),
                    np.array(self.out_emissions[seq][emis])) for model in model_collection]).T
            # forward backward to calculate posterior
            log_gamma, log_epsilon, log_likelihood = forward_backward(
                log_prob_initial, log_prob_transition, log_Ey, self.dfs_logStates[seq][1])
            self.log_gammas.append(log_gamma)
            self.log_epsilons.append(log_epsilon)
            self.log_likelihoods.append(log_likelihood)
        self.log_likelihood = sum(self.log_likelihoods)

    def M_step(self):
        """
        The Maximization step, Update
        (1) model_initial: a linear model
        (2) model_transitions: a list of linear models, one for each hidden state.
        (3) model_emissions: a list of list of linear models,
                             the outer list is for each hidden state,
                             the inner list is for each emission model.
        based on the posteriors, and dependent/independent covariates.
        Notes:
        ----------
        In the emission models, if the sum of sample weight is zero,
        the linear model will raise ValueError.
        """

        # optimize initial model
        X = self.inp_initials_all_sequences
        Y = np.exp(np.vstack([lg[0, :].reshape(1, -1) for lg in self.log_gammas]))
        self.model_initial.fit(X, Y)

        # optimize transition models
        X = self.inp_transitions_all_sequences
        for st in range(self.num_states):
            Y = np.exp(np.vstack([eps[:, st, :] for eps in self.log_epsilons]))
            self.model_transition[st].fit(X, Y)

        # optimize emission models
        for emis in range(self.num_emissions):
            X = self.inp_emissions_all_sequences[emis]
            Y = self.out_emissions_all_sequences[emis]
            for st in range(self.num_states):
                sample_weight = np.exp(np.hstack([lg[:, st] for lg in self.log_gammas]))
                self.model_emissions[st][emis].fit(X, Y, sample_weight=sample_weight)

    def train(self):
        """
        The ieratioin of EM step,
        Notes:
        ----------
        For SupervisedIOHMM, max_EM_iter is 1, thus will only go through one iteration of EM step,
        which means that it will only use the ground truth hidden states to train.
        """
        for it in range(self.max_EM_iter):
            log_likelihood_prev = self.log_likelihood
            self.M_step()
            self.E_step()
            logging.info('log likelihood of iteration {0}: {1:.4f}'.format(it, self.log_likelihood))
            if abs(self.log_likelihood - log_likelihood_prev) < self.EM_tol:
                break
        self.trained = True

    def to_json(self, path):
        """
        Generate json object of the IOHMM model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the model
        """
        json_dict = {
            'data_type': self.__class__.__name__,
            'properties': {
                'num_states': self.num_states,
                'covariates_initial': self.covariates_initial,
                'covariates_transition': self.covariates_transition,
                'covariates_emissions': self.covariates_emissions,
                'responses_emissions': self.responses_emissions,
                'model_initial': self.model_initial.to_json(
                    path=os.path.join(path, 'model_initial')),
                'model_transition': [self.model_transition[st].to_json(
                    path=os.path.join(path, 'model_transition', 'state_{}'.format(st))) for
                    st in range(self.num_states)],
                'model_emissions': [[self.model_emissions[st][emis].to_json(
                    path=os.path.join(
                        path, 'model_emissions', 'state_{}'.format(st), 'emission_{}'.format(emis))
                ) for emis in range(self.num_emissions)] for st in range(self.num_states)]
            }
        }
        return json_dict

    @classmethod
    def _from_setup(
            cls, json_dict, num_states,
            model_initial, model_transition, model_emissions,
            covariates_initial, covariates_transition, covariates_emissions,
            responses_emissions, trained):
        """
        Helper function to construct the IOHMM model used by from_json and from_config.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        num_states: number of hidden states
        trained: a boolean indicating whether the models are already trained.
                 If the models are already trained, set the models directly,
                 otherwise initialize from empty linear models.
        if trained models, then the parameters are:
            model_initial: a linear model
            model_transitions: a list of linear models, one for each hidden state.
            model_emissions: a list of list of linear models,
                             the outer list is for each hidden state,
                             the inner list is for each emission model.
        otherwise:
            model_initial: the initial probability model (simply indicates its type)
            model_transition: the transition probability model (simply indicates its type)
            model_emissions: list of linear models, one for each emission.
        covariates_initial: list of strings,
                            each indicates the field name in the dataframe
                            to use as the independent variables.
        covariates_transition: list of strings,
                               each indicates the field name in the dataframe
                               to use as the independent variables.
        covariates_emissions: list of list of strings, each outer list is for one emission model
                              and each inner list of strings
                              indicates the field names in the dataframe
                              to use as the independent variables.
        responses_emissions: list of list of strings, each outer list is for one emission
                             and each inner list of strings
                             indicates the field names in the dataframe
                             to use as the dependent variables.
        Returns
        -------
        IOHMM object: an IOHMM object specified by the json_dict and other arguments
        """
        model = cls(num_states=num_states)
        model.set_models(
            model_initial=model_initial,
            model_transition=model_transition,
            model_emissions=model_emissions,
            trained=trained)
        model.set_inputs(covariates_initial=covariates_initial,
                         covariates_transition=covariates_transition,
                         covariates_emissions=covariates_emissions)
        model.set_outputs(responses_emissions=responses_emissions)
        return model

    @classmethod
    def from_config(cls, json_dict):
        """
        Construct an IOHMM object from a json dictionary which specifies the structure of the model.
        Parameters
        ----------
        json_dict: a json dictionary containing the config/structure of the IOHMM.
        Returns
        -------
        IOHMM: an IOHMM object specified by the json_dict
        """
        return cls._from_setup(
            json_dict,
            num_states=json_dict['properties']['num_states'],
            model_initial=getattr(
                LinearModelLoader, json_dict['properties']['model_initial']['data_type'])(
                    **json_dict['properties']['model_initial']['properties']),
            model_transition=getattr(
                LinearModelLoader, json_dict['properties']['model_transition']['data_type'])(
                    **json_dict['properties']['model_transition']['properties']),
            model_emissions=[getattr(
                LinearModelLoader, model_emission['data_type'])(**model_emission['properties'])
                for model_emission in json_dict['properties']['model_emissions']],
            covariates_initial=json_dict['properties']['covariates_initial'],
            covariates_transition=json_dict['properties']['covariates_transition'],
            covariates_emissions=json_dict['properties']['covariates_emissions'],
            responses_emissions=json_dict['properties']['responses_emissions'],
            trained=False)

    @classmethod
    def from_json(cls, json_dict):
        """
        Construct an IOHMM object from a saved json dictionary.
        Parameters
        ----------
        json_dict: a json dictionary containing the attributes of the IOHMM.
        Returns
        -------
        IOHMM: an IOHMM object specified by the json_dict
        """
        return cls._from_setup(
            json_dict,
            num_states=json_dict['properties']['num_states'],
            model_initial=getattr(
                LinearModelLoader, json_dict['properties']['model_initial']['data_type']).from_json(
                json_dict['properties']['model_initial']),
            model_transition=[getattr(
                LinearModelLoader, model_transition_json['data_type']
            ).from_json(model_transition_json) for
                model_transition_json in json_dict['properties']['model_transition']],
            model_emissions=[[getattr(
                LinearModelLoader, model_emission_json['data_type']
            ).from_json(model_emission_json) for model_emission_json in model_emissions_json] for
                model_emissions_json in json_dict['properties']['model_emissions']],
            covariates_initial=json_dict['properties']['covariates_initial'],
            covariates_transition=json_dict['properties']['covariates_transition'],
            covariates_emissions=json_dict['properties']['covariates_emissions'],
            responses_emissions=json_dict['properties']['responses_emissions'],
            trained=True)


class UnSupervisedIOHMM(BaseIOHMM):
    """
    Unsupervised IOHMM models.
    This model is intended to be used when no ground truth hidden states are available.
    """

    def __init__(self, num_states=2, EM_tol=1e-4, max_EM_iter=100):
        """
        Constructor
        Parameters
        ----------
        num_states: the number of hidden states
        EM_tol: the tolerance of the EM iteration convergence
        max_EM_iter: the maximum number of EM iterations
        -------
        """
        super(UnSupervisedIOHMM, self).__init__(num_states=num_states)
        self.EM_tol = EM_tol
        self.max_EM_iter = max_EM_iter

    def set_data(self, dfs):
        """
        Set data for the model
        Constructs:
        ----------
        (1) num_seqs: number of seqences
        (2) dfs_logStates: list of (dataframe, log_state)
        (3) posteriors with randomness and input/output covariates
        Parameters
        ----------
        dfs: a list of dataframes, each df represents a sequence.
        Notes
        ----------
        The column names of each df must contains the covariates and response fields
        specified above.

        Since there are no ground truth hidden states, all log_state should be empty {}.
        """
        assert all([df.shape[0] > 0 for df in dfs])
        self.num_seqs = len(dfs)
        self.dfs_logStates = [[x, {}] for x in dfs]
        self._initialize(with_randomness=True)

    def to_json(self, path):
        """
        Generate json object of the UnSupervisedIOHMM/SemiSupervisedIOHMM model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the model
        """
        json_dict = super(UnSupervisedIOHMM, self).to_json(path)
        json_dict['properties'].update(
            {
                'EM_tol': self.EM_tol,
                'max_EM_iter': self.max_EM_iter,
            }
        )
        return json_dict

    @classmethod
    def _from_setup(
            cls, json_dict, num_states,
            model_initial, model_transition, model_emissions,
            covariates_initial, covariates_transition, covariates_emissions,
            responses_emissions, trained):
        """
        Helper function to construct the UnSupervisedIOHMM/SemiSupervisedIOHMM model
        used by from_json and from_config.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        num_states: number of hidden states
        trained: a boolean indicating whether the models are already trained.
                 If the models are already trained, set the models directly,
                 otherwise initialize from empty linear models.
        if trained models, then the parameters are:
            model_initial: a linear model
            model_transitions: a list of linear models, one for each hidden state.
            model_emissions: a list of list of linear models,
                             the outer list is for each hidden state,
                             the inner list is for each emission model.
        otherwise:
            model_initial: the initial probability model (simply indicates its type)
            model_transition: the transition probability model (simply indicates its type)
            model_emissions: list of linear models, one for each emission.
        covariates_initial: list of strings,
                            each indicates the field name in the dataframe
                            to use as the independent variables.
        covariates_transition: list of strings,
                               each indicates the field name in the dataframe
                               to use as the independent variables.
        covariates_emissions: list of list of strings, each outer list is for one emission model
                              and each inner list of strings
                              indicates the field names in the dataframe
                              to use as the independent variables.
        responses_emissions: list of list of strings, each outer list is for one emission
                             and each inner list of strings
                             indicates the field names in the dataframe
                             to use as the dependent variables.
        Returns
        -------
        IOHMM object: an IOHMM object specified by the json_dict and other arguments
        """
        model = cls(num_states=num_states,
                    EM_tol=json_dict['properties']['EM_tol'],
                    max_EM_iter=json_dict['properties']['max_EM_iter'])
        model.set_models(
            model_initial=model_initial,
            model_transition=model_transition,
            model_emissions=model_emissions,
            trained=trained)
        model.set_inputs(covariates_initial=covariates_initial,
                         covariates_transition=covariates_transition,
                         covariates_emissions=covariates_emissions)
        model.set_outputs(responses_emissions=responses_emissions)
        return model


class SemiSupervisedIOHMM(UnSupervisedIOHMM):
    """
    SemiSupervised IOHMM models.
    This model is intended to be used when there are some ground truth hidden states,
    but the user don't want to solely use these labeled data to train.
    """

    def set_data(self, dfs_states):
        """
        Set data for the model
        Constructs:
        ----------
        (1) num_seqs: number of seqences
        (2) dfs_logStates: list of (dataframe, log_state)
        (3) posteriors with randomness and input/output covariates
        Parameters
        ----------
        dfs_states: a list of (dataframes, states), each dataframe represents a sequence.
                    and states is a dictionary of (timestamp -> array of shape (num_states, ))
                    states[t][k] is 1 and states[t][~k] is 0 if the hidden state is k at
                    timestamp t.
        Notes
        ----------
        The column names of each df must contain the covariates and response fields
        specified above.
        """
        self.num_seqs = len(dfs_states)
        self.dfs_logStates = [[x[0], {k: np.log(x[1][k]) for k in x[1]}] for x in dfs_states]
        self._initialize(with_randomness=True)


class SupervisedIOHMM(BaseIOHMM):
    """
    SemiSupervised IOHMM models.
    This model is intended to be used when the user
    simply want to use ground truth hidden states to train the model
    """

    def __init__(self, num_states=2):
        """
        Constructor
        Parameters
        ----------
        num_states: the number of hidden states
        -------
        """
        super(SupervisedIOHMM, self).__init__(num_states=num_states)
        self.max_EM_iter = 1
        self.EM_tol = 0

    def set_data(self, dfs_states):
        """
        Set data for the model
        Constructs:
        ----------
        (1) num_seqs: number of seqences
        (2) dfs_logStates: list of (dataframe, log_state)
        (3) posteriors withOUT randomness and input/output covariates
        Parameters
        ----------
        dfs_states: a list of (dataframes, states), each dataframe represents a sequence.
                    and states if a dictionary of (timestamp -> array of shape (num_states, ))
                    states[t][k] is 1 and states[t][~k] is 0 if the hidden state is k at
                    timestamp t.
        Notes
        ----------
        The column names of each df must contains the covariates and response fields
        specified above.
        """
        self.num_seqs = len(dfs_states)
        self.dfs_logStates = [[x[0], {k: np.log(x[1][k]) for k in x[1]}] for x in dfs_states]
        self._initialize(with_randomness=False)


================================================
FILE: IOHMM/__init__.py
================================================
from .IOHMM import (UnSupervisedIOHMM,
                    SemiSupervisedIOHMM,
                    SupervisedIOHMM)
from .forward_backward import (forward_backward,
                               forward,
                               backward,
                               cal_log_gamma,
                               cal_log_epsilon,
                               cal_log_likelihood)
from .linear_models import (GLM,
                            OLS,
                            DiscreteMNL,
                            CrossEntropyMNL)

__all__ = [
    UnSupervisedIOHMM, SemiSupervisedIOHMM, SupervisedIOHMM,
    forward_backward, forward, backward,
    cal_log_gamma, cal_log_epsilon,
    cal_log_likelihood,
    GLM, OLS, DiscreteMNL, CrossEntropyMNL
]


================================================
FILE: IOHMM/forward_backward.py
================================================
'''
The forward backward algorithm of hidden markov model (HMM) .
Mainly used in the E-step of IOHMM given the
(1) initial probabilities, (2) transition probabilities, and (3) emission probabilities.

A feature of this implementation is that it is vectorized to the greatest extent
that we use numpy matrix operation as much as possible.
We have only one for loop in forward/backward calculation,
which is necessary due to dynamic programming (DP).

Another feature of this implementation is that it is calculated at the log level,
so that it is more robust to long sequences.
'''
from __future__ import division

from builtins import range
import warnings


import numpy as np
from scipy.special import logsumexp

warnings.simplefilter("ignore")


def forward_backward(log_prob_initial, log_prob_transition, log_Ey, log_state={}):
    """
    The forward_backward algorithm.
    Parameters
    ----------
    log_prob_initial : array-like of shape (k, )
        where k is the number of states of the HMM
        The log of the probability of initial state at timestamp 0.
        log_prob_initial_{i} is the log of the probability of being in state i
        at timestamp 0.
    log_prob_transition : array-like of shape (t-1, k, k)
        where t is the number of timestamps (length) of the sequence.
        log_prob_transition_{t, i, j} is the log of the probability of transferring
        to state j from state i at timestamp t.
    log_Ey : array-like of shape (t, k)
        log_Ey_{t, i} is the log of the probability of observing emission variables
        from state i at timestamp t.
    log_state: dict(int -> array-like of shape (k, ))
        timestamp i is a key of log_state if we know the state of that timestamp.
        Mostly used in semi-supervised and supervised IOHMM.
        log_state[t][i] is 0 and log_state[t][~i] is -np.Infinity
        if we know the state is i at timestamp t.
    Returns
    -------
    (1) posterior state log probability of each timestamp.
    (2) posterior "transition" log probability of each timestamp.
    (3) log likelihood of the sequence.
    see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    log_alpha = forward(log_prob_initial, log_prob_transition, log_Ey, log_state)
    log_beta = backward(log_prob_transition, log_Ey, log_state)
    log_likelihood = cal_log_likelihood(log_alpha)
    log_gamma = cal_log_gamma(log_alpha, log_beta, log_likelihood, log_state)
    log_epsilon = cal_log_epsilon(log_prob_transition, log_Ey, log_alpha,
                                  log_beta, log_likelihood, log_state)
    return log_gamma, log_epsilon, log_likelihood


def forward(log_prob_initial, log_prob_transition, log_Ey, log_state={}):
    """
    The forward function to calculate log of forward variable alpha.
    Parameters
    ----------
    log_prob_initial : array-like of shape (k, )
        where k is the number of states of the HMM
        The log of the probability of initial state at timestamp 0.
        log_prob_initial_{i} is the log of the probability of being in state i
        at timestamp 0.
    log_prob_transition : array-like of shape (t-1, k, k)
        where t is the number of timestamps (length) of the sequence.
        log_prob_transition_{t, i, j} is the log of the probability of transferring
        to state j from state i at timestamp t.
    log_Ey : array-like of shape (t, k)
        log_Ey_{t, i} is the log of the probability of observing emission variables
        from state i at timestamp t.
    log_state: dict(int -> array-like of shape (k, ))
        timestamp i is a key of log_state if we know the state of that timestamp.
        Mostly used in semi-supervised and supervised IOHMM.
        log_state[t][i] is 0 and log_state[t][~i] is -np.Infinity
        if we know the state is i at timestamp t.
    Returns
    -------
    log_alpha : array-like of shape (t, k)
        log of forward variable alpha.
        see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    assert log_prob_initial.ndim == 1
    assert log_prob_transition.ndim == 3
    assert log_Ey.ndim == 2
    t = log_Ey.shape[0]
    k = log_Ey.shape[1]
    log_alpha = np.zeros((t, k))
    if 0 in log_state:
        log_alpha[0, :] = log_state[0] + log_Ey[0, :]
    else:
        log_alpha[0, :] = log_prob_initial + log_Ey[0, :]
    for i in range(1, t):
        if i in log_state:
            log_alpha[i, :] = logsumexp(log_alpha[i - 1, :]) + log_state[i] + log_Ey[i, :]
        else:
            log_alpha[i, :] = logsumexp(log_prob_transition[i - 1, :, :].T +
                                        log_alpha[i - 1, :], axis=1) + log_Ey[i, :]
    assert log_alpha.shape == (t, k)
    return log_alpha


def backward(log_prob_transition, log_Ey, log_state={}):
    """
    The function to calculate log of backward variable beta.
    Parameters
    ----------
    log_prob_transition : array-like of shape (t-1, k, k)
        where t is the number of timestamps (length) of the sequence.
        log_prob_transition_{t, i, j} is the log of the probability of transferring
        to state j from state i at timestamp t.
    log_Ey : array-like of shape (t, k)
        log_Ey_{t, i} is the log of the probability of observing emission variables
        from state i at timestamp t.
    log_state: dict(int -> array-like of shape (k, ))
        timestamp i is a key of log_state if we know the state of that timestamp.
        Mostly used in semi-supervised and supervised IOHMM.
        log_state[t][i] is 0 and log_state[t][~i] is -np.Infinity
        if we know the state is i at timestamp t.
    Returns
    -------
    log_beta : array-like of shape (t, k)
        log of backward variable beta.
        see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    assert log_prob_transition.ndim == 3
    assert log_Ey.ndim == 2
    t = log_Ey.shape[0]
    k = log_Ey.shape[1]
    log_beta = np.zeros((t, k))
    for i in range(t - 2, -1, -1):
        if i + 1 in log_state:
            log_beta[i, :] = logsumexp(log_state[i + 1] + log_beta[i + 1, :] + log_Ey[i + 1, :])
        else:
            log_beta[i, :] = logsumexp(log_prob_transition[i, :, :] +
                                       (log_beta[i + 1, :] + log_Ey[i + 1, :]), axis=1)
    assert log_beta.shape == (t, k)
    return log_beta


def cal_log_likelihood(log_alpha):
    """
    The function to calculate the log likelihood of the sequence.
    Parameters
    ----------
    log_alpha : array-like of shape (t, k)
        log of forward variable alpha.
    Returns
    -------
    log_likelihood : float
        The log likelihood of the sequence.
        see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    return logsumexp(log_alpha[-1, :])


def cal_log_gamma(log_alpha, log_beta, log_likelihood, log_state={}):
    """
    The function to calculate the log of the posterior probability of each state
    at each timestamp.
    Parameters
    ----------
    log_alpha : array-like of shape (t, k)
        log of forward variable alpha.
    log_alpha : array-like of shape (t, k)
        log of backward variable beta.
    log_likelihood : float
        log likelihood of the sequence
    log_state: dict(int -> array-like of shape (k, ))
        timestamp i is a key of log_state if we know the state of that timestamp.
        Mostly used in semi-supervised and supervised IOHMM.
        log_state[t][i] is 0 and log_state[t][~i] is -np.Infinity
        if we know the state is i at timestamp t.
    Returns
    -------
    log_gamma : array-like of shape (t, k)
        the log of the posterior probability of each state.
        log_gamma_{t, i} is the posterior log of the probability of
        being in state i at stimestamp t.
        see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    log_gamma = log_alpha + log_beta - log_likelihood
    for i in log_state:
        log_gamma[i, :] = log_state[i]
    return log_gamma


def cal_log_epsilon(log_prob_transition, log_Ey, log_alpha, log_beta, log_likelihood, log_state={}):
    """
    The function to calculate the log of the posterior joint probability
    of two consecutive timestamps
    Parameters
    ----------
    log_prob_transition : array-like of shape (t-1, k, k)
        where t is the number of timestamps (length) of the sequence.
        log_prob_transition_{t, i, j} is the log of the probability of transferring
        to state j from state i at timestamp t.
    log_Ey : array-like of shape (t, k)
        log_Ey_{t, i} is the log of the probability of observing emission variables
        from state i at timestamp t.
    log_alpha : array-like of shape (t, k)
        log of forward variable alpha.
    log_alpha : array-like of shape (t, k)
        log of backward variable beta.
    log_likelihood : float
        log likelihood of the sequence
    log_state: dict(int -> array-like of shape (k, ))
        timestamp i is a key of log_state if we know the state of that timestamp.
        Mostly used in semi-supervised and supervised IOHMM.
        log_state[t][i] is 0 and log_state[t][~i] is -np.Infinity
        if we know the state is i at timestamp t.
    Returns
    -------
    log_epsilon : array-like of shape (t-1, k, k)
        the log of the posterior probability of two consecutive timestamps.
        log_gamma_{t, i, j} is the posterior log of the probability of
        being in state i at timestamp t and
        being in state j at timestamp t+1.
        see https://en.wikipedia.org/wiki/Forward-backward_algorithm for details.
    """
    k = log_Ey.shape[1]
    if log_prob_transition.shape[0] == 0:
        return np.zeros((0, k, k))
    else:
        log_p = log_prob_transition
        for i in log_state:
            log_p[i - 1, :, :] = log_state[i]
        log_epsilon = np.tile((log_Ey + log_beta)[1:, np.newaxis, :], [1, k, 1]) + \
            np.tile(log_alpha[:-1, :, np.newaxis], [1, 1, k]) + log_p - log_likelihood
        for i in log_state:
            if i + 1 in log_state:
                log_epsilon[i, :, :] = np.add.outer(log_state[i], log_state[i + 1])
        return log_epsilon


================================================
FILE: IOHMM/linear_models.py
================================================
'''
This is a unified interface/wrapper of general/generalized linear models from
sklearn/statsmodels packages.

Problems with sklearn:
1. No Generalized linear models available.
2. Does not estimate standard error of coefficients.
3. Logistic regression does not handle 1 class case.
4. For 2 class logistic regression, the 'ovr' result is not same as 'multinomial' result.

Problems with statsmodels:
1. No working version of multivariate OLS with sample weights.
2. MNLogit does not support sample weights.

Problem with both:
1. No interface to calculate loglike_per_sample,
   which is need to calculate emission probability in IOHMM.
2. No json-serialization.


In this implementations,
we will mainly use statsmodels for
1. Generalized linear models with simple response

we will mainly use sklearn for
1. Univariate/Multivariate Ordinary least square (OLS) models,
2. Multinomial Logistic Regression with discrete output/probability outputs

Note:
1. If using customized arguments for constructor, you may encounter compalints
   from the statsmodels/sklearn on imcompatible arguments.
   This maybe especially true for the compatibility between solver and regularization method.

2. For the GLM, statsmodels is not great when fitting with regularizations
   (espicially l1, and elstic_net). In this case the coefficients might be np.nan.
   Try not using regularizations if you select GLM until statsmodels is stable on this.
'''

# //TODO in future add arguments compatibility check

from __future__ import division

from future import standard_library

from builtins import range
from builtins import object
import pickle as pickle
import logging
import numbers
import os


import numpy as np
from scipy.stats import multivariate_normal
from sklearn import linear_model
from sklearn.linear_model._base import _rescale_data
from sklearn.preprocessing import label_binarize
import statsmodels.api as sm
from statsmodels.genmod.families import Poisson, Binomial
from statsmodels.tools import add_constant
standard_library.install_aliases()
EPS = np.finfo(float).eps


class BaseModel(object):
    """
    A generic supervised model for data with input and output.
    BaseModel does nothing, but lays out the methods expected of any subclass.
    """

    def __init__(self,
                 solver,
                 fit_intercept=True,
                 est_stderr=False,
                 tol=1e-4,
                 max_iter=100,
                 reg_method=None,
                 alpha=0,
                 l1_ratio=0,
                 coef=None,
                 stderr=None):
        """
        Constructor
        Parameters
        ----------
        solver: specific solver for each linear model
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, l1, l2, elstic_net).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model
        -------
        """
        self.solver = solver
        self.fit_intercept = fit_intercept
        self.est_stderr = est_stderr
        self.tol = tol
        self.max_iter = max_iter
        self.reg_method = reg_method
        self.alpha = alpha
        self.l1_ratio = l1_ratio
        self.coef = coef
        self.stderr = stderr

    def fit(self, X, Y, sample_weight=None):
        """
        Fit the weighted model
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : observed response matrix of shape
            (n_samples, ) or (n_samples, k) based on specific model
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        """
        raise NotImplementedError

    def _raise_error_if_model_not_trained(self):
        """
        Raise error if the model is not trained (thus has coef)
        ----------
        """
        if self.coef is None:
            raise ValueError('Model is not trained.')

    def _raise_error_if_sample_weight_sum_zero(self, sample_weight):
        """
        Raise error if the sum of sample_weight is 0
        ----------
        sample_weight: array of (n_samples, )
        """
        if np.sum(sample_weight) < EPS:
            raise ValueError('Sum of sample weight is 0.')

    def _transform_X(self, X):
        """
        Transform the design matrix X
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        X : design matrix of shape (n_samples, n_features + 1) if fit intercept
        """
        if self.fit_intercept:
            X = add_constant(X, has_constant='add')
        return X

    def _transform_sample_weight(self, X, sample_weight=None):
        """
        Transform the sample weight from anyform to array
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        Returns
        -------
        sample_weight: array of (n_samples, )
        """
        if sample_weight is None:
            sample_weight = np.ones(X.shape[0])
        elif isinstance(sample_weight, numbers.Number):
            sample_weight = np.ones(X.shape[0]) * sample_weight
        assert X.shape[0] == sample_weight.shape[0]
        return sample_weight

    def _transform_X_sample_weight(self, X, sample_weight=None):
        """
        Transform the design matrix X and sample_weight to the form they can be used to fit
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        Returns
        -------
        X : design matrix of shape (n_samples, n_features + 1) if fit intercept
        sample_weight: array of (n_samples, )
        """
        X = self._transform_X(X)
        sample_weight = self._transform_sample_weight(X, sample_weight=sample_weight)
        return X, sample_weight

    def predict(self, X):
        """
        Predict the Y value based on the model
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        predicted value: of shape (n_samples, ) or (n_samples, k) based on specific model
        """
        raise NotImplementedError

    def loglike_per_sample(self, X, Y):
        """
        Given a set of X and Y, calculate the log probability of
        observing each of Y_i value given each X_i value
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : observed response matrix of shape
            (n_samples, ) or (n_samples, k) based on specific model
        Returns
        -------
        log_p: array of shape (n_samples, )
        """
        raise NotImplementedError

    def loglike(self, X, Y, sample_weight=None):
        """
        Given a set of X and Y, calculate the log probability of
        observing Y, considering the sample weight.
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : observed response matrix of shape
            (n_samples, ) or (n_samples, k) based on specific model
        Returns
        -------
        log_likelihood: float
        """
        self._raise_error_if_model_not_trained()
        sample_weight = self._transform_sample_weight(X, sample_weight=sample_weight)
        return np.sum(sample_weight * self.loglike_per_sample(X, Y))

    def to_json(self, path):
        """
        Generate json object of the model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the model
        """
        json_dict = {
            'data_type': self.__class__.__name__,
            'properties': {
                'solver': self.solver,
                'fit_intercept': self.fit_intercept,
                'est_stderr': self.est_stderr,
                'tol': self.tol,
                'max_iter': self.max_iter,
                'reg_method': self.reg_method,
                'alpha': self.alpha,
                'l1_ratio': self.l1_ratio,
                'coef': {
                    'data_type': 'numpy.ndarray',
                    'path': os.path.join(path, 'coef.npy')
                },
                'stderr': {
                    'data_type': 'numpy.ndarray',
                    'path': os.path.join(path, 'stderr.npy')
                }
            }
        }
        if not os.path.exists(os.path.dirname(json_dict['properties']['coef']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['coef']['path']))
        np.save(json_dict['properties']['coef']['path'], self.coef)
        if not os.path.exists(os.path.dirname(json_dict['properties']['stderr']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['stderr']['path']))
        np.save(json_dict['properties']['stderr']['path'], self.stderr)
        return json_dict

    @classmethod
    def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
                   tol, max_iter, reg_method, alpha, l1_ratio, coef, stderr):
        """
        Helper function to construct the linear model used by from_json.
        This function is designed to be override by subclasses.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for each linear model
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, l1, l2, elstic_net).
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        linear model object: a linear model object specified by the json_dict and other arguments
        """
        return cls(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            tol=tol, max_iter=max_iter,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr)

    @classmethod
    def from_json(cls, json_dict):
        """
        Construct a linear model from a saved dictionary.
        This function is NOT designed to be override by subclasses.
        Parameters
        ----------
        json_dict: a json dictionary containing the attributes of the linear model.
        Returns
        -------
        linear model: a linear model object specified by the json_dict
        """
        return cls._from_json(
            json_dict,
            solver=json_dict['properties']['solver'],
            fit_intercept=json_dict['properties']['fit_intercept'],
            est_stderr=json_dict['properties']['est_stderr'],
            tol=json_dict['properties']['tol'],
            max_iter=json_dict['properties']['max_iter'],
            reg_method=json_dict['properties']['reg_method'],
            alpha=json_dict['properties']['alpha'],
            l1_ratio=json_dict['properties']['l1_ratio'],
            coef=np.load(json_dict['properties']['coef']['path'], allow_pickle=True),
            stderr=np.load(json_dict['properties']['stderr']['path'], allow_pickle=True))


class GLM(BaseModel):
    """
    A wrapper for Generalized linear models.
    fit_regularized only support Poisson and Binomial due to statsmodels,
    and it is not stable. Try not using regularizations in GLM.
    """

    def __init__(self,
                 family,
                 solver='IRLS',
                 fit_intercept=True,
                 est_stderr=False,
                 tol=1e-4,
                 max_iter=100,
                 reg_method=None,
                 alpha=0,
                 l1_ratio=0,
                 coef=None,
                 stderr=None,
                 dispersion=None):
        """
        Constructor
        Parameters
        ----------
        solver: solver for GLM, default 'IRLS', otherwise will use gradient.
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: TRY NOT USING REGULARIZATIONS FOR GLM.
                    method to regularize the model, one of (None, elstic_net).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model

        family: statsmodels.genmod.families.family.Family
        dispersion: dispersion/scale of the GLM
        -------
        """
        super(GLM, self).__init__(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            tol=tol, max_iter=max_iter,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr)
        self.family = family
        self.dispersion = dispersion
        if self.coef is not None:
            dummy_X = dummy_Y = dummy_weight = np.zeros(1)
            self._model = sm.GLM(dummy_Y, dummy_X, family=family,
                                 freq_weights=dummy_weight)

    def fit(self, X, Y, sample_weight=None):
        """
        Fit the weighted model
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) or (n_samples, k) depending on family
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        """
        def _estimate_dispersion():
            """
            Estimate dispersion/scale based on the fitted model
            Returns
            -------
            dispersion: float
            """
            if isinstance(self.family, (Binomial, Poisson)):
                return 1.
            return self._model.scale

        def _estimate_stderr():
            """
            Estimate standard deviation of the coefficients.
            Returns
            -------
            standard deviation of the coefficients: array with the same shape as coef
            Notes
            -------
            I think the stderr of statsmodels is wrong.
            It uses the WLS stderr as the std err of GLM, which does not make sense,
            because the variance in WLS is inverse proportional to the weights.

            Anyway I will leave it here, stderr is not important.
            """
            if self.reg_method is None or self.alpha < EPS:
                return fit_results.bse * np.sqrt(self.dispersion / self._model.scale)
            return None

        X, sample_weight = self._transform_X_sample_weight(X, sample_weight=sample_weight)
        self._raise_error_if_sample_weight_sum_zero(sample_weight)
        Y = self._transform_Y(Y)
        self._model = sm.GLM(Y, X, family=self.family, freq_weights=sample_weight)
        # dof in weighted regression does not make sense, hard code it to the total weights
        self._model.df_resid = np.sum(sample_weight)
        if self.reg_method is None or self.alpha < EPS:
            fit_results = self._model.fit(
                maxiter=self.max_iter, tol=self.tol, method=self.solver, wls_method='pinv')
        else:
            fit_results = self._model.fit_regularized(
                method=self.reg_method, alpha=self.alpha,
                L1_wt=self.l1_ratio, maxiter=self.max_iter)
        self.coef = fit_results.params
        self.dispersion = _estimate_dispersion()
        if self.est_stderr:
            self.stderr = _estimate_stderr()

    def _transform_Y(self, Y):
        """
        Transform the response Y
        ----------
        Y : response matrix of shape (n_samples, ) or (n_samples, k) depending on family
        Returns
        -------
        Y : response matrix of shape (n_samples, ) or (n_samples, k) depending on family
        """
        if Y.ndim == 2 and Y.shape[1] == 1:
            Y = Y.reshape(-1,)
        return Y

    def predict(self, X):
        """
        Predict the Y value based on the model
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        predicted value, of shape (n_samples, ), 1d
        """
        self._raise_error_if_model_not_trained()
        X = self._transform_X(X)
        return self._model.predict(self.coef, exog=X)

    def loglike_per_sample(self, X, Y):
        """
        Given a set of X and Y, calculate the log probability of
        observing each of Y value given each X value
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) or (n_samples, k) depending on family
        Returns
        -------
        log_p: array of shape (n_samples, )
        """
        self._raise_error_if_model_not_trained()
        assert X.shape[0] == Y.shape[0]
        Y = self._transform_Y(Y)
        mu = self.predict(X)
        if isinstance(self.family, Binomial):
            endog, _ = self.family.initialize(Y, 1.0)
        else:
            endog = Y
        if self.dispersion > EPS:
            return self.family.loglike_obs(endog, mu, scale=self.dispersion)
        log_p = np.zeros(endog.shape[0])
        log_p[~np.isclose(endog, mu)] = - np.Infinity
        return log_p

    def to_json(self, path):
        """
        Generate json object of the model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the GLM
        """
        json_dict = super(GLM, self).to_json(path=path)
        json_dict['properties'].update(
            {
                'family': {
                    'data_type': self.family.__class__.__name__,
                    'path': os.path.join(path, 'family.p')
                },
                'dispersion': {
                    'data_type': 'numpy.ndarray',
                    'path': os.path.join(path, 'dispersion.npy')
                }
            })
        if not os.path.exists(os.path.dirname(json_dict['properties']['family']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['family']['path']))
        pickle.dump(self.family, open(json_dict['properties']['family']['path'], 'wb'))
        if not os.path.exists(os.path.dirname(json_dict['properties']['dispersion']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['dispersion']['path']))
        np.save(json_dict['properties']['dispersion']['path'], self.dispersion)
        return json_dict

    @classmethod
    def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
                   tol, max_iter, reg_method, alpha, l1_ratio, coef, stderr):
        """
        Helper function to construct the GLM used by from_json.
        This function overrides the parent class.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for GLM
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, elstic_net).
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        GLM object: a GLM object specified by the json_dict and other arguments
        """
        with open(json_dict['properties']['family']['path'], 'rb') as f:
            return cls(
                solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
                reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
                coef=coef, stderr=stderr, tol=tol, max_iter=max_iter,
                family=pickle.load(f),
                dispersion=np.load(json_dict['properties']['dispersion']['path'],
                                   allow_pickle=True))


class OLS(BaseModel):
    """
    A wrapper for Univariate and Multivariate Ordinary Least Squares (OLS).
    """

    def __init__(self, solver='svd', fit_intercept=True, est_stderr=False,
                 reg_method=None,  alpha=0, l1_ratio=0, tol=1e-4, max_iter=100,
                 coef=None, stderr=None,  dispersion=None, n_targets=None):
        """
        Constructor
        Parameters
        ----------
        solver: specific solver for OLS, default 'svd', possible solvers are:
                {'auto', 'svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag'}.
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, l1, l2, elstic_net).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model

        n_targets: the number of dependent variables
        dispersion: dispersion/scale mareix of the OLS
        -------
        """
        super(OLS, self).__init__(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            tol=tol, max_iter=max_iter,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr)
        self.dispersion = dispersion
        self.n_targets = n_targets
        self._pick_model()
        if self.coef is not None:
            self._model.coef_ = coef
            self._model.intercept_ = 0

    def _pick_model(self):
        """
        Helper function to select a proper sklearn linear regression model
        based on the regulariztaion specified by the user.
        """
        if self.reg_method is None or self.alpha < EPS:
            self._model = linear_model.LinearRegression(
                fit_intercept=False)
        if self.reg_method == 'l1':
            self._model = linear_model.Lasso(
                fit_intercept=False, alpha=self.alpha,
                tol=self.tol, max_iter=self.max_iter)
        if self.reg_method == 'l2':
            self._model = linear_model.Ridge(
                fit_intercept=False, alpha=self.alpha, tol=self.tol,
                max_iter=self.max_iter, solver=self.solver)
        if self.reg_method == 'elastic_net':
            self._model = linear_model.ElasticNet(
                fit_intercept=False, alpha=self.alpha,
                l1_ratio=self.l1_ratio, tol=self.tol,
                max_iter=self.max_iter)

    def fit(self, X, Y, sample_weight=None):
        """
        Fit the weighted model
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, n_targets), 2d
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        """
        def _estimate_dispersion():
            """
            Estimate dispersion matrix based on the fitted model
            Returns
            -------
            dispersion matrix: array of shape (n_targets, n_targets), 2d
            """
            mu, wendog, _ = _rescale_data(self.predict(X), Y, sample_weight)
            wresid = mu - wendog
            return np.dot(wresid.T, wresid) / np.sum(sample_weight)

        def _estimate_stderr():
            """
            Estimate standard deviation of the coefficients.
            Returns
            -------
            standard deviation of the coefficients: array with the same shape as coef
            Notes
            -------
            It is not the same stderr as Weighted Least Squares (WLS).
            WLS assumes sample weight is inversely proportional to the covariance.
            Useful links:
            http://www.public.iastate.edu/~maitra/stat501/lectures/MultivariateRegression.pdf
            https://stats.stackexchange.com/questions/52704/covariance-of-linear-
            regression-coefficients-in-weighted-least-squares-method
            http://pj.freefaculty.org/guides/stat/Regression/GLS/GLS-1-guide.pdf
            https://stats.stackexchange.com/questions/27033/in-r-given-an-output-from-
            optim-with-a-hessian-matrix-how-to-calculate-paramet
            http://msekce.karlin.mff.cuni.cz/~vorisek/Seminar/0910l/jonas.pdf
            """
            if self.reg_method is None or self.alpha < EPS:
                wexog, wendog, _ = _rescale_data(X_train, Y, sample_weight)
                stderr = np.zeros((self.n_targets, X_train.shape[1]))
                try:
                    XWX_inverse_XW_sqrt = np.linalg.inv(np.dot(wexog.T, wexog)).dot(wexog.T)
                except np.linalg.linalg.LinAlgError:
                    logging.warning('Covariance matrix is singular, cannot estimate stderr.')
                    return None
                sqrt_diag_XWX_inverse_XW_sqrt_W_XWX_inverse_XW_sqrt = np.sqrt(np.diag(
                    XWX_inverse_XW_sqrt.dot(np.diag(sample_weight)).dot(XWX_inverse_XW_sqrt.T)))
                for target in range(self.n_targets):
                    stderr[target, :] = (np.sqrt(self.dispersion[target, target]) *
                                         sqrt_diag_XWX_inverse_XW_sqrt_W_XWX_inverse_XW_sqrt)
                return stderr.reshape(self.coef.shape)
            return None

        X_train, sample_weight = self._transform_X_sample_weight(X, sample_weight=sample_weight)
        self._raise_error_if_sample_weight_sum_zero(sample_weight)
        Y = self._transform_Y(Y)
        self.n_targets = Y.shape[1]
        self._model.fit(X_train, Y, sample_weight)
        self.coef = self._model.coef_
        self.dispersion = _estimate_dispersion()
        if self.est_stderr:
            self.stderr = _estimate_stderr()

    def _transform_Y(self, Y):
        """
        Transform the response Y
        ----------
        Y : response matrix of shape (n_samples, ) or (n_samples, n_targets) depending on family
        Returns
        -------
        Y : response matrix of shape (n_samples, n_targets)
        """
        if Y.ndim == 1:
            Y = Y.reshape(-1, 1)
        return Y

    def predict(self, X):
        """
        Predict the Y value based on the model
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        predicted value, of shape (n_samples, n_targets), 2d
        """
        self._raise_error_if_model_not_trained()
        X = self._transform_X(X)
        return self._model.predict(X).reshape(-1, self.n_targets)

    def loglike_per_sample(self, X, Y):
        """
        Given a set of X and Y, calculate the log probability of
        observing each of Y value given each X value
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : observed response matrix of shape (n_samples, n_targets), 2d
        Returns
        -------
        log_p: array of shape (n_samples, )
        """
        self._raise_error_if_model_not_trained()
        assert X.shape[0] == Y.shape[0]
        mu = self.predict(X)
        # https://stackoverflow.com/questions/13312498/how-to-find-degenerate-
        # rows-columns-in-a-covariance-matrix
        Y = self._transform_Y(Y)
        zero_inds = np.where(np.diag(self.dispersion) < EPS)[0]
        log_p = np.zeros(Y.shape[0])
        log_p[~np.isclose(
            np.linalg.norm(
                Y[:, zero_inds] - mu[:, zero_inds], axis=1), 0)] = - np.Infinity
        non_zero_inds = np.setdiff1d(
            np.arange(Y.shape[1]), zero_inds, assume_unique=True)
        dispersion = self.dispersion[np.ix_(non_zero_inds, non_zero_inds)]
        if dispersion.shape[0] == 0:
            return log_p
        if np.linalg.cond(dispersion) < 1 / EPS:
            # This is a harsh test, if the det is ensured to be > 0
            # all diagonal of dispersion will be > 0
            # for the zero parts:
            rv = multivariate_normal(cov=dispersion)
            log_p += rv.logpdf(Y[:, non_zero_inds] - mu[:, non_zero_inds])
            return log_p
        else:
            raise ValueError(
                """
                    Dispersion matrix is singular, cannot calculate likelike_per_sample.
                    Most like due to perfect correlations among dependent variables.
                    Try another model specification.
                """
            )

    def to_json(self, path):
        """
        Generate json object of the model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the OLS
        """
        json_dict = super(OLS, self).to_json(path=path)
        json_dict['properties'].update(
            {
                'dispersion': {
                    'data_type': 'numpy.ndarray',
                    'path': os.path.join(path, 'dispersion.npy')
                },
                'n_targets': self.n_targets
            })
        if not os.path.exists(os.path.dirname(json_dict['properties']['dispersion']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['dispersion']['path']))
        np.save(json_dict['properties']['dispersion']['path'], self.dispersion)
        return json_dict

    @classmethod
    def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
                   tol, max_iter, reg_method, alpha, l1_ratio, coef, stderr):
        """
        Helper function to construct the OLS used by from_json.
        This function overrides the parent class.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for OLS
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, l1, l2, elstic_net).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: if elastic_net, the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        OLS object: an OLS object specified by the json_dict and other arguments
        """
        return cls(solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
                   reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
                   coef=coef, stderr=stderr,
                   tol=tol, max_iter=max_iter,
                   dispersion=np.load(json_dict['properties']['dispersion']['path'],
                                      allow_pickle=True),
                   n_targets=json_dict['properties']['n_targets'])


class BaseMNL(BaseModel):
    """
    A Base Multinomial Logistic regression model.
    BaseMNL does nothing, to be extended by
    (1) MNL with discrete output (DiscreteMNL) and.
    (2) MNL with probability output (CrossEntropyMNL).
    """

    def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
                 reg_method='l2', alpha=0, l1_ratio=0,
                 tol=1e-4, max_iter=100,
                 coef=None, stderr=None,
                 classes=None, n_classes=None):
        """
        Constructor
        Parameters
        ----------
        solver: specific solver for each linear model, default 'lbfgs',
                possible solvers are {'newton-cg', 'lbfgs', 'liblinear', 'sag'}.
                Need to be consistent with the regularization method.
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model

        classes: an array of class labels
        n_classes: the number of classes to be classified
        -------
        """
        super(BaseMNL, self).__init__(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            tol=tol, max_iter=max_iter,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr)

        self.classes = classes
        self.n_classes = n_classes
        if self.coef is not None:
            if self.n_classes >= 2:
                self._pick_model()
                self._model.coef_ = coef
                self._model.classes_ = classes
                self._model.intercept_ = 0

    def _pick_model(self):
        """
        Helper function to select a proper sklearn logistic regression model
        based on the regulariztaion specified by the user.
        """
        C = np.float64(1) / self.alpha
        if self.n_classes == 2:
            # perform logistic regression
            self._model = linear_model.LogisticRegression(
                fit_intercept=False, penalty=self.reg_method, C=C,
                solver=self.solver, tol=self.tol, max_iter=self.max_iter)

        else:
            # perform multinomial logistic regression
            self._model = linear_model.LogisticRegression(
                fit_intercept=False, penalty=self.reg_method, C=C,
                solver=self.solver, tol=self.tol, max_iter=self.max_iter,
                multi_class='multinomial')

    def fit(self, X, Y, sample_weight=None):
        """
        Fit the weighted model
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) for DiscreteMNL and
            (n_samples, n_classes) for CrossEntropyMNL
        sample_weight: sample weight vector of shape (n_samples, ), or float, or None
        """
        def _estimate_stderr():
            """
            Estimate standard deviation of the coefficients.
            Returns
            -------
            None for now, since I am not sure if we can estimate the stderr
            under the case there is sample_weight since there is no likelihood,
            thus no hessian of the log likelihood.
            Notes
            -------
            http://mplab.ucsd.edu/tutorials/MultivariateLogisticRegression.pdf
            https://github.com/cran/mlogit/blob/master/R/mlogit.methods.R
            https://arxiv.org/pdf/1404.3177.pdf
            https://stats.stackexchange.com/questions/283780/calculate-standard-
            error-of-weighted-logistic-regression-coefficients

            Two codes to calculate hessian:
            1. with sample weights:
            https://github.com/scikit-learn/scikit-learn/
            blob/ab93d657eb4268ac20c4db01c48065b5a1bfe80d/sklearn/linear_model/logistic.py
            2. without sample weights
            http://www.statsmodels.org/dev/_modules/statsmodels/
            discrete/discrete_model.html#MNLogit
            """
            return None

        X, sample_weight = self._transform_X_sample_weight(X, sample_weight=sample_weight)
        self._raise_error_if_sample_weight_sum_zero(sample_weight)
        X, Y, sample_weight = self._label_encoder(
            X, Y, sample_weight)
        assert Y.ndim == 1
        classes = np.unique(Y)
        self.n_classes = len(classes)

        if self.n_classes == 1:
            # no need to perform any model
            # self.coef is a all zeros array of shape (n_features,1)
            self.coef = np.zeros((X.shape[1], 1))
            self.classes = classes
        else:
            self._pick_model()
            self._model.fit(X, Y, sample_weight=sample_weight)
            # self.coef shape is wierd in sklearn, I will stick with it
            self.coef = self._model.coef_
            self.classes = self._model.classes_
            if self.est_stderr:
                self.stderr = _estimate_stderr()

    @staticmethod
    def _label_encoder(X, Y, sample_weight):
        """
        Convert input to proper format to be used by sklearn logistic regression.
        Mainly transforms Y to a 1d vector containing the class label for each sample.
        This function is designed to be override by subclasses.
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) for DiscreteMNL and
            (n_samples, n_classes) for CrossEntropyMNL
        sample_weight: sample weight vector of shape (n_samples, )
        Returns
        -------
        X_transformed : design matrix of shape (n, n_features), 2d
        Y_transformed : response matrix of shape (n, )
        sample_weight_transformed: sample weight vector of shape (n, )
        where n:
        is n_samples in the discrete case and
        is n_samples * n_classes in the cross entropy case
        """
        raise NotImplementedError

    def _label_decoder(self, Y):
        """
        Convert the response vector to probability matrix.
        This function is designed to be override by subclasses.
        Parameters
        ----------
        Y : response matrix of shape (n_samples, ) for DiscreteMNL and
            (n_samples, n_classes) for CrossEntropyMNL
        Returns
        -------
        Y_transformed : of shape (n_samples, n_classes).
        """
        raise NotImplementedError

    def predict_log_proba(self, X):
        """
        Predict the log probability of each class
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        log probability matrix : of shape (n_samples, n_classes), 2d
        """
        self._raise_error_if_model_not_trained()
        X = self._transform_X(X)
        if self.n_classes == 1:
            return np.zeros((X.shape[0], 1))

        return self._model.predict_log_proba(X)

    def predict(self, X):
        """
        Predict the most likely class label for each sample
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Returns
        -------
        labels : of shape (n_samples, ), 1d
        """
        self._raise_error_if_model_not_trained()
        X = self._transform_X(X)
        if self.n_classes == 1:
            return self.classes[np.zeros(X.shape[0], dtype=np.int)]
        return self._model.predict(X)

    def loglike_per_sample(self, X, Y):
        """
        Given a set of X and Y, calculate the log probability of
        observing each of Y value given each X value
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, ) for DiscreteMNL and
            (n_samples, n_classes) for CrossEntropyMNL
        Returns
        -------
        log_p: array of shape (n_samples, )
        """
        self._raise_error_if_model_not_trained()
        assert X.shape[0] == Y.shape[0]
        Y = self._label_decoder(Y)
        assert X.shape[0] == Y.shape[0]
        assert Y.shape[1] == self.n_classes
        log_p = np.sum(self.predict_log_proba(X) * Y, axis=1)
        log_p[np.sum(Y, axis=1) < EPS] = -np.Infinity
        return log_p

    @classmethod
    def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,
                       tol, max_iter, reg_method, alpha, l1_ratio, coef, stderr):
        """
        Helper function within the BaseMNL class to construct the specific MNL used by _from_json.
        This function is designed to be override by subsubclasses.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for each MNL
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        Discrete/CrossEntropyMNL object: a MNL object specified by the json_dict and other arguments
        """
        return cls(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr, tol=tol, max_iter=max_iter)

    @classmethod
    def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
                   tol, max_iter, reg_method, alpha, l1_ratio, coef, stderr):
        """
        Helper function to construct the linear model used by from_json.
        This function overrides the parent class.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for each MNL
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        Discrete/CrossEntropyMNL object: a MNL object specified by the json_dict and other arguments
        """
        return cls._from_json_MNL(
            json_dict,
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr, tol=tol, max_iter=max_iter)


class DiscreteMNL(BaseMNL):
    """
    A MNL for the case where responses are discrete labels.
    """

    def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
                 reg_method='l2', alpha=0, l1_ratio=0,
                 tol=1e-4, max_iter=100,
                 coef=None, stderr=None,
                 classes=None):
        """
        Constructor
        Parameters
        ----------
        solver: specific solver for each linear model, default 'lbfgs',
                possible solvers are {'newton-cg', 'lbfgs', 'liblinear', 'sag'}.
                Need to be consistent with the regularization method.
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model

        classes: class labels if loading from trained model
        -------
        """
        n_classes = None if classes is None else classes.shape[0]
        super(DiscreteMNL, self).__init__(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            tol=tol, max_iter=max_iter,
            coef=coef, stderr=stderr,
            classes=classes, n_classes=n_classes)

    @staticmethod
    def _label_encoder(X, Y, sample_weight):
        """
        Convert input to proper format to be used by sklearn logistic regression.
        Basically do nothing for the discrete case.
        This function overrides parent class.
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, )
        sample_weight: sample weight vector of shape (n_samples, )
        Returns
        -------
        X_transformed : design matrix of shape (n_samples, n_features), 2d
        Y_transformed : response matrix of shape (n_samples, )
        sample_weight_transformed: sample weight vector of shape (n_samples, )
        """
        if Y.ndim == 2 and Y.shape[1] == 1:
            Y = Y.reshape(-1,)
        return X, Y, sample_weight

    def _label_decoder(self, Y):
        """
        Convert the response vector to probability matrix.
        This function overrides parent classes.
        Parameters
        ----------
        Y : response matrix of shape (n_samples, )
        Returns
        -------
        Y_transformed : of shape (n_samples, n_classes).
        """
        # consider the case of outside labels
        if Y.ndim == 2 and Y.shape[1] == 1:
            Y = Y.reshape(-1,)
        assert Y.ndim == 1
        if self.n_classes == 1:
            return (Y == self.classes).reshape(-1, 1).astype(float)
        if self.n_classes == 2:
            # sklearn is stupid here
            label = np.zeros((Y.shape[0], self.n_classes))
            for clas_i, clas in enumerate(self.classes):
                label[:, clas_i] = (Y == clas).astype(float)
            return label
        return label_binarize(Y, self.classes)

    def to_json(self, path):
        """
        Generate json object of the model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the DiscreteMNL
        """
        json_dict = super(DiscreteMNL, self).to_json(path=path)
        json_dict['properties'].update(
            {
                'classes': {
                    'data_type': 'numpy.ndarray',
                    'path': os.path.join(path, 'classes.npy')
                }
            })
        if not os.path.exists(os.path.dirname(json_dict['properties']['classes']['path'])):
            os.makedirs(os.path.dirname(json_dict['properties']['classes']['path']))
        np.save(json_dict['properties']['classes']['path'], self.classes)
        return json_dict

    @classmethod
    def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,
                       reg_method, alpha, l1_ratio, coef, stderr,
                       tol, max_iter):
        """
        Helper function within the construct the DiscreteMNL used by _from_json.
        This function overrides parent class.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for each linear model
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (None, l1, l2, elstic_net).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        DiscreteMNL object: a DiscreteMNL object specified by the json_dict and other arguments
        """
        return cls(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr,
            tol=tol, max_iter=max_iter,
            classes=np.load(json_dict['properties']['classes']['path'], allow_pickle=True))


class CrossEntropyMNL(BaseMNL):
    """
    A MNL for the case where responses are probabilities sum to one.
    """

    def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
                 reg_method='l2', alpha=0, l1_ratio=0,
                 tol=1e-4, max_iter=100,
                 coef=None, stderr=None,
                 n_classes=None):
        """
        Constructor
        Parameters
        ----------
        solver: specific solver for each linear model, default 'lbfgs',
                possible solvers are {'newton-cg', 'lbfgs', 'liblinear', 'sag'}.
                Need to be consistent with the regularization method.
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients if loading from trained model
        stderr: the std.err of coefficients if loading from trained model

        n_classes: number of classes to be classified
        -------
        """
        classes = None if n_classes is None else np.arange(n_classes)
        super(CrossEntropyMNL, self).__init__(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            tol=tol, max_iter=max_iter,
            coef=coef, stderr=stderr,
            classes=classes, n_classes=n_classes)

    @staticmethod
    def _label_encoder(X, Y, sample_weight):
        """
        Convert input to proper format to be used by sklearn logistic regression.
        Mainly transforms Y to a 1d vector containing the class label for each sample.
        This function overrides parent class.
        Parameters
        ----------
        X : design matrix of shape (n_samples, n_features), 2d
        Y : response matrix of shape (n_samples, n_classes)
        sample_weight: sample weight vector of shape (n_samples, )
        Returns
        -------
        X_repeated : design matrix of shape (n_samples * n_classes, n_features), 2d
        Y_repeated : response matrix of shape (n_samples * n_classes, )
        sample_weight_repeated: sample weight vector of shape (n_samples * n_classes, )
        Notes
        ----------
        idea from https://stats.stackexchange.com/questions/90622/
        regression-model-where-output-is-a-probability
        """
        n_samples, n_classes = X.shape[0], Y.shape[1]
        X_repeated = np.repeat(X, n_classes, axis=0)
        Y_repeated = np.tile(np.arange(n_classes), n_samples)
        sample_weight_repeated = Y.reshape(-1, ) * np.repeat(sample_weight, n_classes)
        return X_repeated, Y_repeated, sample_weight_repeated

    def _label_decoder(self, Y):
        """
        Convert the response vector to probability matrix.
        In CrossEntropyMNL, this function basically does nothing.
        This function overrides parent classes.
        Parameters
        ----------
        Y : response matrix of shape (n_samples, n_classes)
        Returns
        -------
        Y_transformed : of shape (n_samples, n_classes).
        """
        assert Y.ndim == 2
        assert Y.shape[1] == self.n_classes
        return Y

    def to_json(self, path):
        """
        Generate json object of the model
        Parameters
        ----------
        path : the path to save the model
        Returns
        -------
        json_dict: a dictionary containing the attributes of the CrossEntropyMNL
        """
        json_dict = super(CrossEntropyMNL, self).to_json(path=path)
        json_dict['properties'].update(
            {
                'n_classes': self.n_classes
            })
        return json_dict

    @classmethod
    def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,
                       reg_method, alpha, l1_ratio, coef, stderr,
                       tol, max_iter):
        """
        Helper function within the construct the CrossEntropyMNL used by _from_json.
        This function overrides parent class.
        Parameters
        ----------
        json_dict : the dictionary that specifies the model
        solver: specific solver for each linear model
        fit_intercept: boolean indicating fit intercept or not
        est_stderr: boolean indicating calculte std.err of coefficients (usually expensive) or not
        tol: tolerence of fitting error
        max_iter: maximum iteraration of fitting
        reg_method: method to regularize the model, one of (l1, l2).
                    Need to be compatible with the solver.
        alpha: regularization strength
        l1_ratio: the l1 alpha ratio
        coef: the coefficients
        stderr: the std.err of coefficients
        Returns
        -------
        CrossEntropyMNL object:
            a CrossEntropyMNL object specified by the json_dict and other arguments
        """
        return cls(
            solver=solver, fit_intercept=fit_intercept, est_stderr=est_stderr,
            reg_method=reg_method, alpha=alpha, l1_ratio=l1_ratio,
            coef=coef, stderr=stderr,
            tol=tol, max_iter=max_iter,
            n_classes=json_dict['properties']['n_classes'])


================================================
FILE: LICENCE
================================================
Copyright (c) 2017, Mogeng Yin, Alexei Pozdnukhov
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the 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.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 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.

================================================
FILE: README.md
================================================
# IOHMM

A Python package of Input-Output Hidden Markov Model (IOHMM).

[![Build Status](https://travis-ci.org/Mogeng/IOHMM.svg?branch=master)](https://travis-ci.org/Mogeng/IOHMM) [![Coverage Status](https://coveralls.io/repos/github/Mogeng/IOHMM/badge.svg)](https://coveralls.io/github/Mogeng/IOHMM)

IOHMM extends standard HMM by allowing (a) initial, (b) transition and (c) emission probabilities to depend on various covariates. A graphical representation of  standard HMM and IOHMM:

| Standard HMM | IOHMM |
| --- | --- |
| <img width="300" src="./documents/HMM.png">  |  <img width="300" src="./documents/IOHMM.png">|


The solid nodes represent observed information, while the transparent (white) nodes represent latent random variables. The top layer contains the **observed** input variables *__u<sub>t</sub>__*; the middle layer contains **latent** categorical variable *z<sub>t</sub>*; and the bottom layer contains **observed** output variables *__x<sub>t</sub>__*. The input for (a) initial, (b) transition and (c) emission probabilities does not have to be the same.

For more theoretical details:
* [An Input Output HMM Architecture](https://papers.nips.cc/paper/964-an-input-output-hmm-architecture.pdf)
* [Input-output HMMs for sequence processing](http://ieeexplore.ieee.org/document/536317/)

Applications of IOHMM:
* [A Generative Model of Urban Activities from Cellular Data](http://ieeexplore.ieee.org/document/7932990/)
## Installing
```shell
pip install IOHMM
```

## Examples

The `example` directory contains a set of [Jupyter Notebook of examples and demonstrations](https://github.com/Mogeng/IOHMM/tree/master/examples/notebooks) of:

* [UnSupervised IOHMM](https://github.com/Mogeng/IOHMM/blob/master/examples/notebooks/UnSupervisedIOHMM.ipynb)

* [SemiSupervised IOHMM](https://github.com/Mogeng/IOHMM/blob/master/examples/notebooks/SemiSupervisedIOHMM.ipynb)

* [Supervised IOHMM](https://github.com/Mogeng/IOHMM/blob/master/examples/notebooks/SupervisedIOHMM.ipynb)

## Features

* **3-in-1 IOHMM**. IOHMM package supports:
  - [UnSupervised](https://en.wikipedia.org/wiki/Unsupervised_learning) IOHMM when you have no ground truth hidden states at any timestamp. [Expectation-Maximization (EM) algorithm](https://en.wikipedia.org/wiki/Expectation%E2%80%93maximization_algorithm) will be used to estimate parameters (maximization step) and posteriors (expectation step).
  - [SemiSupervised](https://en.wikipedia.org/wiki/Semi-supervised_learning) IOHMM when you have certain amount of ground truth hidden states and would like to enforce these labeled hidden states during learning and use these labels to help direct the learning process.
  - [Supervised](https://en.wikipedia.org/wiki/Supervised_learning) IOHMM when you want to purely use labeled ground truth hidden states during the learning. There will be no expectation step and only one shot of maximization step during the learning since all the posteriors are from labeled ground truth.

* __Crystal clear structure__. Know each step you go:
	- All sequences are represented by [pandas](http://pandas.pydata.org/) dataframes. It has great interface to load csv, json, etc. files or to pull data from sql databases. It is also easy to visualize.
	- Inputs and outputs covariates are specified by the column names (strings) in the dataframes.
	- You can pass a list of sequences (dataframes) as data -- there is no more need to tag the start of each sequence in a single stacked sequence.
	- You can specify different set of inputs for (a) initial, (b) transition and (c) different emission models.

* __Forward Backward algorithm__. Faster and more robust:
	- Fully [vectorized](https://en.wikipedia.org/wiki/Array_programming). Only one 'for loop' (due to [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)) in the forward/backward pass where most current implementations have more than one 'for loop'.
	- All calculations are at *log* level, this is more robust to long sequence for which the probabilities easily vanish to 0.

* __Json-serialization__. Models on the go:
	-  Save (`to_json`) and load (`from_json`) a trained model in json format. All the attributes are easily visualizable in the json dictionary/file. See [Jupyter Notebook of examples](https://github.com/Mogeng/IOHMM/tree/master/examples/notebooks) for more details.
	- Use a json configuration file to specify the structure of an IOHMM model (`from_config`). This is useful when you have an application that uses IOHMM models and would like to specify the model before hand.

* __Statsmodels and scikit-learn as the backbone__. Take the best of both and better:
	- Unified interface/wrapper to statsmodels and scikit-learn linear models/generalized linear models.
	- Supports fitting the model with sample frequency weights.
	- Supports regularizations in these models.
	- Supports estimation of standard error of the coefficients in certain models.
	- Json-serialization to save (`to_json`) and load (`from_json`) of trained linear models.

## Credits

* The structure of this implementation is inspired by depmixS4: [depmixS4: An R Package for Hidden Markov Models](https://cran.opencpu.org/web/packages/depmixS4/vignettes/depmixS4.pdf).
* This IOHMM package uses/wraps [statsmodels](http://www.statsmodels.org/stable/index.html) and [scikit-learn](http://scikit-learn.org/stable/) APIs for linear supervised models.

## Licensing

Modified BSD (3-clause)

================================================
FILE: examples/data/speed.csv
================================================
"","rt","corr","Pacc","prev"
"1",6.45676965557216,"cor",0,"inc"
"2",5.6021188208797,"cor",0,"cor"
"3",6.25382881157547,"inc",0,"cor"
"4",5.4510384535657,"inc",0,"inc"
"5",5.87211778947542,"inc",0,"inc"
"6",6.00388706710654,"cor",0,"inc"
"7",5.40267738187228,"cor",0.0454545454545455,"cor"
"8",6.40687998606931,"cor",0.0909090909090909,"cor"
"9",6.07764224334903,"inc",0.136363636363636,"cor"
"10",5.48063892334199,"cor",0.181818181818182,"inc"
"11",5.95842469302978,"cor",0.227272727272727,"cor"
"12",5.82894561761021,"inc",0.272727272727273,"cor"
"13",5.92692602597041,"cor",0.318181818181818,"inc"
"14",6.12468339089421,"inc",0.363636363636364,"cor"
"15",5.95583736946483,"cor",0.409090909090909,"inc"
"16",6.0591231955818,"cor",0.454545454545454,"cor"
"17",6.47850964220857,"inc",0.5,"cor"
"18",6.44571981938558,"cor",0.545454545454545,"inc"
"19",6.56244409369372,"cor",0.590909090909091,"cor"
"20",6.64768837356333,"cor",0.636363636363636,"cor"
"21",6.49677499018586,"cor",0.681818181818182,"cor"
"22",6.39526159811545,"cor",0.727272727272727,"cor"
"23",6.52941883826223,"cor",0.772727272727273,"cor"
"24",6.37672694789863,"cor",0.727272727272727,"cor"
"25",6.54821910276237,"cor",0.681818181818182,"cor"
"26",6.40191719672719,"cor",0.636363636363636,"cor"
"27",6.36302810354046,"cor",0.590909090909091,"cor"
"28",6.25382881157547,"cor",0.545454545454545,"cor"
"29",6.33327962813969,"cor",0.5,"cor"
"30",6.51767127291227,"cor",0.454545454545454,"cor"
"31",6.3456363608286,"cor",0.409090909090909,"cor"
"32",6.0913098820777,"cor",0.363636363636364,"cor"
"33",7.15148546390474,"cor",0.318181818181818,"cor"
"34",5.42934562895444,"cor",0.272727272727273,"cor"
"35",5.04342511691925,"cor",0.227272727272727,"cor"
"36",5.33271879326537,"inc",0.181818181818182,"cor"
"37",5.27811465923052,"cor",0.136363636363636,"inc"
"38",5.56452040732269,"cor",0.0909090909090909,"cor"
"39",5.39816270151775,"inc",0.0454545454545455,"cor"
"40",5.50533153593236,"cor",0.0909090909090909,"inc"
"41",5.5683445037611,"cor",0.136363636363636,"cor"
"42",5.51745289646471,"cor",0.181818181818182,"cor"
"43",5.39816270151775,"inc",0.227272727272727,"cor"
"44",5.56068163101553,"cor",0.272727272727273,"inc"
"45",6.16961073249146,"cor",0.318181818181818,"cor"
"46",6.06610809010375,"inc",0.363636363636364,"cor"
"47",6.18414889093748,"cor",0.409090909090909,"inc"
"48",6.22653666928747,"cor",0.454545454545454,"cor"
"49",6.35088571671474,"cor",0.5,"cor"
"50",6.22059017009974,"cor",0.545454545454545,"cor"
"51",6.12249280951439,"cor",0.590909090909091,"cor"
"52",6.26909628370626,"cor",0.636363636363636,"cor"
"53",6.66949808985788,"cor",0.590909090909091,"cor"
"54",6.41345895716736,"cor",0.545454545454545,"cor"
"55",6.48157712927643,"inc",0.5,"cor"
"56",6.90775527898214,"cor",0.454545454545454,"inc"
"57",6.36302810354046,"cor",0.409090909090909,"cor"
"58",6.58617165485467,"cor",0.363636363636364,"cor"
"59",6.36647044773144,"cor",0.318181818181818,"cor"
"60",5.91889385427315,"inc",0.272727272727273,"cor"
"61",6.0282785202307,"inc",0.227272727272727,"inc"
"62",5.44673737166631,"cor",0.181818181818182,"inc"
"63",5.92157841964382,"cor",0.136363636363636,"cor"
"64",5.91889385427315,"inc",0.0909090909090909,"cor"
"65",6.24610676548156,"cor",0.0454545454545455,"inc"
"66",5.22035582507832,"cor",0,"cor"
"67",5.4510384535657,"cor",0,"cor"
"68",5.42053499927229,"cor",0.0454545454545455,"cor"
"69",5.54907608489522,"inc",0.0909090909090909,"cor"
"70",5.64190707093811,"cor",0.136363636363636,"inc"
"71",5.58724865840025,"cor",0.181818181818182,"cor"
"72",5.36597601502185,"cor",0.227272727272727,"cor"
"73",5.4510384535657,"cor",0.272727272727273,"cor"
"74",6.12686918411419,"cor",0.318181818181818,"cor"
"75",5.44673737166631,"cor",0.363636363636364,"cor"
"76",5.88887795833288,"cor",0.409090909090909,"cor"
"77",6.51174532964473,"cor",0.454545454545454,"cor"
"78",6.57507584059962,"cor",0.5,"cor"
"79",6.45047042214418,"cor",0.545454545454545,"cor"
"80",7.20042489294496,"cor",0.590909090909091,"cor"
"81",6.361302477573,"cor",0.545454545454545,"cor"
"82",6.4085287910595,"cor",0.5,"cor"
"83",6.1463292576689,"cor",0.454545454545454,"cor"
"84",6.10479323241498,"cor",0.409090909090909,"cor"
"85",6.20859002609663,"cor",0.363636363636364,"cor"
"86",6.93439720992856,"cor",0.318181818181818,"cor"
"87",6.3818160174061,"cor",0.272727272727273,"cor"
"88",5.42053499927229,"cor",0.227272727272727,"cor"
"89",5.34710753071747,"cor",0.181818181818182,"cor"
"90",5.4249500174814,"cor",0.136363636363636,"cor"
"91",5.96100533962327,"inc",0.0909090909090909,"cor"
"92",5.29831736654804,"inc",0.0454545454545455,"inc"
"93",5.43372200355424,"inc",0.0909090909090909,"inc"
"94",5.50125821054473,"inc",0.136363636363636,"inc"
"95",5.29831736654804,"cor",0.181818181818182,"inc"
"96",5.39816270151775,"cor",0.227272727272727,"cor"
"97",5.6021188208797,"cor",0.272727272727273,"cor"
"98",5.40717177146012,"cor",0.318181818181818,"cor"
"99",5.57594910314632,"cor",0.363636363636364,"cor"
"100",5.52545293913178,"cor",0.409090909090909,"cor"
"101",6.19847871649231,"cor",0.454545454545454,"cor"
"102",6.383506634884,"cor",0.5,"cor"
"103",6.13988455222626,"cor",0.545454545454545,"cor"
"104",6.49072353450251,"cor",0.590909090909091,"cor"
"105",6.50727771238501,"cor",0.636363636363636,"cor"
"106",6.31535800152233,"cor",0.590909090909091,"cor"
"107",6.361302477573,"cor",0.545454545454545,"cor"
"108",6.53524127101366,"cor",0.5,"cor"
"109",6.57368016696065,"cor",0.454545454545454,"cor"
"110",6.96318998587024,"cor",0.409090909090909,"cor"
"111",6.42486902390539,"cor",0.363636363636364,"cor"
"112",5.99893656194668,"cor",0.318181818181818,"cor"
"113",6.42162226780652,"cor",0.272727272727273,"cor"
"114",6.31716468674728,"cor",0.227272727272727,"cor"
"115",6.44094654063292,"inc",0.181818181818182,"cor"
"116",5.73657229747919,"inc",0.136363636363636,"inc"
"117",5.80211837537706,"cor",0.0909090909090909,"inc"
"118",5.57215403217776,"inc",0.0454545454545455,"cor"
"119",6.01615715969835,"inc",0,"inc"
"120",5.52545293913178,"cor",0,"inc"
"121",5.43372200355424,"cor",0,"cor"
"122",5.5834963087817,"inc",0.0454545454545455,"cor"
"123",5.37527840768417,"inc",0.0909090909090909,"inc"
"124",5.75574221358691,"inc",0.136363636363636,"inc"
"125",5.59842195899838,"cor",0.181818181818182,"inc"
"126",5.42053499927229,"inc",0.227272727272727,"cor"
"127",5.75890177387728,"inc",0.272727272727273,"inc"
"128",6.50578406012823,"cor",0.318181818181818,"inc"
"129",6.38856140554563,"cor",0.363636363636364,"cor"
"130",6.89264164117209,"cor",0.409090909090909,"cor"
"131",6.55250788703459,"cor",0.454545454545454,"cor"
"132",6.42971947803914,"cor",0.5,"cor"
"133",6.20657592672493,"cor",0.545454545454545,"cor"
"134",6.92165818415113,"cor",0.5,"cor"
"135",6.14203740558736,"cor",0.454545454545454,"cor"
"136",6.20657592672493,"cor",0.409090909090909,"cor"
"137",6.5694814204143,"cor",0.363636363636364,"cor"
"138",6.36302810354046,"cor",0.318181818181818,"cor"
"139",6.15060276844628,"cor",0.272727272727273,"cor"
"140",6.28226674689601,"cor",0.227272727272727,"cor"
"141",6.70196036600254,"cor",0.181818181818182,"cor"
"142",6.86797440897029,"cor",0.136363636363636,"cor"
"143",5.68357976733868,"cor",0.0909090909090909,"cor"
"144",6.0137151560428,"inc",0.0454545454545455,"cor"
"145",5.41164605185504,"inc",0,"inc"
"146",5.65599181081985,"inc",0,"inc"
"147",5.52146091786225,"inc",0,"inc"
"148",5.68697535633982,"inc",0,"inc"
"149",5.42934562895444,"cor",0.0454545454545455,"inc"
"150",5.6021188208797,"inc",0.0909090909090909,"cor"
"151",5.46806014113513,"inc",0.136363636363636,"inc"
"152",5.37989735354046,"cor",0.181818181818182,"inc"
"153",5.73334127689775,"inc",0.227272727272727,"cor"
"154",6.08221891037645,"cor",0.272727272727273,"inc"
"155",6.35437004079735,"cor",0.318181818181818,"cor"
"156",6.12249280951439,"cor",0.363636363636364,"cor"
"157",6.36647044773144,"cor",0.409090909090909,"cor"
"158",6.87316383421252,"cor",0.454545454545454,"cor"
"159",6.34212141872115,"cor",0.5,"cor"
"160",6.13339804299665,"inc",0.454545454545454,"cor"
"161",6.47543271670409,"cor",0.409090909090909,"inc"
"162",6.25575004175337,"cor",0.363636363636364,"cor"
"163",6.93828448401696,"cor",0.318181818181818,"cor"
"164",6.57228254269401,"cor",0.272727272727273,"cor"
"165",5.43372200355424,"inc",0.227272727272727,"cor"
"166",5.59842195899838,"cor",0.181818181818182,"inc"
"167",5.63835466933375,"inc",0.136363636363636,"cor"
"168",5.4971682252932,"cor",0.0909090909090909,"inc"
"169",6.62140565176413,"cor",0,"inc"
"170",5.33271879326537,"cor",0,"cor"
"171",5.46383180502561,"inc",0,"cor"
"172",5.36129216570942,"cor",0,"inc"
"173",5.39816270151775,"cor",0,"cor"
"174",5.38449506278909,"cor",0,"cor"
"175",5.95324333428778,"inc",0.0454545454545455,"cor"
"176",5.70044357339069,"inc",0.0909090909090909,"inc"
"177",5.4510384535657,"inc",0.136363636363636,"inc"
"178",5.41610040220442,"inc",0.181818181818182,"inc"
"179",5.4971682252932,"inc",0.227272727272727,"inc"
"180",5.46806014113513,"cor",0.272727272727273,"inc"
"181",5.64897423816121,"cor",0.318181818181818,"cor"
"182",5.70044357339069,"cor",0.363636363636364,"cor"
"183",5.50938833662798,"cor",0.409090909090909,"cor"
"184",5.40717177146012,"inc",0.454545454545454,"cor"
"185",6.20859002609663,"cor",0.5,"inc"
"186",6.22851100359118,"cor",0.545454545454545,"cor"
"187",6.25958146406492,"inc",0.590909090909091,"cor"
"188",6.28599809450886,"cor",0.636363636363636,"inc"
"189",6.62671774924902,"cor",0.681818181818182,"cor"
"190",6.36302810354046,"inc",0.727272727272727,"cor"
"191",6.28226674689601,"cor",0.772727272727273,"inc"
"192",6.74875954749168,"cor",0.818181818181818,"cor"
"193",6.69950034016168,"cor",0.863636363636364,"cor"
"194",6.93342302573071,"cor",0.909090909090909,"cor"
"195",6.44571981938558,"cor",0.954545454545455,"cor"
"196",6.38012253689976,"cor",1,"cor"
"197",6.35088571671474,"cor",0.954545454545455,"cor"
"198",6.40191719672719,"cor",0.909090909090909,"cor"
"199",6.31173480915291,"cor",0.863636363636364,"cor"
"200",6.24027584517077,"cor",0.818181818181818,"cor"
"201",6.5424719605068,"cor",0.772727272727273,"cor"
"202",6.24027584517077,"cor",0.727272727272727,"cor"
"203",6.18826412308259,"cor",0.681818181818182,"cor"
"204",5.96100533962327,"cor",0.636363636363636,"cor"
"205",6.16120732169508,"cor",0.590909090909091,"cor"
"206",6.33150184989369,"cor",0.545454545454545,"cor"
"207",6.5206211275587,"cor",0.5,"cor"
"208",6.62273632394984,"cor",0.454545454545454,"cor"
"209",6.08904487544685,"inc",0.409090909090909,"cor"
"210",6.22455842927536,"cor",0.363636363636364,"inc"
"211",6.37161184723186,"cor",0.318181818181818,"cor"
"212",6.3818160174061,"cor",0.272727272727273,"cor"
"213",5.90263333340137,"cor",0.227272727272727,"cor"
"214",5.38449506278909,"cor",0.181818181818182,"cor"
"215",5.71373280550937,"inc",0.136363636363636,"cor"
"216",5.48063892334199,"inc",0.0909090909090909,"inc"
"217",5.72358510195238,"cor",0.0454545454545455,"inc"
"218",5.51745289646471,"cor",0.0909090909090909,"cor"
"219",5.63478960316925,"cor",0.136363636363636,"cor"
"220",5.62040086571715,"inc",0.181818181818182,"cor"
"221",5.33271879326537,"cor",0.227272727272727,"inc"
"222",5.48893772615669,"cor",0.272727272727273,"cor"
"223",5.78689738136671,"cor",0.318181818181818,"cor"
"224",5.37989735354046,"inc",0.363636363636364,"cor"
"225",6.41673228251233,"inc",0.409090909090909,"inc"
"226",6.63725803128446,"cor",0.454545454545454,"inc"
"227",6.26339826259162,"cor",0.5,"cor"
"228",6.4085287910595,"cor",0.545454545454545,"cor"
"229",6.97447891102505,"cor",0.590909090909091,"cor"
"230",6.35957386867238,"cor",0.636363636363636,"cor"
"231",6.27664348934165,"cor",0.681818181818182,"cor"
"232",6.23244801655052,"cor",0.636363636363636,"cor"
"233",6.53813982376767,"cor",0.590909090909091,"cor"
"234",6.13122648948314,"inc",0.545454545454545,"cor"
"235",6.59304453414244,"cor",0.5,"inc"
"236",6.93634273583405,"cor",0.454545454545454,"cor"
"237",6.70441435496411,"cor",0.409090909090909,"cor"
"238",5.65599181081985,"inc",0.363636363636364,"cor"
"239",5.62762111369064,"inc",0.318181818181818,"inc"
"240",5.48893772615669,"inc",0.272727272727273,"inc"
"241",5.49306144334055,"cor",0.227272727272727,"inc"
"242",5.42934562895444,"inc",0.181818181818182,"cor"
"243",5.43372200355424,"cor",0.136363636363636,"inc"
"244",5.45958551414416,"cor",0.181818181818182,"cor"
"245",5.48893772615669,"inc",0.227272727272727,"cor"
"246",5.59098698051086,"cor",0.272727272727273,"inc"
"247",5.48479693349065,"cor",0.318181818181818,"cor"
"248",5.62762111369064,"inc",0.363636363636364,"cor"
"249",6.78784498230958,"cor",0.409090909090909,"inc"
"250",6.11809719804135,"cor",0.454545454545454,"cor"
"251",6.39526159811545,"cor",0.5,"cor"
"252",6.45519856334012,"cor",0.545454545454545,"cor"
"253",6.10924758276437,"cor",0.590909090909091,"cor"
"254",6.27852142416584,"inc",0.636363636363636,"cor"
"255",6.29894924685594,"cor",0.590909090909091,"inc"
"256",6.20859002609663,"cor",0.545454545454545,"cor"
"257",6.25190388316589,"cor",0.5,"cor"
"258",6.18826412308259,"cor",0.454545454545454,"cor"
"259",6.04263283368238,"cor",0.409090909090909,"cor"
"260",6.00388706710654,"inc",0.363636363636364,"cor"
"261",6.59578051396131,"cor",0.318181818181818,"inc"
"262",6.39859493453521,"cor",0.272727272727273,"cor"
"263",6.74405918631135,"cor",0.227272727272727,"cor"
"264",5.39816270151775,"inc",0.181818181818182,"cor"
"265",5.41164605185504,"cor",0.136363636363636,"inc"
"266",5.41610040220442,"cor",0.0909090909090909,"cor"
"267",5.71373280550937,"inc",0.0454545454545455,"cor"
"268",5.78382518232974,"cor",0,"inc"
"269",5.64897423816121,"inc",0,"cor"
"270",5.89164421182577,"inc",0.0454545454545455,"inc"
"271",5.42934562895444,"cor",0.0909090909090909,"inc"
"272",5.22035582507832,"cor",0.136363636363636,"cor"
"273",5.60947179518496,"inc",0.181818181818182,"cor"
"274",5.56068163101553,"cor",0.227272727272727,"inc"
"275",5.50533153593236,"inc",0.272727272727273,"cor"
"276",5.37063802812766,"inc",0.318181818181818,"inc"
"277",6.67456139181443,"cor",0.363636363636364,"inc"
"278",6.08221891037645,"cor",0.409090909090909,"cor"
"279",6.18826412308259,"cor",0.454545454545454,"cor"
"280",6.75925527066369,"inc",0.5,"cor"
"281",6.31716468674728,"cor",0.545454545454545,"inc"
"282",6.48004456192665,"cor",0.590909090909091,"cor"
"283",6.38012253689976,"cor",0.636363636363636,"cor"
"284",6.94793706861497,"cor",0.681818181818182,"cor"
"285",6.73696695800186,"cor",0.727272727272727,"cor"
"286",6.81124437860129,"cor",0.772727272727273,"cor"
"287",6.36302810354046,"cor",0.727272727272727,"cor"
"288",6.3919171133926,"cor",0.681818181818182,"cor"
"289",6.40191719672719,"cor",0.636363636363636,"cor"
"290",6.2841341610708,"cor",0.590909090909091,"cor"
"291",6.74523634948436,"cor",0.545454545454545,"cor"
"292",6.2363695902037,"cor",0.5,"cor"
"293",6.46614472423762,"cor",0.454545454545454,"cor"
"294",6.23441072571837,"cor",0.409090909090909,"cor"
"295",6.29526600143965,"cor",0.363636363636364,"cor"
"296",6.3750248198281,"cor",0.318181818181818,"cor"
"297",6.61069604471776,"cor",0.272727272727273,"cor"
"298",6.44888939414686,"cor",0.227272727272727,"cor"
"299",5.41164605185504,"inc",0.181818181818182,"cor"
"300",5.47646355193151,"cor",0.136363636363636,"inc"
"301",5.50125821054473,"cor",0.0909090909090909,"cor"
"302",5.41610040220442,"inc",0.0454545454545455,"cor"
"303",6.34738920965601,"cor",0,"inc"
"304",5.30330490805908,"cor",0,"cor"
"305",5.2257466737132,"inc",0,"cor"
"306",5.49306144334055,"inc",0,"inc"
"307",5.44673737166631,"inc",0,"inc"
"308",6.02586597382531,"cor",0,"inc"
"309",5.87211778947542,"cor",0.0454545454545455,"cor"
"310",5.34710753071747,"inc",0.0909090909090909,"cor"
"311",5.48893772615669,"cor",0.136363636363636,"inc"
"312",5.48893772615669,"cor",0.181818181818182,"cor"
"313",5.45958551414416,"inc",0.227272727272727,"cor"
"314",5.90536184805457,"cor",0.272727272727273,"inc"
"315",5.48479693349065,"cor",0.318181818181818,"cor"
"316",5.47646355193151,"inc",0.363636363636364,"cor"
"317",5.44673737166631,"inc",0.409090909090909,"inc"
"318",5.31320597904179,"inc",0.454545454545454,"inc"
"319",6.25382881157547,"inc",0.5,"inc"
"320",6.2709884318583,"cor",0.545454545454545,"inc"
"321",6.34212141872115,"cor",0.590909090909091,"cor"
"322",6.43133108193348,"cor",0.636363636363636,"cor"
"323",6.2803958389602,"cor",0.681818181818182,"cor"
"324",6.31535800152233,"cor",0.727272727272727,"cor"
"325",6.36818718635049,"cor",0.772727272727273,"cor"
"326",6.06378520868761,"cor",0.727272727272727,"cor"
"327",6.62140565176413,"cor",0.681818181818182,"cor"
"328",6.28785856016178,"cor",0.636363636363636,"cor"
"329",6.26530121273771,"cor",0.590909090909091,"cor"
"330",6.11146733950268,"cor",0.545454545454545,"cor"
"331",6.20455776256869,"cor",0.5,"cor"
"332",6.25190388316589,"inc",0.454545454545454,"cor"
"333",5.57594910314632,"inc",0.409090909090909,"inc"
"334",5.28320372873799,"inc",0.363636363636364,"inc"
"335",5.49306144334055,"inc",0.318181818181818,"inc"
"336",5.4380793089232,"inc",0.272727272727273,"inc"
"337",5.23110861685459,"inc",0.318181818181818,"inc"
"338",5.34233425196481,"cor",0.363636363636364,"inc"
"339",5.43372200355424,"inc",0.409090909090909,"cor"
"340",5.27299955856375,"cor",0.454545454545454,"inc"
"341",5.65948221575962,"cor",0.5,"cor"
"342",5.39362754635236,"inc",0.545454545454545,"cor"
"343",6.289715570909,"cor",0.590909090909091,"inc"
"344",6.02586597382531,"cor",0.636363636363636,"cor"
"345",6.18208490671663,"cor",0.681818181818182,"cor"
"346",6.20050917404269,"cor",0.727272727272727,"cor"
"347",6.45204895443723,"cor",0.772727272727273,"cor"
"348",6.24610676548156,"cor",0.818181818181818,"cor"
"349",6.27852142416584,"cor",0.772727272727273,"cor"
"350",6.24997524225948,"cor",0.727272727272727,"cor"
"351",6.46458830368996,"cor",0.681818181818182,"cor"
"352",6.3491389913798,"cor",0.636363636363636,"cor"
"353",6.28599809450886,"cor",0.590909090909091,"cor"
"354",6.33859407820318,"inc",0.545454545454545,"cor"
"355",6.42971947803914,"cor",0.5,"inc"
"356",6.42162226780652,"cor",0.454545454545454,"cor"
"357",6.24027584517077,"cor",0.409090909090909,"cor"
"358",6.21860011969173,"cor",0.363636363636364,"cor"
"359",6.26720054854136,"cor",0.318181818181818,"cor"
"360",6.74641212857337,"cor",0.272727272727273,"cor"
"361",5.37989735354046,"cor",0.227272727272727,"cor"
"362",5.44241771052179,"inc",0.181818181818182,"cor"
"363",5.65248918026865,"inc",0.136363636363636,"inc"
"364",5.43372200355424,"cor",0.0909090909090909,"inc"
"365",5.41164605185504,"inc",0.0454545454545455,"cor"
"366",5.4380793089232,"cor",0,"inc"
"367",5.37989735354046,"inc",0.0454545454545455,"cor"
"368",5.26785815906333,"inc",0.0909090909090909,"inc"
"369",5.44241771052179,"inc",0.136363636363636,"inc"
"370",5.37989735354046,"inc",0.181818181818182,"inc"
"371",4.66343909411207,"cor",0.227272727272727,"inc"
"372",5.38449506278909,"cor",0.272727272727273,"cor"
"373",5.40267738187228,"inc",0.318181818181818,"cor"
"374",5.34710753071747,"inc",0.363636363636364,"inc"
"375",5.605802066296,"inc",0.409090909090909,"inc"
"376",6.77764659363512,"cor",0.454545454545454,"inc"
"377",6.24416690066374,"cor",0.5,"cor"
"378",6.19440539110467,"cor",0.545454545454545,"cor"
"379",6.25575004175337,"inc",0.590909090909091,"cor"
"380",6.49828214947643,"cor",0.636363636363636,"inc"
"381",6.383506634884,"cor",0.681818181818182,"cor"
"382",6.50727771238501,"cor",0.727272727272727,"cor"
"383",6.62406522779989,"cor",0.772727272727273,"cor"
"384",6.33150184989369,"cor",0.818181818181818,"cor"
"385",6.24997524225948,"cor",0.863636363636364,"cor"
"386",6.83733281468559,"cor",0.818181818181818,"cor"
"387",6.2841341610708,"cor",0.772727272727273,"cor"
"388",6.53233429222235,"cor",0.727272727272727,"cor"
"389",6.27664348934165,"cor",0.681818181818182,"cor"
"390",6.68710860786651,"cor",0.636363636363636,"cor"
"391",6.5191472879404,"inc",0.590909090909091,"cor"
"392",6.46925031679577,"inc",0.545454545454545,"inc"
"393",6.8596149036542,"cor",0.5,"inc"
"394",6.18001665365257,"cor",0.454545454545454,"cor"
"395",6.4425401664682,"cor",0.409090909090909,"cor"
"396",6.58479139238572,"cor",0.363636363636364,"cor"
"397",6.34212141872115,"cor",0.318181818181818,"cor"
"398",6.59987049921284,"cor",0.272727272727273,"cor"
"399",6.18208490671663,"cor",0.227272727272727,"cor"
"400",6.36302810354046,"cor",0.181818181818182,"cor"
"401",6.31173480915291,"inc",0.136363636363636,"cor"
"402",6.62140565176413,"cor",0.0909090909090909,"inc"
"403",5.34710753071747,"cor",0.0454545454545455,"cor"
"404",5.34710753071747,"cor",0,"cor"
"405",5.35658627467201,"cor",0,"cor"
"406",5.33271879326537,"cor",0,"cor"
"407",5.30826769740121,"cor",0,"cor"
"408",5.66642668811243,"cor",0.0454545454545455,"cor"
"409",5.42053499927229,"inc",0.0909090909090909,"cor"
"410",5.4249500174814,"cor",0.136363636363636,"inc"
"411",5.4971682252932,"inc",0.181818181818182,"cor"
"412",6.0282785202307,"cor",0.227272727272727,"inc"
"413",6.4281052726846,"cor",0.272727272727273,"cor"
"414",5.38449506278909,"cor",0.318181818181818,"cor"
"415",5.5834963087817,"inc",0.363636363636364,"cor"
"416",6.21460809842219,"cor",0.409090909090909,"inc"
"417",6.17586727010576,"cor",0.454545454545454,"cor"
"418",5.8636311755981,"inc",0.5,"cor"
"419",6.13556489108174,"cor",0.545454545454545,"inc"
"420",6.68085467879022,"cor",0.590909090909091,"cor"
"421",6.28226674689601,"cor",0.636363636363636,"cor"
"422",6.3750248198281,"cor",0.681818181818182,"cor"
"423",6.32435896238131,"cor",0.727272727272727,"cor"
"424",6.3456363608286,"cor",0.772727272727273,"cor"
"425",6.08904487544685,"cor",0.727272727272727,"cor"
"426",6.24027584517077,"cor",0.681818181818182,"cor"
"427",6.18620862390049,"inc",0.636363636363636,"cor"
"428",6.56244409369372,"cor",0.590909090909091,"inc"
"429",7.04925484125584,"cor",0.545454545454545,"cor"
"430",6.39024066706535,"cor",0.5,"cor"
"431",6.5792512120101,"cor",0.454545454545454,"cor"
"432",6.28599809450886,"cor",0.409090909090909,"cor"
"433",6.7719355558396,"cor",0.363636363636364,"cor"
"434",6.48920493132532,"cor",0.318181818181818,"cor"
"435",5.4380793089232,"cor",0.272727272727273,"cor"
"436",6.03068526026126,"cor",0.227272727272727,"cor"
"437",5.42053499927229,"cor",0.181818181818182,"cor"
"438",5.56452040732269,"inc",0.136363636363636,"cor"
"439",5.40717177146012,"cor",0.0909090909090909,"inc"


================================================
FILE: examples/models/SemiSupervisedIOHMM/config.json
================================================
{
    "data_type": "SemiSupervisedIOHMM",
    "properties": {
        "EM_tol": 1e-10,
        "covariates_emissions": [
            []
        ],
        "covariates_initial": [],
        "covariates_transition": [],
        "max_EM_iter": 200,
        "model_emissions": [
            {
                "data_type": "OLS",
                "properties": {}
            }
        ],
        "model_initial": {
            "data_type": "CrossEntropyMNL",
            "properties": {
                "solver": "lbfgs"
            }
        },
        "model_transition": {
            "data_type": "CrossEntropyMNL",
            "properties": {
                "solver": "lbfgs"
            }
        },
        "num_states": 4,
        "responses_emissions": [
            [
                "rt"
            ]
        ]
    }
}

================================================
FILE: examples/models/SemiSupervisedIOHMM/model.json
================================================
{
    "data_type": "SemiSupervisedIOHMM", 
    "properties": {
        "EM_tol": 1e-10, 
        "covariates_emissions": [
            []
        ], 
        "covariates_initial": [], 
        "covariates_transition": [], 
        "max_EM_iter": 200, 
        "model_emissions": [
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ], 
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ], 
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ], 
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ]
        ], 
        "model_initial": {
            "data_type": "CrossEntropyMNL", 
            "properties": {
                "alpha": 0, 
                "coef": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/SemiSupervisedIOHMM/model_initial/coef.npy"
                }, 
                "est_stderr": false, 
                "fit_intercept": true, 
                "l1_ratio": 0, 
                "max_iter": 100, 
                "n_classes": 4, 
                "reg_method": "l2", 
                "solver": "lbfgs", 
                "stderr": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/SemiSupervisedIOHMM/model_initial/stderr.npy"
                }, 
                "tol": 0.0001
            }
        }, 
        "model_transition": [
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_0/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 4, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_0/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }, 
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_1/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 4, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_1/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }, 
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_2/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 4, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_2/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }, 
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_3/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 4, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SemiSupervisedIOHMM/model_transition/state_3/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }
        ], 
        "num_states": 4, 
        "responses_emissions": [
            [
                "rt"
            ]
        ]
    }
}

================================================
FILE: examples/models/SupervisedIOHMM/config.json
================================================
{
    "data_type": "SupervisedIOHMM",
    "properties": {
        "covariates_emissions": [
            []
        ],
        "covariates_initial": [],
        "covariates_transition": [],
        "model_emissions": [
            {
                "data_type": "OLS",
                "properties": {}
            }
        ],
        "model_initial": {
            "data_type": "CrossEntropyMNL",
            "properties": {
                "solver": "lbfgs"
            }
        },
        "model_transition": {
            "data_type": "CrossEntropyMNL",
            "properties": {
                "solver": "lbfgs"
            }
        },
        "num_states": 2,
        "responses_emissions": [
            [
                "rt"
            ]
        ]
    }
}

================================================
FILE: examples/models/SupervisedIOHMM/model.json
================================================
{
    "data_type": "SupervisedIOHMM", 
    "properties": {
        "covariates_emissions": [
            []
        ], 
        "covariates_initial": [], 
        "covariates_transition": [], 
        "model_emissions": [
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_0/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_0/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_0/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ], 
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_1/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_1/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/SupervisedIOHMM/model_emissions/state_1/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ]
        ], 
        "model_initial": {
            "data_type": "CrossEntropyMNL", 
            "properties": {
                "alpha": 0, 
                "coef": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/SupervisedIOHMM/model_initial/coef.npy"
                }, 
                "est_stderr": false, 
                "fit_intercept": true, 
                "l1_ratio": 0, 
                "max_iter": 100, 
                "n_classes": 2, 
                "reg_method": "l2", 
                "solver": "lbfgs", 
                "stderr": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/SupervisedIOHMM/model_initial/stderr.npy"
                }, 
                "tol": 0.0001
            }
        }, 
        "model_transition": [
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SupervisedIOHMM/model_transition/state_0/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 2, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SupervisedIOHMM/model_transition/state_0/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }, 
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SupervisedIOHMM/model_transition/state_1/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 2, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/SupervisedIOHMM/model_transition/state_1/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }
        ], 
        "num_states": 2, 
        "responses_emissions": [
            [
                "rt"
            ]
        ]
    }
}

================================================
FILE: examples/models/UnSupervisedIOHMM/config.json
================================================
{
    "data_type": "UnSupervisedIOHMM",
    "properties": {
        "EM_tol": 1e-06,
        "covariates_emissions": [
            [],
            [
                "Pacc"
            ]
        ],
        "covariates_initial": [],
        "covariates_transition": [],
        "max_EM_iter": 200,
        "model_emissions": [
            {
                "data_type": "OLS",
                "properties": {
                    "est_stderr": true
                }
            },
            {
                "data_type": "DiscreteMNL",
                "properties": {}
            }
        ],
        "model_initial": {
            "data_type": "CrossEntropyMNL",
            "properties": {}
        },
        "model_transition": {
            "data_type": "CrossEntropyMNL",
            "properties": {}
        },
        "num_states": 2,
        "responses_emissions": [
            [
                "rt"
            ],
            [
                "corr"
            ]
        ]
    }
}

================================================
FILE: examples/models/UnSupervisedIOHMM/model.json
================================================
{
    "data_type": "UnSupervisedIOHMM", 
    "properties": {
        "EM_tol": 1e-06, 
        "covariates_emissions": [
            [], 
            [
                "Pacc"
            ]
        ], 
        "covariates_initial": [], 
        "covariates_transition": [], 
        "max_EM_iter": 200, 
        "model_emissions": [
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": true, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }, 
                {
                    "data_type": "DiscreteMNL", 
                    "properties": {
                        "alpha": 0, 
                        "classes": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_1/classes.npy"
                        }, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_1/coef.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "reg_method": "l2", 
                        "solver": "lbfgs", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_0/emission_1/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ], 
            [
                {
                    "data_type": "OLS", 
                    "properties": {
                        "alpha": 0, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_0/coef.npy"
                        }, 
                        "dispersion": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_0/dispersion.npy"
                        }, 
                        "est_stderr": true, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "n_targets": 1, 
                        "reg_method": null, 
                        "solver": "svd", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_0/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }, 
                {
                    "data_type": "DiscreteMNL", 
                    "properties": {
                        "alpha": 0, 
                        "classes": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_1/classes.npy"
                        }, 
                        "coef": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_1/coef.npy"
                        }, 
                        "est_stderr": false, 
                        "fit_intercept": true, 
                        "l1_ratio": 0, 
                        "max_iter": 100, 
                        "reg_method": "l2", 
                        "solver": "lbfgs", 
                        "stderr": {
                            "data_type": "numpy.ndarray", 
                            "path": "../models/UnSupervisedIOHMM/model_emissions/state_1/emission_1/stderr.npy"
                        }, 
                        "tol": 0.0001
                    }
                }
            ]
        ], 
        "model_initial": {
            "data_type": "CrossEntropyMNL", 
            "properties": {
                "alpha": 0, 
                "coef": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/UnSupervisedIOHMM/model_initial/coef.npy"
                }, 
                "est_stderr": false, 
                "fit_intercept": true, 
                "l1_ratio": 0, 
                "max_iter": 100, 
                "n_classes": 2, 
                "reg_method": "l2", 
                "solver": "lbfgs", 
                "stderr": {
                    "data_type": "numpy.ndarray", 
                    "path": "../models/UnSupervisedIOHMM/model_initial/stderr.npy"
                }, 
                "tol": 0.0001
            }
        }, 
        "model_transition": [
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/UnSupervisedIOHMM/model_transition/state_0/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 2, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/UnSupervisedIOHMM/model_transition/state_0/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }, 
            {
                "data_type": "CrossEntropyMNL", 
                "properties": {
                    "alpha": 0, 
                    "coef": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/UnSupervisedIOHMM/model_transition/state_1/coef.npy"
                    }, 
                    "est_stderr": false, 
                    "fit_intercept": true, 
                    "l1_ratio": 0, 
                    "max_iter": 100, 
                    "n_classes": 2, 
                    "reg_method": "l2", 
                    "solver": "lbfgs", 
                    "stderr": {
                        "data_type": "numpy.ndarray", 
                        "path": "../models/UnSupervisedIOHMM/model_transition/state_1/stderr.npy"
                    }, 
                    "tol": 0.0001
                }
            }
        ], 
        "num_states": 2, 
        "responses_emissions": [
            [
                "rt"
            ], 
            [
                "corr"
            ]
        ]
    }
}

================================================
FILE: examples/notebooks/SemiSupervisedIOHMM.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is the IOHMM model with the parameters learned in a semi-supervised way. By using some labeled data, we force the learning process in a certain direction. The unlabeled data will be estimated using EM algorithm iteratively. See notes in http://pages.cs.wisc.edu/~jerryzhu/pub/sslicml07.pdf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SemiSupervisedIOHMM "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from __future__ import  division\n",
    "\n",
    "import json\n",
    "import warnings\n",
    "\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "from IOHMM import SemiSupervisedIOHMM\n",
    "from IOHMM import OLS, CrossEntropyMNL\n",
    "\n",
    "\n",
    "warnings.simplefilter(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load speed data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Unnamed: 0</th>\n",
       "      <th>rt</th>\n",
       "      <th>corr</th>\n",
       "      <th>Pacc</th>\n",
       "      <th>prev</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>6.456770</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>5.602119</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>6.253829</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>5.451038</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>5.872118</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Unnamed: 0        rt corr  Pacc prev\n",
       "0           1  6.456770  cor   0.0  inc\n",
       "1           2  5.602119  cor   0.0  cor\n",
       "2           3  6.253829  inc   0.0  cor\n",
       "3           4  5.451038  inc   0.0  inc\n",
       "4           5  5.872118  inc   0.0  inc"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "speed = pd.read_csv('../data/speed.csv')\n",
    "speed.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Label some states"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In our structure of the code, the states should be a dictionary, the key is the index in the sequence (e.g. 0, 5) and the value is a one-out-of-n code of array where the kth value is 1 if the hidden state is k. n is the number of states in total.\n",
    "\n",
    "In the following example, we assume that the \"corr\" column gives the correct hidden states. Here we assume only the first half of the sequence is labeled.\n",
    "\n",
    "To make sure that the semi supervised model works, we set the value of 'rt' in state 0 as 0 and we set the value of 'rt' in state 1 as 1, the other values are not changed. So after training, we should be able to see 4 states clearly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "states = {}\n",
    "corr = np.array(speed['corr'])\n",
    "for i in range(int(len(corr)/2)):\n",
    "    state = np.zeros((4,))\n",
    "    if corr[i] == 'cor':\n",
    "        states[i] = np.array([0,1,0,0])\n",
    "        speed.at[i, 'rt'] = 1\n",
    "    else:\n",
    "        states[i] = np.array([1,0,0,0])\n",
    "        speed.at[i, 'rt'] = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "439\n",
      "219\n"
     ]
    }
   ],
   "source": [
    "print(speed.shape[0])\n",
    "print(len(states))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 0 0]\n",
      "[0 1 0 0]\n",
      "[1 0 0 0]\n",
      "[1 0 0 0]\n",
      "[1 0 0 0]\n"
     ]
    }
   ],
   "source": [
    "for t in range(5):\n",
    "    print(states[t])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up a simple model manully"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# there should be 4 hidden states in this model\n",
    "SHMM = SemiSupervisedIOHMM(num_states=4, max_EM_iter=200, EM_tol=1e-10)\n",
    "\n",
    "# we set only one output 'rt' modeled by a linear regression model\n",
    "SHMM.set_models(model_emissions = [OLS()], \n",
    "                model_transition=CrossEntropyMNL(solver='lbfgs'),\n",
    "                model_initial=CrossEntropyMNL(solver='lbfgs'))\n",
    "\n",
    "# we set no covariates associated with initial/transitiojn/emission models\n",
    "SHMM.set_inputs(covariates_initial = [], covariates_transition = [], covariates_emissions = [[]])\n",
    "\n",
    "# set the response of the emission model\n",
    "SHMM.set_outputs([['rt']])\n",
    "\n",
    "# set the data and ground truth states\n",
    "SHMM.set_data([[speed, states]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Start training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "SHMM.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See the training results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.]]\n",
      "[[ 1.]]\n",
      "[[ 6.38975526]]\n",
      "[[ 5.47039844]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "# clearly the enforcement worked since the coefficient of the first two states are 0, and 1\n",
    "print(SHMM.model_emissions[0][0].coef)\n",
    "print(SHMM.model_emissions[1][0].coef)\n",
    "print(SHMM.model_emissions[2][0].coef)\n",
    "print(SHMM.model_emissions[3][0].coef)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.]]\n",
      "[[  1.66533454e-15]]\n",
      "[[ 0.22536249]]\n",
      "[[ 0.17915255]]\n"
     ]
    }
   ],
   "source": [
    "# the scale/dispersion of the output model of each states\n",
    "# since we know the first two states perfectly, the dispersions are 0.\n",
    "print(np.sqrt(SHMM.model_emissions[0][0].dispersion))\n",
    "print(np.sqrt(SHMM.model_emissions[1][0].dispersion))\n",
    "print(np.sqrt(SHMM.model_emissions[2][0].dispersion))\n",
    "print(np.sqrt(SHMM.model_emissions[3][0].dispersion))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[  4.03845430e-01   5.96154225e-01   1.72468132e-07   1.72468132e-07]]\n",
      "[[  1.85628510e-01   8.08383511e-01   8.78709574e-10   5.98797866e-03]]\n",
      "[[  1.74937041e-07   1.74937041e-07   9.27082453e-01   7.29171969e-02]]\n",
      "[[  3.41629345e-08   3.41629345e-08   1.11257897e-01   8.88742035e-01]]\n"
     ]
    }
   ],
   "source": [
    "# the transition probability from each state\n",
    "print(np.exp(SHMM.model_transition[0].predict_log_proba(np.array([[]]))))\n",
    "print(np.exp(SHMM.model_transition[1].predict_log_proba(np.array([[]]))))\n",
    "print(np.exp(SHMM.model_transition[2].predict_log_proba(np.array([[]]))))\n",
    "print(np.exp(SHMM.model_transition[3].predict_log_proba(np.array([[]]))))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Save the trained model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'data_type': 'SemiSupervisedIOHMM',\n",
       " 'properties': {'EM_tol': 1e-10,\n",
       "  'covariates_emissions': [[]],\n",
       "  'covariates_initial': [],\n",
       "  'covariates_transition': [],\n",
       "  'max_EM_iter': 200,\n",
       "  'model_emissions': [[{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_0/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}],\n",
       "   [{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_1/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}],\n",
       "   [{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_2/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}],\n",
       "   [{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SemiSupervisedIOHMM/model_emissions/state_3/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}]],\n",
       "  'model_initial': {'data_type': 'CrossEntropyMNL',\n",
       "   'properties': {'alpha': 0,\n",
       "    'coef': {'data_type': 'numpy.ndarray',\n",
       "     'path': '../models/SemiSupervisedIOHMM/model_initial/coef.npy'},\n",
       "    'est_stderr': False,\n",
       "    'fit_intercept': True,\n",
       "    'l1_ratio': 0,\n",
       "    'max_iter': 100,\n",
       "    'n_classes': 4,\n",
       "    'reg_method': 'l2',\n",
       "    'solver': 'lbfgs',\n",
       "    'stderr': {'data_type': 'numpy.ndarray',\n",
       "     'path': '../models/SemiSupervisedIOHMM/model_initial/stderr.npy'},\n",
       "    'tol': 0.0001}},\n",
       "  'model_transition': [{'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_0/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 4,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_0/stderr.npy'},\n",
       "     'tol': 0.0001}},\n",
       "   {'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_1/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 4,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_1/stderr.npy'},\n",
       "     'tol': 0.0001}},\n",
       "   {'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_2/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 4,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_2/stderr.npy'},\n",
       "     'tol': 0.0001}},\n",
       "   {'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_3/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 4,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SemiSupervisedIOHMM/model_transition/state_3/stderr.npy'},\n",
       "     'tol': 0.0001}}],\n",
       "  'num_states': 4,\n",
       "  'responses_emissions': [['rt']]}}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "json_dict = SHMM.to_json('../models/SemiSupervisedIOHMM/')\n",
    "json_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "with open('../models/SemiSupervisedIOHMM/model.json', 'w') as outfile:\n",
    "    json.dump(json_dict, outfile, indent=4, sort_keys=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load back the trained model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SHMM_from_json = SemiSupervisedIOHMM.from_json(json_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See if the coefficients are any different"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.]]\n",
      "[[ 1.]]\n",
      "[[ 6.38975526]]\n",
      "[[ 5.47039844]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "# clearly the enforcement worked since the coefficient of the first two states are 0, and 1\n",
    "print(SHMM_from_json.model_emissions[0][0].coef)\n",
    "print(SHMM_from_json.model_emissions[1][0].coef)\n",
    "print(SHMM_from_json.model_emissions[2][0].coef)\n",
    "print(SHMM_from_json.model_emissions[3][0].coef)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up the model using a config file, instead of doing it manully"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "with open('../models/SemiSupervisedIOHMM/config.json') as json_data:\n",
    "    json_dict = json.load(json_data)\n",
    "\n",
    "SHMM_from_config = SemiSupervisedIOHMM.from_config(json_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set data and start training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SHMM_from_config.set_data([[speed, states]])\n",
    "SHMM_from_config.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See if the training results are any different?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.]]\n",
      "[[ 1.]]\n",
      "[[ 6.38975526]]\n",
      "[[ 5.47039844]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "# clearly the enforcement worked since the coefficient of the first two states are 0, and 1\n",
    "print(SHMM_from_config.model_emissions[0][0].coef)\n",
    "print(SHMM_from_config.model_emissions[1][0].coef)\n",
    "print(SHMM_from_config.model_emissions[2][0].coef)\n",
    "print(SHMM_from_config.model_emissions[3][0].coef)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}


================================================
FILE: examples/notebooks/SupervisedIOHMM.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is the IOHMM model with the parameters learned in a supervised way. This is corresponding to the counting frequency process as in the supervised HMM. See notes in http://www.cs.columbia.edu/4761/notes07/chapter4.3-HMM.pdf."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SupervisedIOHMM "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from __future__ import  division\n",
    "\n",
    "import json\n",
    "import warnings\n",
    "\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "from IOHMM import SupervisedIOHMM\n",
    "from IOHMM import OLS, CrossEntropyMNL\n",
    "\n",
    "\n",
    "warnings.simplefilter(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load speed data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Unnamed: 0</th>\n",
       "      <th>rt</th>\n",
       "      <th>corr</th>\n",
       "      <th>Pacc</th>\n",
       "      <th>prev</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>6.456770</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>5.602119</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>6.253829</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>5.451038</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>5.872118</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Unnamed: 0        rt corr  Pacc prev\n",
       "0           1  6.456770  cor   0.0  inc\n",
       "1           2  5.602119  cor   0.0  cor\n",
       "2           3  6.253829  inc   0.0  cor\n",
       "3           4  5.451038  inc   0.0  inc\n",
       "4           5  5.872118  inc   0.0  inc"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "speed = pd.read_csv('../data/speed.csv')\n",
    "speed.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Label some/all states"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In our structure of the code, the states should be a dictionary, the key is the index in the sequence (e.g. 0, 5) and the value is a one-out-of-n code of array where the kth value is 1 if the hidden state is k. n is the number of states in total.\n",
    "\n",
    "In the following example, we assume that the \"corr\" column gives the correct hidden states."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "states = {}\n",
    "corr = np.array(speed['corr'])\n",
    "for i in range(len(corr)):\n",
    "    state = np.zeros((2,))\n",
    "    if corr[i] == 'cor':\n",
    "        states[i] = np.array([0,1])\n",
    "    else:\n",
    "        states[i] = np.array([1,0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up a simple model manully"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# we choose 2 hidden states in this model\n",
    "SHMM = SupervisedIOHMM(num_states=2)\n",
    "\n",
    "# we set only one output 'rt' modeled by a linear regression model\n",
    "SHMM.set_models(model_emissions = [OLS()], \n",
    "                model_transition=CrossEntropyMNL(solver='lbfgs'),\n",
    "                model_initial=CrossEntropyMNL(solver='lbfgs'))\n",
    "\n",
    "# we set no covariates associated with initial/transitiojn/emission models\n",
    "SHMM.set_inputs(covariates_initial = [], covariates_transition = [], covariates_emissions = [[]])\n",
    "\n",
    "# set the response of the emission model\n",
    "SHMM.set_outputs([['rt']])\n",
    "\n",
    "# set the data and ground truth states\n",
    "SHMM.set_data([[speed, states]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Start training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true,
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "SHMM.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See the training results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 5.70451774]]\n",
      "[[ 6.13678825]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "print(SHMM.model_emissions[0][0].coef)\n",
    "print(SHMM.model_emissions[1][0].coef)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.35831781]]\n",
      "[[ 0.47356034]]\n"
     ]
    }
   ],
   "source": [
    "# the scale/dispersion of the output model of each states\n",
    "print(np.sqrt(SHMM.model_emissions[0][0].dispersion))\n",
    "print(np.sqrt(SHMM.model_emissions[1][0].dispersion))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.38392857  0.61607143]]\n",
      "[[ 0.21165647  0.78834353]]\n"
     ]
    }
   ],
   "source": [
    "# the transition probability from each state\n",
    "print(np.exp(SHMM.model_transition[0].predict_log_proba(np.array([[]]))))\n",
    "print(np.exp(SHMM.model_transition[1].predict_log_proba(np.array([[]]))))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Save the trained model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'data_type': 'SupervisedIOHMM',\n",
       " 'properties': {'covariates_emissions': [[]],\n",
       "  'covariates_initial': [],\n",
       "  'covariates_transition': [],\n",
       "  'model_emissions': [[{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_0/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_0/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_0/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}],\n",
       "   [{'data_type': 'OLS',\n",
       "     'properties': {'alpha': 0,\n",
       "      'coef': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_1/emission_0/coef.npy'},\n",
       "      'dispersion': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_1/emission_0/dispersion.npy'},\n",
       "      'est_stderr': False,\n",
       "      'fit_intercept': True,\n",
       "      'l1_ratio': 0,\n",
       "      'max_iter': 100,\n",
       "      'n_targets': 1,\n",
       "      'reg_method': None,\n",
       "      'solver': 'svd',\n",
       "      'stderr': {'data_type': 'numpy.ndarray',\n",
       "       'path': '../models/SupervisedIOHMM/model_emissions/state_1/emission_0/stderr.npy'},\n",
       "      'tol': 0.0001}}]],\n",
       "  'model_initial': {'data_type': 'CrossEntropyMNL',\n",
       "   'properties': {'alpha': 0,\n",
       "    'coef': {'data_type': 'numpy.ndarray',\n",
       "     'path': '../models/SupervisedIOHMM/model_initial/coef.npy'},\n",
       "    'est_stderr': False,\n",
       "    'fit_intercept': True,\n",
       "    'l1_ratio': 0,\n",
       "    'max_iter': 100,\n",
       "    'n_classes': 2,\n",
       "    'reg_method': 'l2',\n",
       "    'solver': 'lbfgs',\n",
       "    'stderr': {'data_type': 'numpy.ndarray',\n",
       "     'path': '../models/SupervisedIOHMM/model_initial/stderr.npy'},\n",
       "    'tol': 0.0001}},\n",
       "  'model_transition': [{'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SupervisedIOHMM/model_transition/state_0/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 2,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SupervisedIOHMM/model_transition/state_0/stderr.npy'},\n",
       "     'tol': 0.0001}},\n",
       "   {'data_type': 'CrossEntropyMNL',\n",
       "    'properties': {'alpha': 0,\n",
       "     'coef': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SupervisedIOHMM/model_transition/state_1/coef.npy'},\n",
       "     'est_stderr': False,\n",
       "     'fit_intercept': True,\n",
       "     'l1_ratio': 0,\n",
       "     'max_iter': 100,\n",
       "     'n_classes': 2,\n",
       "     'reg_method': 'l2',\n",
       "     'solver': 'lbfgs',\n",
       "     'stderr': {'data_type': 'numpy.ndarray',\n",
       "      'path': '../models/SupervisedIOHMM/model_transition/state_1/stderr.npy'},\n",
       "     'tol': 0.0001}}],\n",
       "  'num_states': 2,\n",
       "  'responses_emissions': [['rt']]}}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "json_dict = SHMM.to_json('../models/SupervisedIOHMM/')\n",
    "json_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "with open('../models/SupervisedIOHMM/model.json', 'w') as outfile:\n",
    "    json.dump(json_dict, outfile, indent=4, sort_keys=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load back the trained model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SHMM_from_json = SupervisedIOHMM.from_json(json_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See if the coefficients are any different"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 5.70451774]]\n",
      "[[ 6.13678825]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "print(SHMM.model_emissions[0][0].coef)\n",
    "print(SHMM.model_emissions[1][0].coef)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set up the model using a config file, instead of doing it manully"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "with open('../models/SupervisedIOHMM/config.json') as json_data:\n",
    "    json_dict = json.load(json_data)\n",
    "\n",
    "SHMM_from_config = SupervisedIOHMM.from_config(json_dict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set data and start training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SHMM_from_config.set_data([[speed, states]])\n",
    "SHMM_from_config.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## See if the training results are any different?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 5.70451774]]\n",
      "[[ 6.13678825]]\n"
     ]
    }
   ],
   "source": [
    "# the coefficients of the output model for each states\n",
    "print(SHMM_from_config.model_emissions[0][0].coef)\n",
    "print(SHMM_from_config.model_emissions[1][0].coef)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}


================================================
FILE: examples/notebooks/UnSupervisedIOHMM.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# UnSupervisedIOHMM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from __future__ import  division\n",
    "\n",
    "import json\n",
    "import warnings\n",
    "\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "\n",
    "from IOHMM import UnSupervisedIOHMM\n",
    "from IOHMM import OLS, DiscreteMNL, CrossEntropyMNL\n",
    "\n",
    "\n",
    "warnings.simplefilter(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load speed data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style>\n",
       "    .dataframe thead tr:only-child th {\n",
       "        text-align: right;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Unnamed: 0</th>\n",
       "      <th>rt</th>\n",
       "      <th>corr</th>\n",
       "      <th>Pacc</th>\n",
       "      <th>prev</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>6.456770</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>5.602119</td>\n",
       "      <td>cor</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>6.253829</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>cor</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>5.451038</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>5.872118</td>\n",
       "      <td>inc</td>\n",
       "      <td>0.0</td>\n",
       "      <td>inc</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   Unnamed: 0        rt corr  Pacc prev\n",
       "0           1  6.456770  cor   0.0  inc\n",
       "1           2  5.602119  cor   0.0  cor\n",
       "2           3  6.253829  inc   0.0  cor\n",
       "3     
Download .txt
gitextract_fm43qu58/

├── .gitignore
├── .travis.yml
├── IOHMM/
│   ├── IOHMM.py
│   ├── __init__.py
│   ├── forward_backward.py
│   └── linear_models.py
├── LICENCE
├── README.md
├── examples/
│   ├── data/
│   │   └── speed.csv
│   ├── models/
│   │   ├── SemiSupervisedIOHMM/
│   │   │   ├── config.json
│   │   │   ├── model.json
│   │   │   ├── model_emissions/
│   │   │   │   ├── state_0/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   ├── state_1/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   ├── state_2/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   └── state_3/
│   │   │   │       └── emission_0/
│   │   │   │           ├── coef.npy
│   │   │   │           ├── dispersion.npy
│   │   │   │           └── stderr.npy
│   │   │   ├── model_initial/
│   │   │   │   ├── coef.npy
│   │   │   │   └── stderr.npy
│   │   │   └── model_transition/
│   │   │       ├── state_0/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       ├── state_1/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       ├── state_2/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       └── state_3/
│   │   │           ├── coef.npy
│   │   │           └── stderr.npy
│   │   ├── SupervisedIOHMM/
│   │   │   ├── config.json
│   │   │   ├── model.json
│   │   │   ├── model_emissions/
│   │   │   │   ├── state_0/
│   │   │   │   │   └── emission_0/
│   │   │   │   │       ├── coef.npy
│   │   │   │   │       ├── dispersion.npy
│   │   │   │   │       └── stderr.npy
│   │   │   │   └── state_1/
│   │   │   │       └── emission_0/
│   │   │   │           ├── coef.npy
│   │   │   │           ├── dispersion.npy
│   │   │   │           └── stderr.npy
│   │   │   ├── model_initial/
│   │   │   │   ├── coef.npy
│   │   │   │   └── stderr.npy
│   │   │   └── model_transition/
│   │   │       ├── state_0/
│   │   │       │   ├── coef.npy
│   │   │       │   └── stderr.npy
│   │   │       └── state_1/
│   │   │           ├── coef.npy
│   │   │           └── stderr.npy
│   │   └── UnSupervisedIOHMM/
│   │       ├── config.json
│   │       ├── model.json
│   │       ├── model_emissions/
│   │       │   ├── state_0/
│   │       │   │   ├── emission_0/
│   │       │   │   │   ├── coef.npy
│   │       │   │   │   ├── dispersion.npy
│   │       │   │   │   └── stderr.npy
│   │       │   │   └── emission_1/
│   │       │   │       ├── classes.npy
│   │       │   │       ├── coef.npy
│   │       │   │       └── stderr.npy
│   │       │   └── state_1/
│   │       │       ├── emission_0/
│   │       │       │   ├── coef.npy
│   │       │       │   ├── dispersion.npy
│   │       │       │   └── stderr.npy
│   │       │       └── emission_1/
│   │       │           ├── classes.npy
│   │       │           ├── coef.npy
│   │       │           └── stderr.npy
│   │       ├── model_initial/
│   │       │   ├── coef.npy
│   │       │   └── stderr.npy
│   │       └── model_transition/
│   │           ├── state_0/
│   │           │   ├── coef.npy
│   │           │   └── stderr.npy
│   │           └── state_1/
│   │               ├── coef.npy
│   │               └── stderr.npy
│   └── notebooks/
│       ├── SemiSupervisedIOHMM.ipynb
│       ├── SupervisedIOHMM.ipynb
│       └── UnSupervisedIOHMM.ipynb
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests/
    ├── IOHMM_models/
    │   ├── SemiSupervisedIOHMM/
    │   │   ├── model.json
    │   │   ├── model_emissions/
    │   │   │   ├── state_0/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   ├── state_1/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   ├── state_2/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   └── state_3/
    │   │   │       └── emission_0/
    │   │   │           ├── coef.npy
    │   │   │           ├── dispersion.npy
    │   │   │           └── stderr.npy
    │   │   ├── model_initial/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── model_transition/
    │   │       ├── state_0/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       ├── state_1/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       ├── state_2/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       └── state_3/
    │   │           ├── coef.npy
    │   │           └── stderr.npy
    │   ├── SupervisedIOHMM/
    │   │   ├── model.json
    │   │   ├── model_emissions/
    │   │   │   ├── state_0/
    │   │   │   │   └── emission_0/
    │   │   │   │       ├── coef.npy
    │   │   │   │       ├── dispersion.npy
    │   │   │   │       └── stderr.npy
    │   │   │   └── state_1/
    │   │   │       └── emission_0/
    │   │   │           ├── coef.npy
    │   │   │           ├── dispersion.npy
    │   │   │           └── stderr.npy
    │   │   ├── model_initial/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── model_transition/
    │   │       ├── state_0/
    │   │       │   ├── coef.npy
    │   │       │   └── stderr.npy
    │   │       └── state_1/
    │   │           ├── coef.npy
    │   │           └── stderr.npy
    │   └── UnSupervisedIOHMM/
    │       ├── model.json
    │       ├── model_emissions/
    │       │   ├── state_0/
    │       │   │   ├── emission_0/
    │       │   │   │   ├── coef.npy
    │       │   │   │   ├── dispersion.npy
    │       │   │   │   └── stderr.npy
    │       │   │   └── emission_1/
    │       │   │       ├── classes.npy
    │       │   │       ├── coef.npy
    │       │   │       └── stderr.npy
    │       │   └── state_1/
    │       │       ├── emission_0/
    │       │       │   ├── coef.npy
    │       │       │   ├── dispersion.npy
    │       │       │   └── stderr.npy
    │       │       └── emission_1/
    │       │           ├── classes.npy
    │       │           ├── coef.npy
    │       │           └── stderr.npy
    │       ├── model_initial/
    │       │   ├── coef.npy
    │       │   └── stderr.npy
    │       └── model_transition/
    │           ├── state_0/
    │           │   ├── coef.npy
    │           │   └── stderr.npy
    │           └── state_1/
    │               ├── coef.npy
    │               └── stderr.npy
    ├── __init__.py
    ├── linear_models/
    │   ├── CrossentropyMNL/
    │   │   ├── Binary/
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── Multinomial/
    │   │       ├── coef.npy
    │   │       └── stderr.npy
    │   ├── DiscreteMNL/
    │   │   ├── Binary/
    │   │   │   ├── classes.npy
    │   │   │   ├── coef.npy
    │   │   │   └── stderr.npy
    │   │   └── Multinomial/
    │   │       ├── classes.npy
    │   │       ├── coef.npy
    │   │       └── stderr.npy
    │   ├── GLM/
    │   │   ├── Binomial/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── Gamma/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── Gaussian/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   ├── InverseGaussian/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   ├── inv_gaussian.csv
    │   │   │   └── stderr.npy
    │   │   ├── NegativeBinomial/
    │   │   │   ├── coef.npy
    │   │   │   ├── dispersion.npy
    │   │   │   ├── family.p
    │   │   │   └── stderr.npy
    │   │   └── Poisson/
    │   │       ├── coef.npy
    │   │       ├── dispersion.npy
    │   │       ├── family.p
    │   │       └── stderr.npy
    │   └── OLS/
    │       ├── MultivariateOLS/
    │       │   ├── coef.npy
    │       │   ├── dispersion.npy
    │       │   └── stderr.npy
    │       └── UnivariateOLS/
    │           ├── coef.npy
    │           ├── dispersion.npy
    │           └── stderr.npy
    ├── test_CrossentropyMNL.py
    ├── test_DiscreteMNL.py
    ├── test_GLM.py
    ├── test_HMM_utils.py
    ├── test_OLS.py
    ├── test_SemiSupervisedIOHMM.py
    ├── test_SupervisedIOHMM.py
    └── test_UnSupervisedIOHMM.py
Download .txt
SYMBOL INDEX (255 symbols across 11 files)

FILE: IOHMM/IOHMM.py
  class LinearModelLoader (line 47) | class LinearModelLoader(object):
  class BaseIOHMM (line 59) | class BaseIOHMM(object):
    method __init__ (line 65) | def __init__(self, num_states=2):
    method set_models (line 75) | def set_models(self, model_emissions,
    method set_inputs (line 116) | def set_inputs(self, covariates_initial, covariates_transition, covari...
    method set_outputs (line 136) | def set_outputs(self, responses_emissions):
    method set_data (line 153) | def set_data(self, dfs):
    method _initialize (line 166) | def _initialize(self, with_randomness=True):
    method E_step (line 320) | def E_step(self):
    method M_step (line 359) | def M_step(self):
    method train (line 393) | def train(self):
    method to_json (line 410) | def to_json(self, path):
    method _from_setup (line 442) | def _from_setup(
    method from_config (line 497) | def from_config(cls, json_dict):
    method from_json (line 526) | def from_json(cls, json_dict):
  class UnSupervisedIOHMM (line 557) | class UnSupervisedIOHMM(BaseIOHMM):
    method __init__ (line 563) | def __init__(self, num_states=2, EM_tol=1e-4, max_EM_iter=100):
    method set_data (line 577) | def set_data(self, dfs):
    method to_json (line 600) | def to_json(self, path):
    method _from_setup (line 620) | def _from_setup(
  class SemiSupervisedIOHMM (line 678) | class SemiSupervisedIOHMM(UnSupervisedIOHMM):
    method set_data (line 685) | def set_data(self, dfs_states):
  class SupervisedIOHMM (line 709) | class SupervisedIOHMM(BaseIOHMM):
    method __init__ (line 716) | def __init__(self, num_states=2):
    method set_data (line 728) | def set_data(self, dfs_states):

FILE: IOHMM/forward_backward.py
  function forward_backward (line 26) | def forward_backward(log_prob_initial, log_prob_transition, log_Ey, log_...
  function forward (line 64) | def forward(log_prob_initial, log_prob_transition, log_Ey, log_state={}):
  function backward (line 112) | def backward(log_prob_transition, log_Ey, log_state={}):
  function cal_log_likelihood (line 150) | def cal_log_likelihood(log_alpha):
  function cal_log_gamma (line 166) | def cal_log_gamma(log_alpha, log_beta, log_likelihood, log_state={}):
  function cal_log_epsilon (line 197) | def cal_log_epsilon(log_prob_transition, log_Ey, log_alpha, log_beta, lo...

FILE: IOHMM/linear_models.py
  class BaseModel (line 65) | class BaseModel(object):
    method __init__ (line 71) | def __init__(self,
    method fit (line 110) | def fit(self, X, Y, sample_weight=None):
    method _raise_error_if_model_not_trained (line 122) | def _raise_error_if_model_not_trained(self):
    method _raise_error_if_sample_weight_sum_zero (line 130) | def _raise_error_if_sample_weight_sum_zero(self, sample_weight):
    method _transform_X (line 139) | def _transform_X(self, X):
    method _transform_sample_weight (line 152) | def _transform_sample_weight(self, X, sample_weight=None):
    method _transform_X_sample_weight (line 169) | def _transform_X_sample_weight(self, X, sample_weight=None):
    method predict (line 184) | def predict(self, X):
    method loglike_per_sample (line 195) | def loglike_per_sample(self, X, Y):
    method loglike (line 210) | def loglike(self, X, Y, sample_weight=None):
    method to_json (line 227) | def to_json(self, path):
    method _from_json (line 267) | def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
    method from_json (line 296) | def from_json(cls, json_dict):
  class GLM (line 321) | class GLM(BaseModel):
    method __init__ (line 328) | def __init__(self,
    method fit (line 374) | def fit(self, X, Y, sample_weight=None):
    method _transform_Y (line 430) | def _transform_Y(self, Y):
    method predict (line 443) | def predict(self, X):
    method loglike_per_sample (line 456) | def loglike_per_sample(self, X, Y):
    method to_json (line 482) | def to_json(self, path):
    method _from_json (line 513) | def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
  class OLS (line 545) | class OLS(BaseModel):
    method __init__ (line 550) | def __init__(self, solver='svd', fit_intercept=True, est_stderr=False,
    method _pick_model (line 586) | def _pick_model(self):
    method fit (line 608) | def fit(self, X, Y, sample_weight=None):
    method _transform_Y (line 673) | def _transform_Y(self, Y):
    method predict (line 686) | def predict(self, X):
    method loglike_per_sample (line 699) | def loglike_per_sample(self, X, Y):
    method to_json (line 743) | def to_json(self, path):
    method _from_json (line 768) | def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
  class BaseMNL (line 800) | class BaseMNL(BaseModel):
    method __init__ (line 808) | def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
    method _pick_model (line 850) | def _pick_model(self):
    method fit (line 869) | def fit(self, X, Y, sample_weight=None):
    method _label_encoder (line 928) | def _label_encoder(X, Y, sample_weight):
    method _label_decoder (line 950) | def _label_decoder(self, Y):
    method predict_log_proba (line 964) | def predict_log_proba(self, X):
    method predict (line 980) | def predict(self, X):
    method loglike_per_sample (line 995) | def loglike_per_sample(self, X, Y):
    method _from_json_MNL (line 1018) | def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,
    method _from_json (line 1047) | def _from_json(cls, json_dict, solver, fit_intercept, est_stderr,
  class DiscreteMNL (line 1077) | class DiscreteMNL(BaseMNL):
    method __init__ (line 1082) | def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
    method _label_encoder (line 1117) | def _label_encoder(X, Y, sample_weight):
    method _label_decoder (line 1137) | def _label_decoder(self, Y):
    method to_json (line 1162) | def to_json(self, path):
    method _from_json_MNL (line 1186) | def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,
  class CrossEntropyMNL (line 1218) | class CrossEntropyMNL(BaseMNL):
    method __init__ (line 1223) | def __init__(self, solver='lbfgs', fit_intercept=True, est_stderr=False,
    method _label_encoder (line 1258) | def _label_encoder(X, Y, sample_weight):
    method _label_decoder (line 1284) | def _label_decoder(self, Y):
    method to_json (line 1300) | def to_json(self, path):
    method _from_json_MNL (line 1318) | def _from_json_MNL(cls, json_dict, solver, fit_intercept, est_stderr,

FILE: tests/test_CrossentropyMNL.py
  class CrossEntropyMNLUnaryTests (line 15) | class CrossEntropyMNLUnaryTests(unittest.TestCase):
    method setUpClass (line 18) | def setUpClass(cls):
    method test_label_encoder (line 22) | def test_label_encoder(self):
    method test_lr (line 45) | def test_lr(self):
    method test_lr_sample_weight_all_half (line 66) | def test_lr_sample_weight_all_half(self):
    method test_lr_one_data_point (line 81) | def test_lr_one_data_point(self):
  class CrossEntropyMNLBinaryTests (line 102) | class CrossEntropyMNLBinaryTests(unittest.TestCase):
    method setUpClass (line 105) | def setUpClass(cls):
    method test_lr (line 126) | def test_lr(self):
    method test_lr_disturbed (line 164) | def test_lr_disturbed(self):
    method test_lr_regularized (line 189) | def test_lr_regularized(self):
    method test_lr_sample_weight_all_half (line 214) | def test_lr_sample_weight_all_half(self):
    method test_lr_disturbed_sample_weight_all_half (line 231) | def test_lr_disturbed_sample_weight_all_half(self):
    method test_lr_sample_weight_all_zero (line 256) | def test_lr_sample_weight_all_zero(self):
    method test_lr_sample_weight_half_zero_half_one (line 264) | def test_lr_sample_weight_half_zero_half_one(self):
    method test_lr_disturbed_sample_weight_half_zero_half_one (line 284) | def test_lr_disturbed_sample_weight_half_zero_half_one(self):
    method test_lr_two_data_point (line 305) | def test_lr_two_data_point(self):
    method test_lr_disturbed_two_data_point (line 329) | def test_lr_disturbed_two_data_point(self):
    method test_lr_multicolinearty (line 353) | def test_lr_multicolinearty(self):
    method test_lr_disturbed_multicolinearty (line 379) | def test_lr_disturbed_multicolinearty(self):
  class CrossEntropyMNLMultinomialTests (line 406) | class CrossEntropyMNLMultinomialTests(unittest.TestCase):
    method setUpClass (line 409) | def setUpClass(cls):
    method test_label_encoder (line 414) | def test_label_encoder(self):
    method test_lr (line 450) | def test_lr(self):
    method test_lr_disturbed (line 480) | def test_lr_disturbed(self):
    method test_lr_regularized (line 497) | def test_lr_regularized(self):
    method test_lr_disturbed_regularized (line 513) | def test_lr_disturbed_regularized(self):
    method test_lr_sample_weight_all_half (line 529) | def test_lr_sample_weight_all_half(self):
    method test_lr_disturbed_sample_weight_all_half (line 552) | def test_lr_disturbed_sample_weight_all_half(self):
    method test_lr_sample_weight_all_zero (line 575) | def test_lr_sample_weight_all_zero(self):
    method test_lr_sample_weight_half_zero_half_one (line 583) | def test_lr_sample_weight_half_zero_half_one(self):
    method test_lr_disturbed_sample_weight_half_zero_half_one (line 603) | def test_lr_disturbed_sample_weight_half_zero_half_one(self):
    method test_lr_three_data_point (line 624) | def test_lr_three_data_point(self):
    method test_lr_disturbed_three_data_point (line 645) | def test_lr_disturbed_three_data_point(self):
    method test_lr_multicolinearty (line 666) | def test_lr_multicolinearty(self):
    method test_lr_disturbed_multicolinearty (line 689) | def test_lr_disturbed_multicolinearty(self):

FILE: tests/test_DiscreteMNL.py
  class DiscreteMNLUnaryTests (line 15) | class DiscreteMNLUnaryTests(unittest.TestCase):
    method setUpClass (line 18) | def setUpClass(cls):
    method test_lr (line 22) | def test_lr(self):
    method test_lr_sample_weight_all_half (line 43) | def test_lr_sample_weight_all_half(self):
    method test_lr_one_data_point (line 58) | def test_lr_one_data_point(self):
  class DiscreteMNLBinaryTests (line 80) | class DiscreteMNLBinaryTests(unittest.TestCase):
    method setUpClass (line 83) | def setUpClass(cls):
    method test_lr (line 86) | def test_lr(self):
    method test_lr_regularized (line 124) | def test_lr_regularized(self):
    method test_lr_sample_weight_all_half (line 149) | def test_lr_sample_weight_all_half(self):
    method test_lr_sample_weight_all_zero (line 166) | def test_lr_sample_weight_all_zero(self):
    method test_lr_sample_weight_half_zero_half_one (line 174) | def test_lr_sample_weight_half_zero_half_one(self):
    method test_lr_two_data_point (line 195) | def test_lr_two_data_point(self):
    method test_lr_multicolinearty (line 240) | def test_lr_multicolinearty(self):
  class DiscreteMNLMultinomialTests (line 267) | class DiscreteMNLMultinomialTests(unittest.TestCase):
    method setUpClass (line 270) | def setUpClass(cls):
    method test_lr (line 273) | def test_lr(self):
    method test_lr_regularized (line 303) | def test_lr_regularized(self):
    method test_lr_sample_weight_all_half (line 319) | def test_lr_sample_weight_all_half(self):
    method test_lr_sample_weight_all_zero (line 342) | def test_lr_sample_weight_all_zero(self):
    method test_lr_sample_weight_half_zero_half_one (line 350) | def test_lr_sample_weight_half_zero_half_one(self):
    method test_lr_three_data_point (line 371) | def test_lr_three_data_point(self):
    method test_lr_multicolinearty (line 392) | def test_lr_multicolinearty(self):

FILE: tests/test_GLM.py
  class PoissonTests (line 15) | class PoissonTests(unittest.TestCase):
    method setUpClass (line 18) | def setUpClass(cls):
    method test_glm_IRLS (line 24) | def test_glm_IRLS(self):
    method test_glm_regularized (line 94) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 133) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 178) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 186) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 220) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 240) | def test_ols_multicolinearty(self):
  class GammaTests (line 275) | class GammaTests(unittest.TestCase):
    method setUpClass (line 278) | def setUpClass(cls):
    method test_glm_IRLS (line 283) | def test_glm_IRLS(self):
    method test_glm_regularized (line 361) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 364) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 413) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 421) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 455) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 458) | def test_ols_multicolinearty(self):
  class GaussianTests (line 489) | class GaussianTests(unittest.TestCase):
    method setUpClass (line 492) | def setUpClass(cls):
    method test_glm_IRLS (line 497) | def test_glm_IRLS(self):
    method test_glm_regularized (line 574) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 577) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 626) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 634) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 668) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 671) | def test_ols_multicolinearty(self):
  class BinomialTests (line 702) | class BinomialTests(unittest.TestCase):
    method setUpClass (line 705) | def setUpClass(cls):
    method test_glm_IRLS (line 710) | def test_glm_IRLS(self):
    method test_glm_regularized (line 841) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 845) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 956) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 964) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 999) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 1020) | def test_ols_multicolinearty(self):
  class InverseGaussianTests (line 1053) | class InverseGaussianTests(unittest.TestCase):
    method setUpClass (line 1056) | def setUpClass(cls):
    method test_glm_IRLS (line 1063) | def test_glm_IRLS(self):
    method test_glm_regularized (line 1125) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 1128) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 1168) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 1176) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 1211) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 1214) | def test_ols_multicolinearty(self):
  class NegativeBinomialTests (line 1245) | class NegativeBinomialTests(unittest.TestCase):
    method setUpClass (line 1248) | def setUpClass(cls):
    method test_glm_IRLS (line 1257) | def test_glm_IRLS(self):
    method test_glm_regularized (line 1329) | def test_glm_regularized(self):
    method test_glm_sample_weight_all_half (line 1332) | def test_glm_sample_weight_all_half(self):
    method test_glm_sample_weight_all_zero (line 1378) | def test_glm_sample_weight_all_zero(self):
    method test_GLM_sample_weight_half_zero_half_one (line 1386) | def test_GLM_sample_weight_half_zero_half_one(self):
    method test_glm_one_data_point (line 1420) | def test_glm_one_data_point(self):
    method test_ols_multicolinearty (line 1423) | def test_ols_multicolinearty(self):

FILE: tests/test_HMM_utils.py
  class HMMUtilsTests (line 8) | class HMMUtilsTests(unittest.TestCase):
    method setUpClass (line 11) | def setUpClass(cls):
    method test_cal_alpha_beta (line 32) | def test_cal_alpha_beta(self):
    method test_forward_backward (line 68) | def test_forward_backward(self):

FILE: tests/test_OLS.py
  class UnivariateOLSTests (line 34) | class UnivariateOLSTests(unittest.TestCase):
    method setUpClass (line 37) | def setUpClass(cls):
    method test_ols (line 40) | def test_ols(self):
    method test_ols_l1_regularized (line 100) | def test_ols_l1_regularized(self):
    method test_ols_l2_regularized (line 104) | def test_ols_l2_regularized(self):
    method test_ols_elastic_net_regularized (line 156) | def test_ols_elastic_net_regularized(self):
    method test_ols_sample_weight_all_half (line 160) | def test_ols_sample_weight_all_half(self):
    method test_ols_sample_weight_all_zero (line 202) | def test_ols_sample_weight_all_zero(self):
    method test_ols_sample_weight_half_zero_half_one (line 210) | def test_ols_sample_weight_half_zero_half_one(self):
    method test_ols_one_data_point (line 242) | def test_ols_one_data_point(self):
    method test_ols_multicolinearty (line 261) | def test_ols_multicolinearty(self):
  class IndependentMultivariateOLSTests (line 293) | class IndependentMultivariateOLSTests(unittest.TestCase):
    method setUpClass (line 296) | def setUpClass(cls):
    method test_ols (line 301) | def test_ols(self):
    method test_ols_l2_regularized (line 353) | def test_ols_l2_regularized(self):
    method test_ols_l1_regularized (line 381) | def test_ols_l1_regularized(self):
    method test_ols_sample_weight_all_half (line 385) | def test_ols_sample_weight_all_half(self):
    method test_ols_sample_weight_all_zero (line 422) | def test_ols_sample_weight_all_zero(self):
    method test_ols_sample_weight_half_zero_half_one (line 429) | def test_ols_sample_weight_half_zero_half_one(self):
    method test_ols_one_data_point (line 461) | def test_ols_one_data_point(self):
    method test_ols_multicolinearty (line 483) | def test_ols_multicolinearty(self):
  class PerfectCorrelationMultivariateOLSTests (line 512) | class PerfectCorrelationMultivariateOLSTests(unittest.TestCase):
    method setUpClass (line 515) | def setUpClass(cls):
    method test_ols (line 522) | def test_ols(self):
    method test_ols_l1_regularized (line 573) | def test_ols_l1_regularized(self):
    method test_ols_l2_regularized (line 577) | def test_ols_l2_regularized(self):
    method test_ols_elastic_net_regularized (line 628) | def test_ols_elastic_net_regularized(self):
    method test_ols_sample_weight_all_half (line 632) | def test_ols_sample_weight_all_half(self):
    method test_ols_sample_weight_all_zero (line 677) | def test_ols_sample_weight_all_zero(self):
    method test_ols_sample_weight_half_zero_half_one (line 684) | def test_ols_sample_weight_half_zero_half_one(self):
    method test_ols_one_data_point (line 716) | def test_ols_one_data_point(self):
    method test_ols_multicolinearty (line 737) | def test_ols_multicolinearty(self):

FILE: tests/test_SemiSupervisedIOHMM.py
  class SemiSupervisedIOHMMTests (line 16) | class SemiSupervisedIOHMMTests(unittest.TestCase):
    method setUpClass (line 19) | def setUpClass(cls):
    method _mock_states (line 24) | def _mock_states(cls):
    method setUp (line 36) | def setUp(self):
    method test_train_no_covariates (line 39) | def test_train_no_covariates(self):
    method test_from_json (line 101) | def test_from_json(self):
    method test_from_config (line 144) | def test_from_config(self):

FILE: tests/test_SupervisedIOHMM.py
  class SupervisedIOHMMTests (line 13) | class SupervisedIOHMMTests(unittest.TestCase):
    method setUpClass (line 16) | def setUpClass(cls):
    method _mock_states (line 21) | def _mock_states(cls):
    method test_train_no_covariates (line 31) | def test_train_no_covariates(self):
    method test_from_json (line 77) | def test_from_json(self):
    method test_from_config (line 104) | def test_from_config(self):

FILE: tests/test_UnSupervisedIOHMM.py
  class UnSupervisedIOHMMTests (line 12) | class UnSupervisedIOHMMTests(unittest.TestCase):
    method setUpClass (line 15) | def setUpClass(cls):
    method setUp (line 18) | def setUp(self):
    method test_train_no_covariates (line 21) | def test_train_no_covariates(self):
    method test_train_covariates_for_transition (line 63) | def test_train_covariates_for_transition(self):
    method test_train_multivariate (line 100) | def test_train_multivariate(self):
    method test_from_json (line 150) | def test_from_json(self):
    method test_from_config (line 180) | def test_from_config(self):
Condensed preview — 178 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,265K chars).
[
  {
    "path": ".gitignore",
    "chars": 1713,
    "preview": "# Apple\n.DS_Store\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Grunt intermediate s"
  },
  {
    "path": ".travis.yml",
    "chars": 361,
    "preview": "language: python\npython:\n  - \"3.7\"\n  - \"3.8\"\n  - \"3.9\"\n\n# command to install dependencies\ninstall:\n - pip install -r req"
  },
  {
    "path": "IOHMM/IOHMM.py",
    "chars": 34782,
    "preview": "'''\nThis module implements IOHMM models:\n\n(1) UnSupervisedIOHMM:\n    Standard IOHMM with no ground truth label of hidden"
  },
  {
    "path": "IOHMM/__init__.py",
    "chars": 764,
    "preview": "from .IOHMM import (UnSupervisedIOHMM,\n                    SemiSupervisedIOHMM,\n                    SupervisedIOHMM)\nfro"
  },
  {
    "path": "IOHMM/forward_backward.py",
    "chars": 10223,
    "preview": "'''\nThe forward backward algorithm of hidden markov model (HMM) .\nMainly used in the E-step of IOHMM given the\n(1) initi"
  },
  {
    "path": "IOHMM/linear_models.py",
    "chars": 55044,
    "preview": "'''\nThis is a unified interface/wrapper of general/generalized linear models from\nsklearn/statsmodels packages.\n\nProblem"
  },
  {
    "path": "LICENCE",
    "chars": 1535,
    "preview": "Copyright (c) 2017, Mogeng Yin, Alexei Pozdnukhov\nAll rights reserved.\n\nRedistribution and use in source and binary form"
  },
  {
    "path": "README.md",
    "chars": 5445,
    "preview": "# IOHMM\n\nA Python package of Input-Output Hidden Markov Model (IOHMM).\n\n[![Build Status](https://travis-ci.org/Mogeng/IO"
  },
  {
    "path": "examples/data/speed.csv",
    "chars": 22231,
    "preview": "\"\",\"rt\",\"corr\",\"Pacc\",\"prev\"\n\"1\",6.45676965557216,\"cor\",0,\"inc\"\n\"2\",5.6021188208797,\"cor\",0,\"cor\"\n\"3\",6.25382881157547,\""
  },
  {
    "path": "examples/models/SemiSupervisedIOHMM/config.json",
    "chars": 826,
    "preview": "{\n    \"data_type\": \"SemiSupervisedIOHMM\",\n    \"properties\": {\n        \"EM_tol\": 1e-10,\n        \"covariates_emissions\": ["
  },
  {
    "path": "examples/models/SemiSupervisedIOHMM/model.json",
    "chars": 9859,
    "preview": "{\n    \"data_type\": \"SemiSupervisedIOHMM\", \n    \"properties\": {\n        \"EM_tol\": 1e-10, \n        \"covariates_emissions\":"
  },
  {
    "path": "examples/models/SupervisedIOHMM/config.json",
    "chars": 769,
    "preview": "{\n    \"data_type\": \"SupervisedIOHMM\",\n    \"properties\": {\n        \"covariates_emissions\": [\n            []\n        ],\n  "
  },
  {
    "path": "examples/models/SupervisedIOHMM/model.json",
    "chars": 5450,
    "preview": "{\n    \"data_type\": \"SupervisedIOHMM\", \n    \"properties\": {\n        \"covariates_emissions\": [\n            []\n        ], \n"
  },
  {
    "path": "examples/models/UnSupervisedIOHMM/config.json",
    "chars": 996,
    "preview": "{\n    \"data_type\": \"UnSupervisedIOHMM\",\n    \"properties\": {\n        \"EM_tol\": 1e-06,\n        \"covariates_emissions\": [\n "
  },
  {
    "path": "examples/models/UnSupervisedIOHMM/model.json",
    "chars": 8011,
    "preview": "{\n    \"data_type\": \"UnSupervisedIOHMM\", \n    \"properties\": {\n        \"EM_tol\": 1e-06, \n        \"covariates_emissions\": ["
  },
  {
    "path": "examples/notebooks/SemiSupervisedIOHMM.ipynb",
    "chars": 21173,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"This is the IOHMM model with the pa"
  },
  {
    "path": "examples/notebooks/SupervisedIOHMM.ipynb",
    "chars": 15230,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"This is the IOHMM model with the pa"
  },
  {
    "path": "examples/notebooks/UnSupervisedIOHMM.ipynb",
    "chars": 20570,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# UnSupervisedIOHMM\"\n   ]\n  },\n  {\n"
  },
  {
    "path": "requirements.txt",
    "chars": 108,
    "preview": "numpy >= 1.20.0\nfuture >= 0.18.2\npandas >= 1.2.1\nscikit-learn >= 1.2.2\nscipy >= 1.6.0\nstatsmodels >= 0.12.2\n"
  },
  {
    "path": "setup.cfg",
    "chars": 71,
    "preview": "[metadata]\ndescription-file = README.md\n[flake8]\nmax-line-length = 100\n"
  },
  {
    "path": "setup.py",
    "chars": 1605,
    "preview": "from setuptools import setup\n\nsetup(\n    name=\"IOHMM\",\n    version=\"0.0.7\",\n    description='A python library for Input "
  },
  {
    "path": "tests/IOHMM_models/SemiSupervisedIOHMM/model.json",
    "chars": 9936,
    "preview": "{\n    \"data_type\": \"SemiSupervisedIOHMM\",\n    \"properties\": {\n        \"EM_tol\": 1e-10,\n        \"covariates_emissions\": ["
  },
  {
    "path": "tests/IOHMM_models/SupervisedIOHMM/model.json",
    "chars": 5491,
    "preview": "{\n    \"data_type\": \"SupervisedIOHMM\",\n    \"properties\": {\n        \"covariates_emissions\": [\n            []\n        ],\n  "
  },
  {
    "path": "tests/IOHMM_models/UnSupervisedIOHMM/model.json",
    "chars": 8074,
    "preview": "{\n    \"data_type\": \"UnSupervisedIOHMM\",\n    \"properties\": {\n        \"EM_tol\": 1e-06,\n        \"covariates_emissions\": [\n "
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/linear_models/GLM/InverseGaussian/inv_gaussian.csv",
    "chars": 766639,
    "preview": "xig,x1,x2\r\n1.2733455,.49838599,1.3452648\r\n.3134754,.4823677,.74737328\r\n.79428208,1.136941,.14203239\r\n1.1283428,.1553426,"
  },
  {
    "path": "tests/test_CrossentropyMNL.py",
    "chars": 32937,
    "preview": "from __future__ import division\nfrom builtins import range\nfrom past.utils import old_div\nimport unittest\n\n\nimport numpy"
  },
  {
    "path": "tests/test_DiscreteMNL.py",
    "chars": 18761,
    "preview": "from __future__ import print_function\nfrom __future__ import division\nfrom builtins import range\nfrom past.utils import "
  },
  {
    "path": "tests/test_GLM.py",
    "chars": 67433,
    "preview": "from __future__ import print_function\nfrom __future__ import division\nfrom past.utils import old_div\nimport unittest\n\n\ni"
  },
  {
    "path": "tests/test_HMM_utils.py",
    "chars": 6411,
    "preview": "import unittest\n\nimport numpy as np\n\nfrom IOHMM import (forward, backward, forward_backward)\n\n\nclass HMMUtilsTests(unitt"
  },
  {
    "path": "tests/test_OLS.py",
    "chars": 33960,
    "preview": "from __future__ import print_function\nfrom __future__ import division\n# import json\nfrom past.utils import old_div\nimpor"
  },
  {
    "path": "tests/test_SemiSupervisedIOHMM.py",
    "chars": 9401,
    "preview": "from __future__ import print_function\nfrom __future__ import division\nfrom builtins import range\nfrom past.utils import "
  },
  {
    "path": "tests/test_SupervisedIOHMM.py",
    "chars": 6235,
    "preview": "from builtins import range\nimport json\nimport unittest\n\n\nimport numpy as np\nimport pandas as pd\n\nfrom IOHMM import Super"
  },
  {
    "path": "tests/test_UnSupervisedIOHMM.py",
    "chars": 10109,
    "preview": "import json\nimport unittest\n\n\nimport numpy as np\nimport pandas as pd\n\nfrom IOHMM import UnSupervisedIOHMM\nfrom IOHMM imp"
  }
]

// ... and 144 more files (download for full content)

About this extraction

This page contains the full source code of the Mogeng/IOHMM GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 178 files (1.1 MB), approximately 482.9k tokens, and a symbol index with 255 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!