GitLab Repo

amachine.am_create.am_cyclic_orbit_cluster

 1from amachine.am_id_generator import next_group_id
 2from ..am_hmm          import HMM
 3from ..am_vocabulary   import Vocabulary
 4
 5from .am_random_machine import random_machine
 6from .am_isomorphic_to import isomorphic_to
 7from .am_star_composition import star_composition
 8
 9import numpy as np
10
11def cyclic_orbit_cluster(
12    n_states : int,
13    n_base_symbols : int,
14    connectedness : float,
15    randomness : float,
16    star_joined : bool,
17    residency_factor : float = 0.75,
18    instances_per_component : int = 1,
19    np_rng : np.random.Generator | None = None ) -> HMM | list[HMM] :
20
21    base_symbol_pool  = Vocabulary.digits()      + Vocabulary.letters_lower()
22    enter_symbol_pool = Vocabulary.greek_lower() + Vocabulary.greek_upper()
23    exit_symbol = '*'
24
25    max_n_symbols = min( len(base_symbol_pool), len(enter_symbol_pool) )
26
27    if n_base_symbols > max_n_symbols :
28        raise ValueError( f"Only up to {len(base_symbol_pool)} base symbols supported" )
29
30    alphabet = base_symbol_pool[ 0:n_base_symbols ]
31    enter_symbols = enter_symbol_pool[ 0:n_base_symbols ]
32
33    m = random_machine( 
34        n_states=n_states, 
35        symbols=alphabet, 
36        randomness=randomness,
37        connectedness=connectedness,
38        ensure_strongly_connected=True,
39        ensure_minimal=True,
40        np_rng=np_rng )
41    
42    group_id = next_group_id()
43
44    machines = []
45    for i in range( n_base_symbols ) :
46        
47        alphabet = alphabet[ -1: ] + alphabet[ 0 : n_base_symbols - 1 ]
48        im = isomorphic_to( m, alphabet=alphabet.copy(), decorator=enter_symbols[ i ] )
49        machines.append( im )
50
51    for im in machines :
52
53        new_states = []
54
55        for s_idx, state in enumerate( im.states ) :
56            
57            # get the undecorated name
58            undecorated   = state.name[ 0:-1 ]
59            my_decoration = state.name[-1]
60
61            isomorphs = set()
62
63            # capture decorated names of all isomorphic states
64            for a in enter_symbols :
65
66                # self not storing self as isomorph
67                if a == my_decoration :
68                    continue
69
70                decorated = undecorated + a
71                isomorphs.add( decorated )
72            
73            new_states.append( im.states[ s_idx ].modified_deep_copy(
74                isomorphs=isomorphs
75            ) )
76
77        im.set_states( new_states )
78
79    if star_joined :
80
81        final_machine = star_composition(
82            exit_symbol=exit_symbol, 
83            enter_symbols=enter_symbols,
84            component_residency_factor=residency_factor,
85            component_groups={ group_id : machines },
86            instances_per_component=instances_per_component
87        )
88
89        if not final_machine.is_row_stochastic() :
90            raise Exception( "Cyclic cluster is not row stochastic" )
91
92        if not final_machine.is_unifilar() :
93            raise Exception( "Cyclic cluster is not unifilar" )
94
95        return final_machine
96
97    else : 
98        return machines
def cyclic_orbit_cluster( n_states: int, n_base_symbols: int, connectedness: float, randomness: float, star_joined: bool, residency_factor: float = 0.75, instances_per_component: int = 1, np_rng: numpy.random._generator.Generator | None = None) -> amachine.am_hmm.HMM | list[amachine.am_hmm.HMM]:
12def cyclic_orbit_cluster(
13    n_states : int,
14    n_base_symbols : int,
15    connectedness : float,
16    randomness : float,
17    star_joined : bool,
18    residency_factor : float = 0.75,
19    instances_per_component : int = 1,
20    np_rng : np.random.Generator | None = None ) -> HMM | list[HMM] :
21
22    base_symbol_pool  = Vocabulary.digits()      + Vocabulary.letters_lower()
23    enter_symbol_pool = Vocabulary.greek_lower() + Vocabulary.greek_upper()
24    exit_symbol = '*'
25
26    max_n_symbols = min( len(base_symbol_pool), len(enter_symbol_pool) )
27
28    if n_base_symbols > max_n_symbols :
29        raise ValueError( f"Only up to {len(base_symbol_pool)} base symbols supported" )
30
31    alphabet = base_symbol_pool[ 0:n_base_symbols ]
32    enter_symbols = enter_symbol_pool[ 0:n_base_symbols ]
33
34    m = random_machine( 
35        n_states=n_states, 
36        symbols=alphabet, 
37        randomness=randomness,
38        connectedness=connectedness,
39        ensure_strongly_connected=True,
40        ensure_minimal=True,
41        np_rng=np_rng )
42    
43    group_id = next_group_id()
44
45    machines = []
46    for i in range( n_base_symbols ) :
47        
48        alphabet = alphabet[ -1: ] + alphabet[ 0 : n_base_symbols - 1 ]
49        im = isomorphic_to( m, alphabet=alphabet.copy(), decorator=enter_symbols[ i ] )
50        machines.append( im )
51
52    for im in machines :
53
54        new_states = []
55
56        for s_idx, state in enumerate( im.states ) :
57            
58            # get the undecorated name
59            undecorated   = state.name[ 0:-1 ]
60            my_decoration = state.name[-1]
61
62            isomorphs = set()
63
64            # capture decorated names of all isomorphic states
65            for a in enter_symbols :
66
67                # self not storing self as isomorph
68                if a == my_decoration :
69                    continue
70
71                decorated = undecorated + a
72                isomorphs.add( decorated )
73            
74            new_states.append( im.states[ s_idx ].modified_deep_copy(
75                isomorphs=isomorphs
76            ) )
77
78        im.set_states( new_states )
79
80    if star_joined :
81
82        final_machine = star_composition(
83            exit_symbol=exit_symbol, 
84            enter_symbols=enter_symbols,
85            component_residency_factor=residency_factor,
86            component_groups={ group_id : machines },
87            instances_per_component=instances_per_component
88        )
89
90        if not final_machine.is_row_stochastic() :
91            raise Exception( "Cyclic cluster is not row stochastic" )
92
93        if not final_machine.is_unifilar() :
94            raise Exception( "Cyclic cluster is not unifilar" )
95
96        return final_machine
97
98    else : 
99        return machines