GitLab Repo

amachine.am_create.am_isomorphic_to_with_superstate_permutations

  1import copy
  2from collections import defaultdict
  3
  4from ..am_hmm    import HMM
  5from ..am_random import unique_random_permutations
  6
  7import numpy as np
  8
  9
 10def isomorphic_to_with_superstate_permutations(
 11    m : HMM,
 12    superstate_connectors : list[ tuple[int,int] ],
 13    n_permutations : int,
 14    decorator : str,
 15    np_rng : np.random.Generator | None = None ) -> list[ HMM ] :
 16
 17    n_superstates = len( superstate_connectors )
 18
 19    permutations = unique_random_permutations(
 20        [ n_superstates ],
 21        n_samples=n_permutations-1,
 22        np_rng=np_rng
 23    )
 24
 25    permutations = [
 26        p[ 0 ] for p in permutations
 27    ]
 28
 29    enter_state_to_superstate : dict[ int, int ] = {}
 30    exit_state_to_superstate  : dict[ int, int ] = {}
 31    connector_states : set[int] = set()
 32
 33    for i, ( entry_state, exit_state ) in enumerate( superstate_connectors ) :
 34        enter_state_to_superstate[ entry_state ] = i
 35        exit_state_to_superstate[   exit_state ] = i
 36        connector_states.add( entry_state )
 37        connector_states.add( exit_state  )
 38
 39    result = [ copy.deepcopy( m ) ]
 40
 41    def is_external( tr ) :
 42        return ( tr.origin_state_idx in exit_state_to_superstate 
 43             and tr.target_state_idx in enter_state_to_superstate 
 44             and exit_state_to_superstate[ tr.origin_state_idx ] 
 45              != enter_state_to_superstate[ tr.target_state_idx ] )
 46
 47    for p_idx, permutation in enumerate( permutations ) :
 48
 49        alphabet = tuple(m.alphabet)
 50
 51        states = [ 
 52            s.modified_deep_copy( name=f"{s.name}{decorator}" )
 53            for s in m.states
 54        ]
 55
 56        exit_external_mass          : dict[ int, float ] = defaultdict( float )
 57        exit_external_mass_permuted : dict[ int, float ] = defaultdict( float )
 58
 59        transitions = []
 60        for i, tr in enumerate( m.transitions ) :
 61
 62            new_origin_idx = tr.origin_state_idx
 63            new_target_idx = tr.target_state_idx
 64
 65            if is_external( tr ) :
 66                 
 67                origin_superstate =  exit_state_to_superstate[ tr.origin_state_idx ]
 68                target_superstate = enter_state_to_superstate[ tr.target_state_idx ]
 69
 70                new_origin_superstate = permutation[ origin_superstate ]
 71                new_target_superstate = permutation[ target_superstate ]
 72
 73                new_origin_idx = superstate_connectors[ new_origin_superstate ][ 1 ]
 74                new_target_idx = superstate_connectors[ new_target_superstate ][ 0 ]
 75
 76                exit_external_mass[     tr.origin_state_idx ] += tr.prob
 77                exit_external_mass_permuted[ new_origin_idx ] += tr.prob
 78
 79            transitions.append( 
 80                tr.modified_deep_copy( 
 81                    origin_state_idx=new_origin_idx,
 82                    target_state_idx=new_target_idx 
 83                ) 
 84            )
 85
 86        # adjust the probabilities of the between superstate nodes
 87        # to preserve row-stochasticity
 88        for i, tr in enumerate( transitions ) :
 89            if is_external( tr ) :
 90                original_mass = exit_external_mass[ tr.origin_state_idx ]
 91                new_mass      = exit_external_mass_permuted[ tr.origin_state_idx ]
 92                transitions[ i ] = tr.modified_deep_copy(
 93                    prob = tr.prob * ( original_mass / new_mass )
 94                )
 95
 96        res = HMM(
 97            states=states,
 98            transitions=transitions,
 99            start_state=0,
100            alphabet=alphabet
101        )
102
103        if not res.is_row_stochastic() :
104            raise Exception( "Superstate permutation is not row stochastic" )
105
106        if not  res.is_unifilar() :
107            raise Exception( "Superstate permutation is not unifilar" )
108
109        result.append( res )
110
111    return result
def isomorphic_to_with_superstate_permutations( m: amachine.am_hmm.HMM, superstate_connectors: list[tuple[int, int]], n_permutations: int, decorator: str, np_rng: numpy.random._generator.Generator | None = None) -> list[amachine.am_hmm.HMM]:
 11def isomorphic_to_with_superstate_permutations(
 12    m : HMM,
 13    superstate_connectors : list[ tuple[int,int] ],
 14    n_permutations : int,
 15    decorator : str,
 16    np_rng : np.random.Generator | None = None ) -> list[ HMM ] :
 17
 18    n_superstates = len( superstate_connectors )
 19
 20    permutations = unique_random_permutations(
 21        [ n_superstates ],
 22        n_samples=n_permutations-1,
 23        np_rng=np_rng
 24    )
 25
 26    permutations = [
 27        p[ 0 ] for p in permutations
 28    ]
 29
 30    enter_state_to_superstate : dict[ int, int ] = {}
 31    exit_state_to_superstate  : dict[ int, int ] = {}
 32    connector_states : set[int] = set()
 33
 34    for i, ( entry_state, exit_state ) in enumerate( superstate_connectors ) :
 35        enter_state_to_superstate[ entry_state ] = i
 36        exit_state_to_superstate[   exit_state ] = i
 37        connector_states.add( entry_state )
 38        connector_states.add( exit_state  )
 39
 40    result = [ copy.deepcopy( m ) ]
 41
 42    def is_external( tr ) :
 43        return ( tr.origin_state_idx in exit_state_to_superstate 
 44             and tr.target_state_idx in enter_state_to_superstate 
 45             and exit_state_to_superstate[ tr.origin_state_idx ] 
 46              != enter_state_to_superstate[ tr.target_state_idx ] )
 47
 48    for p_idx, permutation in enumerate( permutations ) :
 49
 50        alphabet = tuple(m.alphabet)
 51
 52        states = [ 
 53            s.modified_deep_copy( name=f"{s.name}{decorator}" )
 54            for s in m.states
 55        ]
 56
 57        exit_external_mass          : dict[ int, float ] = defaultdict( float )
 58        exit_external_mass_permuted : dict[ int, float ] = defaultdict( float )
 59
 60        transitions = []
 61        for i, tr in enumerate( m.transitions ) :
 62
 63            new_origin_idx = tr.origin_state_idx
 64            new_target_idx = tr.target_state_idx
 65
 66            if is_external( tr ) :
 67                 
 68                origin_superstate =  exit_state_to_superstate[ tr.origin_state_idx ]
 69                target_superstate = enter_state_to_superstate[ tr.target_state_idx ]
 70
 71                new_origin_superstate = permutation[ origin_superstate ]
 72                new_target_superstate = permutation[ target_superstate ]
 73
 74                new_origin_idx = superstate_connectors[ new_origin_superstate ][ 1 ]
 75                new_target_idx = superstate_connectors[ new_target_superstate ][ 0 ]
 76
 77                exit_external_mass[     tr.origin_state_idx ] += tr.prob
 78                exit_external_mass_permuted[ new_origin_idx ] += tr.prob
 79
 80            transitions.append( 
 81                tr.modified_deep_copy( 
 82                    origin_state_idx=new_origin_idx,
 83                    target_state_idx=new_target_idx 
 84                ) 
 85            )
 86
 87        # adjust the probabilities of the between superstate nodes
 88        # to preserve row-stochasticity
 89        for i, tr in enumerate( transitions ) :
 90            if is_external( tr ) :
 91                original_mass = exit_external_mass[ tr.origin_state_idx ]
 92                new_mass      = exit_external_mass_permuted[ tr.origin_state_idx ]
 93                transitions[ i ] = tr.modified_deep_copy(
 94                    prob = tr.prob * ( original_mass / new_mass )
 95                )
 96
 97        res = HMM(
 98            states=states,
 99            transitions=transitions,
100            start_state=0,
101            alphabet=alphabet
102        )
103
104        if not res.is_row_stochastic() :
105            raise Exception( "Superstate permutation is not row stochastic" )
106
107        if not  res.is_unifilar() :
108            raise Exception( "Superstate permutation is not unifilar" )
109
110        result.append( res )
111
112    return result