Reachable DFA state equivalence
StateAmaranth connects finite-state fixtures to an AI conversation about your source. Review why states may be merged, keeping the transition table and acceptance behavior visible instead of relying on a suggested simplification alone.
def dfa_classes(table, accepting):
if type(table) is not list or not 1 <= len(table) <= 16:
raise ValueError("state count")
n = len(table)
if type(table[0]) is not list or not 1 <= len(table[0]) <= 4:
raise ValueError("alphabet size")
width = len(table[0])
if any(type(row) is not list or len(row) != width or
any(type(x) is not int or not 0 <= x < n for x in row)
for row in table):
raise ValueError("complete transition table")
if type(accepting) is not set or any(
type(x) is not int or not 0 <= x < n for x in accepting):
raise ValueError("accepting states")
seen, pending = {0}, [0]
while pending:
for dest in table[pending.pop()]:
if dest not in seen:
seen.add(dest); pending.append(dest)
groups = [g for g in (seen & accepting, seen - accepting) if g]
while True:
owner = {s: i for i, g in enumerate(groups) for s in g}
refined = []
for group in groups:
buckets = {}
for state in sorted(group):
key = tuple(owner[d] for d in table[state])
buckets.setdefault(key, set()).add(state)
refined.extend(buckets.values())
if len(refined) == len(groups):
return sorted([sorted(g) for g in refined])
groups = refined| Project | Check equivalent and distinguishable states, excluding unreachable states and rejecting invalid transitions. |
|---|---|
| Review before applying | Compare actual behavior with the expected result. If a check fails, investigate it before applying more changes. |
| Connection | Base URL, credentials and a reachable server. |
def dfa_classes(table, accepting):
if type(table) is not list or not 1 <= len(table) <= 16:
raise ValueError("state count")
n = len(table)
if type(table[0]) is not list or not 1 <= len(table[0]) <= 4:
raise ValueError("alphabet size")
width = len(table[0])
if any(type(row) is not list or len(row) != width or
any(type(x) is not int or not 0 <= x < n for x in row)
for row in table):
raise ValueError("complete transition table")
if type(accepting) is not set or any(
type(x) is not int or not 0 <= x < n for x in accepting):
raise ValueError("accepting states")
seen, pending = {0}, [0]
while pending:
for dest in table[pending.pop()]:
if dest not in seen:
seen.add(dest); pending.append(dest)
groups = [g for g in (seen & accepting, seen - accepting) if g]
while True:
owner = {s: i for i, g in enumerate(groups) for s in g}
refined = []
for group in groups:
buckets = {}
for state in sorted(group):
key = tuple(owner[d] for d in table[state])
buckets.setdefault(key, set()).add(state)
refined.extend(buckets.values())
if len(refined) == len(groups):
return sorted([sorted(g) for g in refined])
groups = refinedSimplify stateful code while preserving observable behavior
StateAmaranth is a Windows coding workspace for inspecting stateful software with a connected AI model. Begin with the events the system accepts and the outcomes a caller can observe. A request to simplify states needs that context so a smaller representation does not become the goal at the expense of the application's behavior.
The supplied deterministic-state example gives one concrete task: examining equivalence among reachable states while preserving acceptance behavior. Bring your transition table and intended initial state into the discussion. Ask the model to explain which distinctions matter before requesting changes, especially if the surrounding application exposes more than a simple accept-or-reject result.
Review any proposed grouping against short event sequences with known outcomes. Include paths that reach different states and cases involving unreachable entries, then run the checks in your project. Inspect the interface that consumes the grouping as well, because a change in state numbering or representation can break callers even when acceptance behavior is preserved.
An unrestricted AI model may be selected through a supported OpenAI-compatible or Anthropic endpoint that offers one. Configure credentials as necessary and verify context limits and tool availability. The model's response behavior remains endpoint-dependent; the evidence for a safe simplification comes from your stated observations and the checks you perform.
Questions for a state-machine refactor
- What should count as observable behavior in my request?
List the outputs or decisions callers rely on, not just internal state names. If transitions emit actions or errors, include them. The illustrated acceptance-based example may be narrower than the behavior your actual application must preserve.
- Can equivalent acceptance states always be merged in my app?
Not without checking the rest of the contract. Your application may expose identifiers, actions or other state-dependent effects. Ask the assistant to inspect those consumers before applying an equivalence argument based only on acceptance.
- How should unreachable states be handled during review?
State the initial state and how reachability is determined. Include a fixture with unreachable entries and the intended output. Review whether callers expect those entries to remain identifiable before accepting a proposed removal or regrouping.
- What should I test after state identifiers change?
Check transitions, stored references and any interface that displays or consumes identifiers. Run behavioral sequences as well as shape checks on the returned grouping. Preserved acceptance alone does not prove that dependent code still interprets the representation correctly.
StateAmaranth · Updated 2026-10-25