Gsm v2 - #111
Open
emuskardin wants to merge 59 commits into
Open
Conversation
…initial output for Moore
… and added string value for short-lex
… children at any point.
…oreCalculation.__init__`. also some other score calc stuff and docstrings
emuskardin
requested review from
zwergziege
and removed request for
zwergziege
September 9, 2026 17:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Blocking
1.
has_local_compatibility/has_score_functionare inverted —aalpy/learning_algs/general_passive/ScoreFunctionsGSM.py:91Both return
Truewhen the method is not overridden. Combined with the guard at line 170,SimpleFutureBasedCompatibility(local_compatibility=..., ...)always raises.Repro:
run_GSM(data, transition_behavior="stochastic")→ValueError("Exernal local compatibility is provided…")atGeneralizedStateMerging.py:130. Also breaksExamples.example_Alergia_extensionandgsm_IOAlergia_domain_knowldege.2.
CountDataHandler.copydrops thedefaultdict—aalpy/learning_algs/general_passive/DataHandler.py:264copyrebuildstransition_countas a plaindict, butmerge(line 257) relies ondefaultdict(x.transition_count[in_sym]). Any reversible (first-pass) partitioning over count data crashes.Repro:
run_GSM(..., transition_behavior='stochastic', score_calc=SimpleScoreCalculation(score_function=...))→KeyError: 'b'.3.
add_labeled_sequencenever callsinit_data()—aalpy/learning_algs/general_passive/DataHandler.py:104Children are created as
GsmNode((in_sym, out_sym), curr_node)withoutself.init_data(), unlikeadd_traceat line 76, sodata is None.Repro:
CountDataHandler().createPTA(labeled_sequences)→AttributeError: 'NoneType' object has no attribute 'transition_count'.4. Deterministic compatibility is no longer enforced —
aalpy/learning_algs/general_passive/GeneralizedStateMerging.py:385The removed
compute_local_compatibilityenforcedGsmNode.deterministic_compatiblefortransition_behavior="deterministic"regardless ofscore_calc; the replacement only checks Moore compatibility, and only whenoutput_behavior == "moore". The same hunk downgrades the nondeterministic-input check towarnings.warn(line 179), removing the second guard.Repro: a mealy/deterministic run with a user-supplied
score_calc(Examples.gsm_edsm, or anySimpleScoreCalculation(score_function=...)) yields a root with'a': ['x','y']andis_deterministic() == False, whichto_automatonthen silently collapses.5.
CacheSUL.__getattr__recurses infinitely —aalpy/base/SUL.py:136Recurses whenever
self.sulis absent, e.g. the__setstate__lookup during unpickling.Repro:
pickle.loads(pickle.dumps(CacheSUL(AutomatonSUL(dfa))))→RecursionError.Fix: guard with
if name == 'sul': raise AttributeError(name)(or useobject.__getattribute__).Correctness
6.
best_candidateupdate is nested in the cache-miss branch —GeneralizedStateMerging.py:216The
best_candidate/best_scoreupdate sits insideif partitioning is None:. Cached partitionings survive promotions (only merges clear the cache), so from the round after a promotion onward a cached candidate can never be selected as best — even one withImmediateAccept. The best is then chosen only among freshly computed pairs, giving a different/worse merge sequence than intended.7.
ScoreCombinatorfakes an early verdict —ScoreFunctionsGSM.py:329initialize_mergeaggregates sub-results withaggregate_score, whose default returns a list. Sub-scores with no early verdict returnNone, so the result is[None, None], whichis not SpecialScores.NoScore. GSM treats that as an early verdict, never builds the partitioning, and never calls the combinator'slocal_compatibilityorscore_function— everyScoreCombinatoraccepts merges on a meaningless score.8.
CountOnPTADataHandler.copyloses PTA state —DataHandler.py:275It inherits
copyfromCountDataHandler, which returns a bareCountData, droppingpta_countandshadow_ptaand changing the type. When such a copy is written back (GeneralizedStateMerging.py:250), laterhoeffding_compatibility(compare_original=True)raisesAttributeError: pta_count, andSimpleFutureBasedCompatibilityraises "no PTA data is available". Reachable wheneverCountOnPTADataHandleris combined with a score calc that has no early verdict.9.
EDSM_scorechecks are dead —GsmAlgorithms.py:40It still tests
node.get_prefix_output() is Noneto skip "no evidence" nodes, butunknown_outputchanged fromNonetoobject()(GsmNode.py:32). Both checks (lines 40 and 44) are now dead, so unlabeled nodes count as evidence andrun_EDSM's scoring differs from its intent.10. Stale cached partitioning misses newly-blue successors —
GeneralizedStateMerging.py:241After a promotion only the promoted blue's cache entries are dropped, but
red_states_backing_sethas grown. Cached partitionings capturedred_partitionsat first-pass time; when such a candidate is later applied the second pass returns early and reuses the stalenew_bluelist, so successors that should have become blue are never enqueued. Currently masked by finding 6 — it goes live once that is fixed.11.
moore_checkusesredinstead ofpartition—GeneralizedStateMerging.py:386It calls
GsmNode.moore_compatible(red, blue)on the originalredrather than onpartition, whose prefix output may already have been resolved byresolve_unknown_prefix_output. The check is strictly more permissive than intended and can let a Moore-incompatible implied merge through.API / reporting
12.
run_Alergia_EDSMdropped from package exports —aalpy/learning_algs/__init__.py:15Replaced by
run_Alergia_GSM, sofrom aalpy.learning_algs import run_Alergia_EDSM— previously public API — now fails. The alias survives only insideGsmAlgorithms(line 160), which is what the test imports.13.
nr_merged_statesis double-counted —GeneralizedStateMerging.py:381Incremented in both passes, so pairs deferred to
remaining_mergesare counted twice.ProgressReport.log_merge(Instrumentation.py:98) accumulates this, making the printed "Remaining" count drift and go negative.14. First broken promotion goes unreported —
Instrumentation.py:154In
MergeViolationDebugger.log_promotethe newelif node is Nonebranch is unreachable for the first broken promotion:self.map.get(None)isNone, so theold_red is Nonebranch fires first, insertsNoneas a map key and logs a normal"promote". Only the second broken promotion is reported.