fn viterbi(&self, observed_sequence: &[u8]) -> Result<(f64, Vec<String>), String> {
        if observed_sequence.is_empty() {
            return Ok((0., Vec::new()));
        }
        #[allow(non_snake_case)]
        let mut V = Vec::new(); // this could be much faster if we mapped the states to usize and used an array instead.
        for _ in 0..observed_sequence.len() {
            V.push(HashMap::new())
        }
        let mut path = HashMap::new();
        for (state, emission) in self.states.iter() {
            V[0].insert(
                state,
                if *emission == observed_sequence[0] {
                    0.
                } else {
                    f64::NEG_INFINITY
                },
            );
            //V[0][state] = 0 if emission == observed_sequence[0] else float("-inf")
            path.insert(state, vec![state]);
        }

        for t in 1..observed_sequence.len() {
            let mut new_path = HashMap::new(); // we only keep the survivor paths for the latest t
            let mut any_survivors = false;
            for (to_state, emission) in self.states.iter() {
                // # for each state we could potentially end up
                if *emission == observed_sequence[t] {
                    //   this is valid transition, we can end up here
                    // let's find out what's the most likely state we could have come from
                    let mut survivor_score = f64::NEG_INFINITY;
                    let mut survivor_state = None;
                    for from_state in self.states.keys() {
                        //how expensive is it to go from from_state to to_state?
                        let transition_score = self
                            .transition_scores
                            .get(&(from_state.to_string(), to_state.to_string()))
                            .expect("invalid transition");
                        // and how good was being in from_state in the first place
                        let score_for_being_in_from_state = V[t - 1].get(from_state).unwrap();
                        let score_here = transition_score + score_for_being_in_from_state; // + in log is * in the normal scale
                        if score_here > survivor_score {
                            // it's best (so far) to have come from from_state!
                            survivor_score = score_here;
                            survivor_state = Some(from_state);
                            any_survivors = true; // this Hmm can have produced this sequence
                        }
                    }
                    match survivor_state {
                        Some(survivor_state) => {
                            let mut ns = path[survivor_state].clone();
                            ns.push(to_state);
                            new_path.insert(to_state, ns);
                            V[t].insert(to_state, survivor_score);
                        }
                        None => {
                            V[t].insert(to_state, f64::NEG_INFINITY);
                        }
                    }
                } else {
                    V[t].insert(to_state, f64::NEG_INFINITY);
                }
            }

            if !any_survivors {
                return Err(format!(
                    "This HMM can't have generated this sequence in position {}",
                    t
                ));
            }
            path = new_path
        }
        let final_scores = &V[V.len() - 1]; // hashmap state->score where we could have ended up
        let mut maximum_final_score = std::f64::NEG_INFINITY;
        let mut maximum_final_state = None;
        for state in final_scores.keys() {
            let fs = *final_scores.get(state).unwrap();
            if fs > maximum_final_score {
                maximum_final_score = fs;
                maximum_final_state = Some(state);
            }
        }
        match maximum_final_state {
            None => Err(format!(
                "This HMM can't have generated this sequence in position {}",
                observed_sequence.len() - 1
            )),
            Some(mf) => Ok((
                std::f64::consts::E.powf(maximum_final_score),
                path.get(mf)
                    .unwrap()
                    .into_iter()
                    .map(|x| x.to_string())
                    .collect(),
            )),
        }

        /*
                    """
                if not observed_sequence:
                    return 0, []

                V = (
                    []
                )  # the score of the survivor path that ended in state y at observed_sequence t
                for t in range(0, len(observed_sequence)):
                    if observed_sequence[t] not in self.allowed_emissions:
                        raise ValueError("Invalid emission: %s" % observed_sequence[t])
                    V.append({})

                path = (
                    {}
                )  # for the position we have reached in observed_sequence, the survivor paths for each possible state

                # initialize for leaving the 'virtual' beginning state'
                # score is 0 for a valid transition,
                # so this 'virtual' begin state does not affect the final score
                for state, emission in self.states.items():
                    V[0][state] = 0 if emission == observed_sequence[0] else float("-inf")
                    path[state] = [state]

                for t in range(1, len(observed_sequence)):
                    new_path = {}  # we only keep the survivor paths for the latest t

                    any_survivors = (
                        False  # to check if this HMM could have reached this state...
                    )
                    for (
                        to_state,
                        emission,
                    ) in self.states.items():  # for each state we could potentially end up
                        if (
                            emission == observed_sequence[t]
                        ):  # this is valid transition, we can end up here
                            # let's find out what's the most likely state we could have come from
                            survivor_score = float("-inf")
                            survivor_state = None
                            for from_state, emission in self.states.items():
                                # how expensive is it to go from from_state to to_state?
                                transition_score = self.transition_scores[from_state, to_state]
                                # and how good was being in from_state in the first place
                                score_for_being_in_from_state = V[t - 1][from_state]
                                score_here = (
                                    transition_score + score_for_being_in_from_state
                                )  # + in log is * in the normal scale
                                if (
                                    score_here > survivor_score
                                ):  # it's best (so far) to have come from from_state!
                                    survivor_score = score_here
                                    survivor_state = from_state
                                    any_survivors = (
                                        True  # this Hmm can have produced this sequence
                                    )
                            if (
                                survivor_state is not None
                            ):  # there is no way we can have ended up in to_state. But there might be other states with the right emission
                                new_path[to_state] = path[survivor_state] + [to_state]
                                V[t][to_state] = survivor_score
                            else:
                                V[t][to_state] = float("-inf")
                        else:
                            # new_path[to_state] = None #no access to this path should happen. Not having this line would make it throw a KeyError if access did happen.
                            V[t][to_state] = float("-inf")

                    if not any_survivors:
                        raise ModelSequenceMismatchException(
                            "This HMM can't have generated this sequence in position %i" % t
                        )
                    path = new_path
                final_scores = V[-1]
                maximum_final_score = float("-inf")
                maximum_final_state = None
                for state in final_scores:
                    if final_scores[state] > maximum_final_score:
                        maximum_final_score = final_scores[state]
                        maximum_final_state = state
                if maximum_final_state is None:
                    raise ModelSequenceMismatchException(
                        "This HMM can't have generated this sequence in position %i"
                        % len(observed_sequence)
                    )
                return pow(2, maximum_final_score), path[maximum_final_state]


        &*/
    }
/// Given a list of observed emissions,
/// give us the maximal score and path through the hidden markov model
/// @observed_sequence: an iterable of symbols
/// result: (maximum_score, [state_0, state_1, state_2..., state_len(observed_sequence)])
Given a list of observed emissions,
give us the maximal score and path through the hidden markov model
Given a list of observed emissions give us the maximal score and path through the hidden markov model
fn viterbi(&self, observed_sequence: &[u8]) -> Result<(f64, Vec<String>), String> {
        if observed_sequence.is_empty() {
            return Ok((0., Vec::new()));
        }
        #[allow(non_snake_case)]
        let mut V = Vec::new(); // this could be much faster if we mapped the states to usize and used an array instead.
        for _ in 0..observed_sequence.len() {
            V.push(HashMap::new())
        }
        let mut path = HashMap::new();
        for (state, emission) in self.states.iter() {
            V[0].insert(
                state,
                if *emission == observed_sequence[0] {
                    0.
                } else {
                    f64::NEG_INFINITY
                },
            );
            //V[0][state] = 0 if emission == observed_sequence[0] else float("-inf")
            path.insert(state, vec![state]);
        }
        for t in 1..observed_sequence.len() {
            let mut new_path = HashMap::new(); // we only keep the survivor paths for the latest t
            let mut any_survivors = false;
            for (to_state, emission) in self.states.iter() {
                // # for each state we could potentially end up
                if *emission == observed_sequence[t] {
                    //   this is valid transition, we can end up here
                    // let's find out what's the most likely state we could have come from
                    let mut survivor_score = f64::NEG_INFINITY;
                    let mut survivor_state = None;
                    for from_state in self.states.keys() {
                        //how expensive is it to go from from_state to to_state?
                        let transition_score = self
                            .transition_scores
                            .get(&(from_state.to_string(), to_state.to_string()))
                            .expect("invalid transition");
                        // and how good was being in from_state in the first place
                        let score_for_being_in_from_state = V[t - 1].get(from_state).unwrap();
                        let score_here = transition_score + score_for_being_in_from_state; // + in log is * in the normal scale
                        if score_here > survivor_score {
                            // it's best (so far) to have come from from_state!
                            survivor_score = score_here;
                            survivor_state = Some(from_state);
                            any_survivors = true; // this Hmm can have produced this sequence
                        }
                    }
                    match survivor_state {
                        Some(survivor_state) => {
                            let mut ns = path[survivor_state].clone();
                            ns.push(to_state);
                            new_path.insert(to_state, ns);
                            V[t].insert(to_state, survivor_score);
                        }
                        None => {
                            V[t].insert(to_state, f64::NEG_INFINITY);
                        }
                    }
                } else {
                    V[t].insert(to_state, f64::NEG_INFINITY);
                }
            }
            if !any_survivors {
                return Err(format!(
                    "This HMM can't have generated this sequence in position {}",
                    t
                ));
            }
            path = new_path
        }
        let final_scores = &V[V.len() - 1]; // hashmap state->score where we could have ended up
        let mut maximum_final_score = std::f64::NEG_INFINITY;
        let mut maximum_final_state = None;
        for state in final_scores.keys() {
            let fs = *final_scores.get(state).unwrap();
            if fs > maximum_final_score {
                maximum_final_score = fs;
                maximum_final_state = Some(state);
            }
        }
        match maximum_final_state {
            None => Err(format!(
                "This HMM can't have generated this sequence in position {}",
                observed_sequence.len() - 1
            )),
            Some(mf) => Ok((
                std::f64::consts::E.powf(maximum_final_score),
                path.get(mf)
                    .unwrap()
                    .into_iter()
                    .map(|x| x.to_string())
                    .collect(),
            )),
        }
        /*
                    """
                if not observed_sequence:
                    return 0, []
                V = (
                    []
                )  # the score of the survivor path that ended in state y at observed_sequence t
                for t in range(0, len(observed_sequence)):
                    if observed_sequence[t] not in self.allowed_emissions:
                        raise ValueError("Invalid emission: %s" % observed_sequence[t])
                    V.append({})
                path = (
                    {}
                )  # for the position we have reached in observed_sequence, the survivor paths for each possible state
                # initialize for leaving the 'virtual' beginning state'
                # score is 0 for a valid transition,
                # so this 'virtual' begin state does not affect the final score
                for state, emission in self.states.items():
                    V[0][state] = 0 if emission == observed_sequence[0] else float("-inf")
                    path[state] = [state]
                for t in range(1, len(observed_sequence)):
                    new_path = {}  # we only keep the survivor paths for the latest t
                    any_survivors = (
                        False  # to check if this HMM could have reached this state...
                    )
                    for (
                        to_state,
                        emission,
                    ) in self.states.items():  # for each state we could potentially end up
                        if (
                            emission == observed_sequence[t]
                        ):  # this is valid transition, we can end up here
                            # let's find out what's the most likely state we could have come from
                            survivor_score = float("-inf")
                            survivor_state = None
                            for from_state, emission in self.states.items():
                                # how expensive is it to go from from_state to to_state?
                                transition_score = self.transition_scores[from_state, to_state]
                                # and how good was being in from_state in the first place
                                score_for_being_in_from_state = V[t - 1][from_state]
                                score_here = (
                                    transition_score + score_for_being_in_from_state
                                )  # + in log is * in the normal scale
                                if (
                                    score_here > survivor_score
                                ):  # it's best (so far) to have come from from_state!
                                    survivor_score = score_here
                                    survivor_state = from_state
                                    any_survivors = (
                                        True  # this Hmm can have produced this sequence
                                    )
                            if (
                                survivor_state is not None
                            ):  # there is no way we can have ended up in to_state. But there might be other states with the right emission
                                new_path[to_state] = path[survivor_state] + [to_state]
                                V[t][to_state] = survivor_score
                            else:
                                V[t][to_state] = float("-inf")
                        else:
                            # new_path[to_state] = None #no access to this path should happen. Not having this line would make it throw a KeyError if access did happen.
                            V[t][to_state] = float("-inf")
                    if not any_survivors:
                        raise ModelSequenceMismatchException(
                            "This HMM can't have generated this sequence in position %i" % t
                        )
                    path = new_path
                final_scores = V[-1]
                maximum_final_score = float("-inf")
                maximum_final_state = None
                for state in final_scores:
                    if final_scores[state] > maximum_final_score:
                        maximum_final_score = final_scores[state]
                        maximum_final_state = state
                if maximum_final_state is None:
                    raise ModelSequenceMismatchException(
                        "This HMM can't have generated this sequence in position %i"
                        % len(observed_sequence)
                    )
                return pow(2, maximum_final_score), path[maximum_final_state]
        &*/
    }
fn viterbi ( & self , observed_sequence : & [ u8 ] ) -> Result < ( f64 , Vec < String > ) , String > { if observed_sequence . is_empty ( ) { return Ok ( ( 0. , Vec :: new ( ) ) ) ; } # [ allow ( non_snake_case ) ] let mut V = Vec :: new ( ) ; // this could be much faster if we mapped the states to usize and used an array instead. for _ in 0 .. observed_sequence . len ( ) { V . push ( HashMap :: new ( ) ) } let mut path = HashMap :: new ( ) ; for ( state , emission ) in self . states . iter ( ) { V [ 0 ] . insert ( state , if * emission == observed_sequence [ 0 ] { 0. } else { f64 :: NEG_INFINITY } , ) ; //V[0][state] = 0 if emission == observed_sequence[0] else float("-inf") path . insert ( state , vec ! [ state ] ) ; } for t in 1 .. observed_sequence . len ( ) { let mut new_path = HashMap :: new ( ) ; // we only keep the survivor paths for the latest t let mut any_survivors = false ; for ( to_state , emission ) in self . states . iter ( ) { // # for each state we could potentially end up if * emission == observed_sequence [ t ] { //   this is valid transition, we can end up here // let's find out what's the most likely state we could have come from let mut survivor_score = f64 :: NEG_INFINITY ; let mut survivor_state = None ; for from_state in self . states . keys ( ) { //how expensive is it to go from from_state to to_state? let transition_score = self . transition_scores . get ( & ( from_state . to_string ( ) , to_state . to_string ( ) ) ) . expect ( " " ) ; // and how good was being in from_state in the first place let score_for_being_in_from_state = V [ t - 1 ] . get ( from_state ) . unwrap ( ) ; let score_here = transition_score + score_for_being_in_from_state ; // + in log is * in the normal scale if score_here > survivor_score { // it's best (so far) to have come from from_state! survivor_score = score_here ; survivor_state = Some ( from_state ) ; any_survivors = true ; // this Hmm can have produced this sequence } } match survivor_state { Some ( survivor_state ) => { let mut ns = path [ survivor_state ] . clone ( ) ; ns . push ( to_state ) ; new_path . insert ( to_state , ns ) ; V [ t ] . insert ( to_state , survivor_score ) ; } None => { V [ t ] . insert ( to_state , f64 :: NEG_INFINITY ) ; } } } else { V [ t ] . insert ( to_state , f64 :: NEG_INFINITY ) ; } } if ! any_survivors { return Err ( format ! ( " " t ) ) ; } path = new_path } let final_scores = & V [ V . len ( ) - 1 ] ; // hashmap state->score where we could have ended up let mut maximum_final_score = std :: f64 :: NEG_INFINITY ; let mut maximum_final_state = None ; for state in final_scores . keys ( ) { let fs = * final_scores . get ( state ) . unwrap ( ) ; if fs > maximum_final_score { maximum_final_score = fs ; maximum_final_state = Some ( state ) ; } } match maximum_final_state { None => Err ( format ! ( " " observed_sequence len ( ) 1 ) ) , Some ( mf ) => Ok ( ( std :: f64 :: consts :: E . powf ( maximum_final_score ) , path . get ( mf ) . unwrap ( ) . into_iter ( ) . map ( | x | x . to_string ( ) ) . collect ( ) , ) ) , } /*
                    """
                if not observed_sequence:
                    return 0, []

                V = (
                    []
                )  # the score of the survivor path that ended in state y at observed_sequence t
                for t in range(0, len(observed_sequence)):
                    if observed_sequence[t] not in self.allowed_emissions:
                        raise ValueError("Invalid emission: %s" % observed_sequence[t])
                    V.append({})

                path = (
                    {}
                )  # for the position we have reached in observed_sequence, the survivor paths for each possible state

                # initialize for leaving the 'virtual' beginning state'
                # score is 0 for a valid transition,
                # so this 'virtual' begin state does not affect the final score
                for state, emission in self.states.items():
                    V[0][state] = 0 if emission == observed_sequence[0] else float("-inf")
                    path[state] = [state]

                for t in range(1, len(observed_sequence)):
                    new_path = {}  # we only keep the survivor paths for the latest t

                    any_survivors = (
                        False  # to check if this HMM could have reached this state...
                    )
                    for (
                        to_state,
                        emission,
                    ) in self.states.items():  # for each state we could potentially end up
                        if (
                            emission == observed_sequence[t]
                        ):  # this is valid transition, we can end up here
                            # let's find out what's the most likely state we could have come from
                            survivor_score = float("-inf")
                            survivor_state = None
                            for from_state, emission in self.states.items():
                                # how expensive is it to go from from_state to to_state?
                                transition_score = self.transition_scores[from_state, to_state]
                                # and how good was being in from_state in the first place
                                score_for_being_in_from_state = V[t - 1][from_state]
                                score_here = (
                                    transition_score + score_for_being_in_from_state
                                )  # + in log is * in the normal scale
                                if (
                                    score_here > survivor_score
                                ):  # it's best (so far) to have come from from_state!
                                    survivor_score = score_here
                                    survivor_state = from_state
                                    any_survivors = (
                                        True  # this Hmm can have produced this sequence
                                    )
                            if (
                                survivor_state is not None
                            ):  # there is no way we can have ended up in to_state. But there might be other states with the right emission
                                new_path[to_state] = path[survivor_state] + [to_state]
                                V[t][to_state] = survivor_score
                            else:
                                V[t][to_state] = float("-inf")
                        else:
                            # new_path[to_state] = None #no access to this path should happen. Not having this line would make it throw a KeyError if access did happen.
                            V[t][to_state] = float("-inf")

                    if not any_survivors:
                        raise ModelSequenceMismatchException(
                            "This HMM can't have generated this sequence in position %i" % t
                        )
                    path = new_path
                final_scores = V[-1]
                maximum_final_score = float("-inf")
                maximum_final_state = None
                for state in final_scores:
                    if final_scores[state] > maximum_final_score:
                        maximum_final_score = final_scores[state]
                        maximum_final_state = state
                if maximum_final_state is None:
                    raise ModelSequenceMismatchException(
                        "This HMM can't have generated this sequence in position %i"
                        % len(observed_sequence)
                    )
                return pow(2, maximum_final_score), path[maximum_final_state]


        &*/ }
Given a list of observed emissions,
give us the maximal score and path through the hidden markov model
Given a list of observed emissions give us the maximal score and path through the hidden markov model
&self
observed_sequence
&[u8]
observed_sequence:
an iterable of symbols
result: (maximum_score, [state_0, state_1, state_2..., state_len(observed_sequence)])
an iterable of symbols result : ( maximum_score [ state_0 state_1 state_2 ... state_len ( observed_sequence ) ] )