Continous Time Markov Chain

    Parameters
    ----------
    data : list of lists
        A python list of N examples (e.g. rating histories of N companies,
        the event data of N basketball games, etc.). The i-th example
        consist of one list with M_i encoded state labels and M_i the
        durations or time periods the state lasted since the recording
        started.

    numstates : int
        number of unique states

    transintv : float
        The time interval

    toltime : float
        (If debug=True) Will throw an exception if the aggregated state
        duration or aggregated time periods of any state is smaller
        than toltime.

    debug : bool
        (Default: False) If True run the ctmc.datacheck function.
        Enable this flag if you to check if your 'data' variable
        has been processed correctly.

    Returns
    -------
    transmat : ndarray
        The estimated transition/stochastic matrix.

    genmat : ndarray
        The estimated generator matrix

    transcount : ndarray

    statetime : ndarray


    Errors:
    -------
    - ctmc assumes a clean data object and does not
        autocorrect any errors as result of it

    The main error sources are

    - transitions counting (e.g. two consequtive states
        has not been aggregated, only one distinct state
        reported) and
    - a state is modeled ore required that does not occur
        in the dataset (e.g. you a certain scale in mind
        and just assume it's in the data) or resp. involved
        in any transition (e.g. an example with just one
        state)

    You can enable error checking and exceptions by setting
    debug=True. You should do this for the first run on a
    smaller dataset.

    Example:
    --------
    Use `datacheck` to check during preprocessing the
    dataset

        data = ...
        ctmc.datacheck(data, numstates, toltime)

    Disable checks in `ctmc`

        transmat, genmat, transcount, statetime = ctmc.ctmc(
            data, numstates, toltime, checks=False)

    Check aftwards if there has been an error

        ctmc.errorcheck(transcount, statetime, toltime)

def ctmc(data, numstates, transintv=1.0, toltime=1e-8, debug=False):
    """ Continous Time Markov Chain

    Parameters
    ----------
    data : list of lists
        A python list of N examples (e.g. rating histories of N companies,
        the event data of N basketball games, etc.). The i-th example
        consist of one list with M_i encoded state labels and M_i the
        durations or time periods the state lasted since the recording
        started.

    numstates : int
        number of unique states

    transintv : float
        The time interval

    toltime : float
        (If debug=True) Will throw an exception if the aggregated state
        duration or aggregated time periods of any state is smaller
        than toltime.

    debug : bool
        (Default: False) If True run the ctmc.datacheck function.
        Enable this flag if you to check if your 'data' variable
        has been processed correctly.

    Returns
    -------
    transmat : ndarray
        The estimated transition/stochastic matrix.

    genmat : ndarray
        The estimated generator matrix

    transcount : ndarray

    statetime : ndarray


    Errors:
    -------
    - ctmc assumes a clean data object and does not
        autocorrect any errors as result of it

    The main error sources are

    - transitions counting (e.g. two consequtive states
        has not been aggregated, only one distinct state
        reported) and
    - a state is modeled ore required that does not occur
        in the dataset (e.g. you a certain scale in mind
        and just assume it's in the data) or resp. involved
        in any transition (e.g. an example with just one
        state)

    You can enable error checking and exceptions by setting
    debug=True. You should do this for the first run on a
    smaller dataset.

    Example:
    --------
    Use `datacheck` to check during preprocessing the
    dataset

        data = ...
        ctmc.datacheck(data, numstates, toltime)

    Disable checks in `ctmc`

        transmat, genmat, transcount, statetime = ctmc.ctmc(
            data, numstates, toltime, checks=False)

    Check aftwards if there has been an error

        ctmc.errorcheck(transcount, statetime, toltime)

    """
    # raise an exception if the data format is wrong
    if debug:
        datacheck(data, numstates, toltime)

    # aggregate event data
    transcount, statetime = aggregateevents(data, numstates)

    # raise an exception if the event data aggregation failed
    if debug:
        errorcheck(transcount, statetime, toltime)

    # create generator matrix
    genmat = generatormatrix(transcount, statetime)

    # compute matrix exponential of the generator matrix
    transmat = scipy.linalg.expm(genmat * transintv)

    # done
    return transmat, genmat, transcount, statetime