Hidden Markov Models - Statistics

Skip to content
Back to top
View this page
Edit this page

# Hidden Markov Models

This is a complete pure-Cython optimized implementation of Hidden Markov Models. It fully supports Discrete, Gaussian, and Mixed Gaussian emissions.

The best references for the basic HMM algorithms implemented here are:

- Tapas Kanungo’s “Hidden Markov Models”

- Jackson’s HMM tutorial:

    http://personal.ee.surrey.ac.uk/Personal/P.Jackson/tutorial/

LICENSE: Some of the code in this file is based on reading Kanungo’s GPLv2+ implementation of discrete HMM’s, hence the present code must be licensed with a GPLv2+ compatible license.

AUTHOR:

> - William Stein, 2010-03

class sage.stats.hmm.hmm.DiscreteHiddenMarkovModel

  Bases: `HiddenMarkovModel`

  A discrete Hidden Markov model implemented using double precision floating point arithmetic.

  INPUT:

  - `A` – list of lists or a square \(N \times N\) matrix, whose \((i,j)\) entry gives the probability of transitioning from state \(i\) to state \(j\).

  - `B` – list of \(N\) lists or a matrix with \(N\) rows, such that \(B[i,k]\) gives the probability of emitting symbol \(k\) while in state \(i\).

  - `pi` – the probabilities of starting in each initial state, i.e., `pi[i]` is the probability of starting in state \(i\).

  - `emission_symbols` – `None` or list (default: `None`); if None, the emission_symbols are the ints `[0..N-1]`, where \(N\) is the number of states. Otherwise, they are the entries of the list `emissions_symbols`, which must all be hashable.

  - `normalize` – boolean (default: `True`); if given, input is normalized to define valid probability distributions, e.g., the entries of \(A\) are made nonnegative and the rows sum to 1, and the probabilities in `pi` are normalized.

  EXAMPLES:

  Sage

      sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
      ....:                                   [[0.1,0.9],[0.5,0.5]],
      ....:                                   [.5,.5]); m
      Discrete Hidden Markov Model with 2 States and 2 Emissions
      Transition matrix:
      [0.4 0.6]
      [0.1 0.9]
      Emission matrix:
      [0.1 0.9]
      [0.5 0.5]
      Initial probabilities: [0.5000, 0.5000]
      sage: m.log_likelihood([0,1,0,1,0,1])
      -4.66693474691329...
      sage: m.viterbi([0,1,0,1,0,1])
      ([1, 1, 1, 1, 1, 1], -5.378832842208748)
      sage: m.baum_welch([0,1,0,1,0,1])
      (0.0, 22)
      sage: m  # rel tol 1e-10
      Discrete Hidden Markov Model with 2 States and 2 Emissions
      Transition matrix:
      [1.0134345614745788e-70                    1.0]
      [                   1.0 3.9974352713558623e-19]
      Emission matrix:
      [ 7.380221566254936e-54                    1.0]
      [                   1.0 3.9974352626002193e-19]
      Initial probabilities: [0.0000, 1.0000]
      sage: m.sample(10)
      [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
      sage: m.graph().plot()                                                          # needs sage.plot
      Graphics object consisting of 6 graphics primitives

  Python

      >>> from sage.all import *
      >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
      ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
      ...                                   [RealNumber('.5'),RealNumber('.5')]); m
      Discrete Hidden Markov Model with 2 States and 2 Emissions
      Transition matrix:
      [0.4 0.6]
      [0.1 0.9]
      Emission matrix:
      [0.1 0.9]
      [0.5 0.5]
      Initial probabilities: [0.5000, 0.5000]
      >>> m.log_likelihood([Integer(0),Integer(1),Integer(0),Integer(1),Integer(0),Integer(1)])
      -4.66693474691329...
      >>> m.viterbi([Integer(0),Integer(1),Integer(0),Integer(1),Integer(0),Integer(1)])
      ([1, 1, 1, 1, 1, 1], -5.378832842208748)
      >>> m.baum_welch([Integer(0),Integer(1),Integer(0),Integer(1),Integer(0),Integer(1)])
      (0.0, 22)
      >>> m  # rel tol 1e-10
      Discrete Hidden Markov Model with 2 States and 2 Emissions
      Transition matrix:
      [1.0134345614745788e-70                    1.0]
      [                   1.0 3.9974352713558623e-19]
      Emission matrix:
      [ 7.380221566254936e-54                    1.0]
      [                   1.0 3.9974352626002193e-19]
      Initial probabilities: [0.0000, 1.0000]
      >>> m.sample(Integer(10))
      [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
      >>> m.graph().plot()                                                          # needs sage.plot
      Graphics object consisting of 6 graphics primitives

  A 3-state model that happens to always outputs ‘b’:

  Sage

      sage: m = hmm.DiscreteHiddenMarkovModel([[1/3]*3]*3, [[0,1,0]]*3, [1/3]*3, ['a','b','c'])
      sage: m.sample(10)
      ['b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']

  [Python]

      >>> from sage.all import *
      >>> m = hmm.DiscreteHiddenMarkovModel([[Integer(1)/Integer(3)]*Integer(3)]*Integer(3), [[Integer(0),Integer(1),Integer(0)]]*Integer(3), [Integer(1)/Integer(3)]*Integer(3), ['a','b','c'])
      >>> m.sample(Integer(10))
      ['b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']

  baum_welch(obs, max_iter=100, log_likelihood_cutoff=0.0001, fix_emissions=False)

    Given an observation sequence obs, improve this HMM using the Baum-Welch algorithm to increase the probability of observing obs.

    INPUT:

    - `obs` – list of emissions

    - `max_iter` – integer (default: 100); maximum number of Baum-Welch steps to take

    - `log_likelihood_cutoff` – positive float (default: 1e-4); the minimal improvement in likelihood with respect to the last iteration required to continue. Relative value to log likelihood.

    - `fix_emissions` – boolean (default: `False`); if `True`, do not change emissions when updating

    OUTPUT:

    changes the model in place, and returns the log likelihood and number of iterations.

    EXAMPLES:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.9,0.1]],
        ....:                                   [[.5,.5],[0,1]],
        ....:                                   [.2,.8])
        sage: m.baum_welch([1,0]*20, log_likelihood_cutoff=0)
        (0.0, 4)
        sage: m  # rel tol 1e-14
        Discrete Hidden Markov Model with 2 States and 2 Emissions
        Transition matrix:
        [1.3515269707707603e-51                    1.0]
        [                   1.0                    0.0]
        Emission matrix:
        [                  1.0 6.462537138850569e-52]
        [                  0.0                   1.0]
        Initial probabilities: [0.0000, 1.0000]

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.9'),RealNumber('0.1')]],
        ...                                   [[RealNumber('.5'),RealNumber('.5')],[Integer(0),Integer(1)]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> m.baum_welch([Integer(1),Integer(0)]*Integer(20), log_likelihood_cutoff=Integer(0))
        (0.0, 4)
        >>> m  # rel tol 1e-14
        Discrete Hidden Markov Model with 2 States and 2 Emissions
        Transition matrix:
        [1.3515269707707603e-51                    1.0]
        [                   1.0                    0.0]
        Emission matrix:
        [                  1.0 6.462537138850569e-52]
        [                  0.0                   1.0]
        Initial probabilities: [0.0000, 1.0000]

    The following illustrates how Baum-Welch is only a local optimizer, i.e., the above model is far more likely to produce the sequence [1,0]*20 than the one we get below:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.5,0.5],[0.5,0.5]],
        ....:                                   [[.5,.5],[.5,.5]],
        ....:                                   [.5,.5])
        sage: m.baum_welch([1,0]*20, log_likelihood_cutoff=0)
        (-27.725887222397784, 1)
        sage: m
        Discrete Hidden Markov Model with 2 States and 2 Emissions
        Transition matrix:
        [0.5 0.5]
        [0.5 0.5]
        Emission matrix:
        [0.5 0.5]
        [0.5 0.5]
        Initial probabilities: [0.5000, 0.5000]

    [Python]

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.5'),RealNumber('0.5')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [[RealNumber('.5'),RealNumber('.5')],[RealNumber('.5'),RealNumber('.5')]],
        ...                                   [RealNumber('.5'),RealNumber('.5')])
        >>> m.baum_welch([Integer(1),Integer(0)]*Integer(20), log_likelihood_cutoff=Integer(0))
        (-27.725887222397784, 1)
        >>> m
        Discrete Hidden Markov Model with 2 States and 2 Emissions
        Transition matrix:
        [0.5 0.5]
        [0.5 0.5]
        Emission matrix:
        [0.5 0.5]
        [0.5 0.5]
        Initial probabilities: [0.5000, 0.5000]

    We illustrate fixing emissions:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.9,0.1]],
        ....:                                   [[.5,.5],[.2,.8]],
        ....:                                   [.2,.8])
        sage: set_random_seed(0); v = m.sample(100)
        sage: m.baum_welch(v,fix_emissions=True)
        (-66.98630856918774, 100)
        sage: m.emission_matrix()
        [0.5 0.5]
        [0.2 0.8]
        sage: m = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.9,0.1]],
        ....:                                   [[.5,.5],[.2,.8]],
        ....:                                   [.2,.8])
        sage: m.baum_welch(v)
        (-66.782360659293..., 100)
        sage: m.emission_matrix()  # rel tol 1e-14
        [ 0.5303085748626447 0.46969142513735535]
        [ 0.2909775550173978  0.7090224449826023]

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.9'),RealNumber('0.1')]],
        ...                                   [[RealNumber('.5'),RealNumber('.5')],[RealNumber('.2'),RealNumber('.8')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> set_random_seed(Integer(0)); v = m.sample(Integer(100))
        >>> m.baum_welch(v,fix_emissions=True)
        (-66.98630856918774, 100)
        >>> m.emission_matrix()
        [0.5 0.5]
        [0.2 0.8]
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.9'),RealNumber('0.1')]],
        ...                                   [[RealNumber('.5'),RealNumber('.5')],[RealNumber('.2'),RealNumber('.8')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> m.baum_welch(v)
        (-66.782360659293..., 100)
        >>> m.emission_matrix()  # rel tol 1e-14
        [ 0.5303085748626447 0.46969142513735535]
        [ 0.2909775550173978  0.7090224449826023]

  emission_matrix()

    Return the matrix whose \(i\)-th row specifies the emission probability distribution for the \(i\)-th state.

    More precisely, the \(i,j\) entry of the matrix is the probability of the Markov model outputting the \(j\)-th symbol when it is in the \(i\)-th state.

    OUTPUT: a Sage matrix with real double precision (RDF) entries.

    EXAMPLES:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
        ....:                                   [[0.1,0.9],[0.5,0.5]],
        ....:                                   [.5,.5])
        sage: E = m.emission_matrix(); E
        [0.1 0.9]
        [0.5 0.5]

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [RealNumber('.5'),RealNumber('.5')])
        >>> E = m.emission_matrix(); E
        [0.1 0.9]
        [0.5 0.5]

    The returned matrix is mutable, but changing it does not change the transition matrix for the model:

    Sage

        sage: E[0,0] = 0; E[0,1] = 1
        sage: m.emission_matrix()
        [0.1 0.9]
        [0.5 0.5]

    [Python]

        >>> from sage.all import *
        >>> E[Integer(0),Integer(0)] = Integer(0); E[Integer(0),Integer(1)] = Integer(1)
        >>> m.emission_matrix()
        [0.1 0.9]
        [0.5 0.5]

  generate_sequence(length, starting_state=None)

    Return a sample of the given length from this HMM.

    INPUT:

    - `length` – positive integer

    - `starting_state` – integer (or `None`); if specified, generate a sequence using this model starting with the given state instead of the initial probabilities to determine the starting state

    OUTPUT:

    - an `IntList` or list of emission symbols

    - `IntList` of the actual states the model was in when emitting the corresponding symbols

    EXAMPLES:

    In this example, the emission symbols are not set:

    Sage

        sage: set_random_seed(0)
        sage: a = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.1,0.9]],
        ....:                                   [[1,0],[0,1]],
        ....:                                   [0,1])
        sage: a.generate_sequence(5)
        ([1, 0, 1, 1, 1], [1, 0, 1, 1, 1])
        sage: list(a.generate_sequence(1000)[0]).count(0)
        90

    Python

        >>> from sage.all import *
        >>> set_random_seed(Integer(0))
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[Integer(1),Integer(0)],[Integer(0),Integer(1)]],
        ...                                   [Integer(0),Integer(1)])
        >>> a.generate_sequence(Integer(5))
        ([1, 0, 1, 1, 1], [1, 0, 1, 1, 1])
        >>> list(a.generate_sequence(Integer(1000))[Integer(0)]).count(Integer(0))
        90

    Here the emission symbols are set:

    Sage

        sage: set_random_seed(0)
        sage: a = hmm.DiscreteHiddenMarkovModel([[0.5,0.5],[0.1,0.9]],
        ....:                                   [[1,0],[0,1]],
        ....:                                   [0,1], ['up', 'down'])
        sage: a.generate_sequence(5)
        (['down', 'up', 'down', 'down', 'down'], [1, 0, 1, 1, 1])

    [Python]

        >>> from sage.all import *
        >>> set_random_seed(Integer(0))
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.5'),RealNumber('0.5')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[Integer(1),Integer(0)],[Integer(0),Integer(1)]],
        ...                                   [Integer(0),Integer(1)], ['up', 'down'])
        >>> a.generate_sequence(Integer(5))
        (['down', 'up', 'down', 'down', 'down'], [1, 0, 1, 1, 1])

    Specify the starting state:

    Sage

        sage: set_random_seed(0); a.generate_sequence(5, starting_state=0)
        (['up', 'up', 'down', 'down', 'down'], [0, 0, 1, 1, 1])

    Python

        >>> from sage.all import *
        >>> set_random_seed(Integer(0)); a.generate_sequence(Integer(5), starting_state=Integer(0))
        (['up', 'up', 'down', 'down', 'down'], [0, 0, 1, 1, 1])

  log_likelihood(obs, scale=True)

    Return the logarithm of the probability that this model produced the given observation sequence. Thus the output is a nonpositive number.

    INPUT:

    - `obs` – sequence of observations

    - `scale` – boolean (default: `True`); if `True`, use rescaling to overoid loss of precision due to the very limited dynamic range of floats. You should leave this as `True` unless the `obs` sequence is very small.

    EXAMPLES:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
        ....:                                   [[0.1,0.9],[0.5,0.5]],
        ....:                                   [.2,.8])
        sage: m.log_likelihood([0, 1, 0, 1, 1, 0, 1, 0, 0, 0])
        -7.3301308009370825
        sage: m.log_likelihood([0, 1, 0, 1, 1, 0, 1, 0, 0, 0], scale=False)
        -7.330130800937082
        sage: m.log_likelihood([])
        0.0

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
        ....:                                   [[0.1,0.9],[0.5,0.5]],
        ....:                                   [.2,.8], ['happy','sad'])
        sage: m.log_likelihood(['happy','happy'])
        -1.6565295199679506
        sage: m.log_likelihood(['happy','sad'])
        -1.4731602941415523

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> m.log_likelihood([Integer(0), Integer(1), Integer(0), Integer(1), Integer(1), Integer(0), Integer(1), Integer(0), Integer(0), Integer(0)])
        -7.3301308009370825
        >>> m.log_likelihood([Integer(0), Integer(1), Integer(0), Integer(1), Integer(1), Integer(0), Integer(1), Integer(0), Integer(0), Integer(0)], scale=False)
        -7.330130800937082
        >>> m.log_likelihood([])
        0.0

        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')], ['happy','sad'])
        >>> m.log_likelihood(['happy','happy'])
        -1.6565295199679506
        >>> m.log_likelihood(['happy','sad'])
        -1.4731602941415523

    Overflow from not using the scale option:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
        ....:                                   [[0.1,0.9],[0.5,0.5]],
        ....:                                   [.2,.8])
        sage: m.log_likelihood([0,1]*1000, scale=True)
        -1433.820666652728
        sage: m.log_likelihood([0,1]*1000, scale=False)
        -inf

    [Python]

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> m.log_likelihood([Integer(0),Integer(1)]*Integer(1000), scale=True)
        -1433.820666652728
        >>> m.log_likelihood([Integer(0),Integer(1)]*Integer(1000), scale=False)
        -inf

  viterbi(obs, log_scale=True)

    Determine “the” hidden sequence of states that is most likely to produce the given sequence seq of observations, along with the probability that this hidden sequence actually produced the observation.

    INPUT:

    - `seq` – sequence of emitted ints or symbols

    - `log_scale` – boolean (default: `True`); whether to scale the sequence in order to avoid numerical overflow

    OUTPUT:

    - `list` – “the” most probable sequence of hidden states, i.e., the Viterbi path

    - `float` – log of probability that the observed sequence was produced by the Viterbi sequence of states

    EXAMPLES:

    Sage

        sage: a = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.1,0.9]],
        ....:                                   [[0.9,0.1],[0.1,0.9]],
        ....:                                   [0.5,0.5])
        sage: a.viterbi([1,0,0,1,0,0,1,1])
        ([1, 0, 0, 1, ..., 0, 1, 1], -11.06245322477221...)

    Python

        >>> from sage.all import *
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.9'),RealNumber('0.1')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [RealNumber('0.5'),RealNumber('0.5')])
        >>> a.viterbi([Integer(1),Integer(0),Integer(0),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1)])
        ([1, 0, 0, 1, ..., 0, 1, 1], -11.06245322477221...)

    We predict the state sequence when the emissions are 3/4 and ‘abc’.:

    Sage

        sage: a = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.1,0.9]],
        ....:                                   [[0.9,0.1],[0.1,0.9]],
        ....:                                   [0.5,0.5], [3/4, 'abc'])

    [Python]

        >>> from sage.all import *
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.9'),RealNumber('0.1')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [RealNumber('0.5'),RealNumber('0.5')], [Integer(3)/Integer(4), 'abc'])

    Note that state 0 is common below, despite the model trying hard to switch to state 1:

    Sage

        sage: a.viterbi([3/4, 'abc', 'abc'] + [3/4]*10)
        ([0, 1, 1, 0, 0 ... 0, 0, 0, 0, 0], -25.299405845367794)

    Python

        >>> from sage.all import *
        >>> a.viterbi([Integer(3)/Integer(4), 'abc', 'abc'] + [Integer(3)/Integer(4)]*Integer(10))
        ([0, 1, 1, 0, 0 ... 0, 0, 0, 0, 0], -25.299405845367794)

class sage.stats.hmm.hmm.HiddenMarkovModel

  Bases: `object`

  Abstract base class for all Hidden Markov Models.

  graph(eps=0.001)

    Create a weighted directed graph from the transition matrix, not including any edge with a probability less than `eps`.

    INPUT:

    - `eps` – nonnegative real number

    OUTPUT: a `DiGraph`

    EXAMPLES:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[.3,0,.7],[0,0,1],[.5,.5,0]],
        ....:                                   [[.5,.5,.2]]*3,
        ....:                                   [1/3]*3)
        sage: G = m.graph(); G                                                      # needs sage.graphs
        Looped digraph on 3 vertices
        sage: G.edges(sort=True)                                                    # needs sage.graphs
        [(0, 0, 0.3), (0, 2, 0.7), (1, 2, 1.0), (2, 0, 0.5), (2, 1, 0.5)]
        sage: G.plot()                                                              # needs sage.graphs sage.plot
        Graphics object consisting of 11 graphics primitives

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('.3'),Integer(0),RealNumber('.7')],[Integer(0),Integer(0),Integer(1)],[RealNumber('.5'),RealNumber('.5'),Integer(0)]],
        ...                                   [[RealNumber('.5'),RealNumber('.5'),RealNumber('.2')]]*Integer(3),
        ...                                   [Integer(1)/Integer(3)]*Integer(3))
        >>> G = m.graph(); G                                                      # needs sage.graphs
        Looped digraph on 3 vertices
        >>> G.edges(sort=True)                                                    # needs sage.graphs
        [(0, 0, 0.3), (0, 2, 0.7), (1, 2, 1.0), (2, 0, 0.5), (2, 1, 0.5)]
        >>> G.plot()                                                              # needs sage.graphs sage.plot
        Graphics object consisting of 11 graphics primitives

  initial_probabilities()

    Return the initial probabilities as a `TimeSeries` of length \(N\), where \(N\) is the number of states of the Markov model.

    EXAMPLES:

    Sage

        sage: m = hmm.DiscreteHiddenMarkovModel([[0.4,0.6],[0.1,0.9]],
        ....:                                   [[0.1,0.9],[0.5,0.5]],
        ....:                                   [.2,.8])
        sage: pi = m.initial_probabilities(); pi
        [0.2000, 0.8000]
        sage: type(pi)
        <... 'sage.stats.time_series.TimeSeries'>

    Python

        >>> from sage.all import *
        >>> m = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.4'),RealNumber('0.6')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.5'),RealNumber('0.5')]],
        ...                                   [RealNumber('.2'),RealNumber('.8')])
        >>> pi = m.initial_probabilities(); pi
        [0.2000, 0.8000]
        >>> type(pi)
        <... 'sage.stats.time_series.TimeSeries'>

    The returned time series is a copy, so changing it does not change the model:

    Sage

        sage: pi[0] = .1; pi[1] = .9
        sage: m.initial_probabilities()
        [0.2000, 0.8000]

    [Python]

        >>> from sage.all import *
        >>> pi[Integer(0)] = RealNumber('.1'); pi[Integer(1)] = RealNumber('.9')
        >>> m.initial_probabilities()
        [0.2000, 0.8000]

    Some other models:

    Sage

        sage: m = hmm.GaussianHiddenMarkovModel([[.1,.9],[.5,.5]],
        ....:                                   [(1,1), (-1,1)],
        ....:                                   [.1,.9])
        sage: m.initial_probabilities()
        [0.1000, 0.9000]
        sage: m = hmm.GaussianMixtureHiddenMarkovModel(
        ....:         [[.9,.1],[.4,.6]],
        ....:         [[(.4,(0,1)), (.6,(1,0.1))], [(1,(0,1))]],
        ....:         [.7,.3])
        sage: m.initial_probabilities()
        [0.7000, 0.3000]

    Python

        >>> from sage.all import *
        >>> m = hmm.GaussianHiddenMarkovModel([[RealNumber('.1'),RealNumber('.9')],[RealNumber('.5'),RealNumber('.5')]],
        ...                                   [(Integer(1),Integer(1)), (-Integer(1),Integer(1))],
        ...                                   [RealNumber('.1'),RealNumber('.9')])
        >>> m.initial_probabilities()
        [0.1000, 0.9000]
        >>> m = hmm.GaussianMixtureHiddenMarkovModel(
        ...         [[RealNumber('.9'),RealNumber('.1')],[RealNumber('.4'),RealNumber('.6')]],
        ...         [[(RealNumber('.4'),(Integer(0),Integer(1))), (RealNumber('.6'),(Integer(1),RealNumber('0.1')))], [(Integer(1),(Integer(0),Integer(1)))]],
        ...         [RealNumber('.7'),RealNumber('.3')])
        >>> m.initial_probabilities()
        [0.7000, 0.3000]

  sample(length, number=None, starting_state=None)

    Return number samples from this HMM of given length.

    INPUT:

    - `length` – positive integer

    - `number` – (default: `None`) if given, compute list of this many sample sequences

    - `starting_state` – integer (or `None`); if specified, generate a sequence using this model starting with the given state instead of the initial probabilities to determine the starting state

    OUTPUT:

    - if `number` is not given, return a single `TimeSeries`.

    - if `number` is given, return a list of `TimeSeries`.

    EXAMPLES:

    Sage

        sage: set_random_seed(0)
        sage: a = hmm.DiscreteHiddenMarkovModel([[0.1,0.9],[0.1,0.9]],
        ....:                                   [[1,0],[0,1]],
        ....:                                   [0,1])
        sage: print(a.sample(10, 3))
        [[1, 0, 1, 1, 1, 1, 0, 1, 1, 1],
         [1, 1, 0, 0, 1, 1, 1, 1, 1, 1],
         [1, 1, 1, 1, 0, 1, 0, 1, 1, 1]]
        sage: a.sample(15)
        [1, 1, 1, 1, 0 ... 1, 1, 1, 1, 1]
        sage: a.sample(3, 1)
        [[1, 1, 1]]
        sage: list(a.sample(1000)).count(0)
        88

    Python

        >>> from sage.all import *
        >>> set_random_seed(Integer(0))
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.1'),RealNumber('0.9')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[Integer(1),Integer(0)],[Integer(0),Integer(1)]],
        ...                                   [Integer(0),Integer(1)])
        >>> print(a.sample(Integer(10), Integer(3)))
        [[1, 0, 1, 1, 1, 1, 0, 1, 1, 1],
         [1, 1, 0, 0, 1, 1, 1, 1, 1, 1],
         [1, 1, 1, 1, 0, 1, 0, 1, 1, 1]]
        >>> a.sample(Integer(15))
        [1, 1, 1, 1, 0 ... 1, 1, 1, 1, 1]
        >>> a.sample(Integer(3), Integer(1))
        [[1, 1, 1]]
        >>> list(a.sample(Integer(1000))).count(Integer(0))
        88

    If the emission symbols are set:

    Sage

        sage: set_random_seed(0)
        sage: a = hmm.DiscreteHiddenMarkovModel([[0.5,0.5],[0.1,0.9]],
        ....:                                   [[1,0],[0,1]], [0,1],
        ....:                                   ['up', 'down'])
        sage: a.sample(10)
        ['down', 'up', 'down', 'down', 'down', 'down', 'up', 'up', 'up', 'up']

    [Python]

        >>> from sage.all import *
        >>> set_random_seed(Integer(0))
        >>> a = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.5'),RealNumber('0.5')],[RealNumber('0.1'),RealNumber('0.9')]],
        ...                                   [[Integer(1),Integer(0)],[Integer(0),Integer(1)]], [Integer(0),Integer(1)],
        ...                                   ['up', 'down'])
        >>> a.sample(Integer(10))
        ['down', 'up', 'down', 'down', 'down', 'down', 'up', 'up', 'up', 'up']

    Force a starting state:

    Sage

        sage: set_random_seed(0); a.sample(10, starting_state=0)
        ['up', 'up', 'down', 'down', 'down', 'down', 'up', 'up', 'up', 'up']

    Python

        >>> from sage.all import *
        >>> set_random_seed(Integer(0)); a.sample(Integer(10), starting_state=Integer(0))
        ['up', 'up', 'down', 'down', 'down', 'down', 'up', 'up', 'up', 'up']

  transition_matrix()

    Return the state transition matrix.

    OUTPUT: a Sage matrix with real double precision (RDF) entries.

    EXAMPLES:

    Sage

        sage: M = hmm.DiscreteHiddenMarkovModel([[0.7,0.3],[0.9,0.1]],
        ....:                                   [[0.5,.5],[.1,.9]],
        ....:                                   [0.3,0.7])
        sage: T = M.transition_matrix(); T
        [0.7 0.3]
        [0.9 0.1]

    Python

        >>> from sage.all import *
        >>> M = hmm.DiscreteHiddenMarkovModel([[RealNumber('0.7'),RealNumber('0.3')],[RealNumber('0.9'),RealNumber('0.1')]],
        ...                                   [[RealNumber('0.5'),RealNumber('.5')],[RealNumber('.1'),RealNumber('.9')]],
        ...                                   [RealNumber('0.3'),RealNumber('0.7')])
        >>> T = M.transition_matrix(); T
        [0.7 0.3]
        [0.9 0.1]

    The returned matrix is mutable, but changing it does not change the transition matrix for the model:

    Sage

        sage: T[0,0] = .1; T[0,1] = .9
        sage: M.transition_matrix()
        [0.7 0.3]
        [0.9 0.1]

    [Python]

        >>> from sage.all import *
        >>> T[Integer(0),Integer(0)] = RealNumber('.1'); T[Integer(0),Integer(1)] = RealNumber('.9')
        >>> M.transition_matrix()
        [0.7 0.3]
        [0.9 0.1]

    Transition matrices for other types of models:

    Sage

        sage: M = hmm.GaussianHiddenMarkovModel([[.1,.9],[.5,.5]],
        ....:                                   [(1,1), (-1,1)],
        ....:                                   [.5,.5])
        sage: M.transition_matrix()
        [0.1 0.9]
        [0.5 0.5]
        sage: M = hmm.GaussianMixtureHiddenMarkovModel(
        ....:         [[.9,.1],[.4,.6]],
        ....:         [[(.4,(0,1)), (.6,(1,0.1))],[(1,(0,1))]],
        ....:         [.7,.3])
        sage: M.transition_matrix()
        [0.9 0.1]
        [0.4 0.6]

    Python

        >>> from sage.all import *
        >>> M = hmm.GaussianHiddenMarkovModel([[RealNumber('.1'),RealNumber('.9')],[RealNumber('.5'),RealNumber('.5')]],
        ...                                   [(Integer(1),Integer(1)), (-Integer(1),Integer(1))],
        ...                                   [RealNumber('.5'),RealNumber('.5')])
        >>> M.transition_matrix()
        [0.1 0.9]
        [0.5 0.5]
        >>> M = hmm.GaussianMixtureHiddenMarkovModel(
        ...         [[RealNumber('.9'),RealNumber('.1')],[RealNumber('.4'),RealNumber('.6')]],
        ...         [[(RealNumber('.4'),(Integer(0),Integer(1))), (RealNumber('.6'),(Integer(1),RealNumber('0.1')))],[(Integer(1),(Integer(0),Integer(1)))]],
        ...         [RealNumber('.7'),RealNumber('.3')])
        >>> M.transition_matrix()
        [0.9 0.1]
        [0.4 0.6]

sage.stats.hmm.hmm.unpickle_discrete_hmm_v0(A, B, pi, emission_symbols, name)

sage.stats.hmm.hmm.unpickle_discrete_hmm_v1(A, B, pi, n_out, emission_symbols, emission_symbols_dict)

  Return a `DiscreteHiddenMarkovModel`, restored from the arguments.

  This function is used internally for unpickling.