Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 7718ce4d authored by cha's avatar cha
Browse files

weird bug on minimize, probably due to normalization? or hashing?

parent fec098f5
No related branches found
No related tags found
No related merge requests found
all: install test
install:
sage -pip install --upgrade --no-index -v .
pip install --upgrade --no-index -v .
test:
sage -tp --force-lib pysemigroup/*py
coverage:
sage -coverage pysemigroup/*
diste:
python3 setup.py sdist
register: dist
twine register dist/pysemigroup-$$VERSION.tar.gz
upload: diste
twine upload dist/pysemigroup-$$VERSION.tar.gz
......@@ -33,6 +33,22 @@ class StateView(frozenset, View):
class C(frozenset, View):
...
class Marker:
values = set()
def __init__(self):
while True:
i = random.randint(0, MAX_INT)
if i not in self.values:
break
self.id = i
self.values.add(i)
def __hash__(self):
return hash((type(self), self.id))
def __repr__(self):
return f"Marker<{self.id}>"
class AlphabetView(frozenset):
def compute_salt(self, retry=4):
......@@ -54,7 +70,7 @@ class AlphabetView(frozenset):
class Epsilon:
salt = hash(object())
salt = random.randint(0, MAX_INT)
def __hash__(self):
return hash((self.__class__, self.__class__.salt))
......@@ -144,17 +160,20 @@ class Transitions:
yield state
yield from self._storage.get(state, {}).get(letter, ())
def eliminate_epsilon(self) -> Self:
"""
Return a Transitions without ε-transitions.
"""
def epsilon_graph(self) -> nx.DiGraph:
EpsilonGraph: nx.DiGraph = nx.DiGraph()
EpsilonGraph.add_nodes_from(self.states)
EpsilonGraph.add_edges_from(
((x, y) for x in self.states for y in self.successors(x, ε) if x != y)
)
TrGraph = nx.transitive_closure(EpsilonGraph, reflexive=True)
return nx.transitive_closure(EpsilonGraph, reflexive=True)
def eliminate_epsilon(self) -> Self:
"""
Return a Transitions without ε-transitions.
"""
TrGraph = self.epsilon_graph()
def iter_tr():
for s1, a, s2 in self.iter_transitions():
......@@ -226,11 +245,12 @@ class Transitions:
def __hash__(self):
return hash((type(self), self.sorted_transitions))
def relabel(self, relabel: Mapping[Hashable, Hashable]) -> Self:
assert len(relabel) == len(set(relabel.values()))
def relabel_states(self, relabel: Mapping[Hashable, Hashable]) -> Self:
if not len(relabel) == len(set(relabel.values())):
raise ValueError("relabel is not a bijection")
def relabel_tr(e):
return (relabel[e[0]], e[1], relabel[e[1]])
return (relabel[e[0]], e[1], relabel[e[2]])
return type(self)(map(relabel_tr, self.iter_transitions()))
......@@ -239,9 +259,9 @@ class Transitions:
Use to convolute state with a fresh markers.
"""
if not marker:
marker = object()
marker = Marker()
relabel = {s: (s, marker) for s in self.states}
return self.relabel(relabel)
return self.relabel_states(relabel)
class SemiNFA:
......@@ -309,9 +329,9 @@ class SemiNFA:
To avoid collision in value, we create fresh symbols to make state, transition and letter inambiguous.
"""
G: nx.DiGraph = nx.DiGraph()
state_marker = object()
letter_marker = object()
transition_marker = object()
state_marker = Marker()
letter_marker = Marker()
transition_marker = Marker()
G.graph["state_marker"] = state_marker # type: ignore[attr-defined]
G.graph["letter_marker"] = letter_marker # type: ignore[attr-defined]
......@@ -342,7 +362,7 @@ class SemiNFA:
def fresh_symbol(self) -> "SemiNFA":
return SemiNFA(self.transitions.fresh_symbol())
def isomorphe(self, other: "SemiNFA", relabel_alphabet=True) -> bool:
def isomorphism(self, other: "SemiNFA", relabel_alphabet=True) -> bool:
"""
Two SemiNFA are isomorphique their digraph encoding are isomorphique.
Two variant exists:
......@@ -351,11 +371,9 @@ class SemiNFA:
"""
if relabel_alphabet:
def node_match(n1_attr, n2_attr):
return n1_attr["type"] == n2_attr["type"]
else:
def node_match(n1_attr, n2_attr):
b = n1_attr["type"] == n2_attr["type"]
if n1_attr["type"] == "letter":
......@@ -385,11 +403,9 @@ class NFA(SemiNFA):
self._initial_states = StateView(self.states.intersection(initial_states))
self._final_states = StateView(self.states.intersection(final_states))
if not self.transitions.is_deterministic():
raise ValueError("Transitions is not deterministic")
def fresh_symbol(self) -> "NFA":
marker = object()
marker = Marker()
return NFA(
self.transitions.fresh_symbol(marker=marker),
((s, marker) for s in self.initial_states),
......@@ -411,28 +427,33 @@ class NFA(SemiNFA):
return bool(c.intersection(self.final_states))
def determinize(self) -> "DFA":
self = self.eliminate_epsilon()
initial_state = C(self.initial_states)
final_states: set[Hashable] = set()
def gen_transitions():
seen = set()
to_deal_with = set(accepting_state)
while to_deal_with:
c = to_deal_with.pop()
seen.add(c)
if c.intersection(self.final_states):
final_states.add(c)
for l in self.alphabet:
c_next = self.next_configuration(c, l)
if c_next not in seen:
to_deal_with.add(c_next)
yield (c, l, c_next)
return DFA(gen_transitions(), initial_state, final_states)
transitions = []
seen = set()
to_deal_with = set([initial_state])
while to_deal_with:
c = to_deal_with.pop()
seen.add(c)
if c.intersection(self.final_states):
final_states.add(c)
for l in self.alphabet:
c_next = C((s2 for s in c for s2 in self.transitions.successors(s, l)))
if c_next not in seen:
to_deal_with.add(c_next)
transitions.append((c, l, c_next))
return DFA(transitions, initial_state, final_states)
def eliminate_epsilon(self) -> "NFA":
TrG = self.transitions.epsilon_graph()
final_states = set()
for f in self.final_states:
final_states.update(TrG[f])
return NFA(
self.transitions.eliminate_epsilon(), self.initial_states, self.final_states
self.transitions.eliminate_epsilon(), self.initial_states, final_states
)
def reverse(self) -> "NFA":
......@@ -457,12 +478,12 @@ class NFA(SemiNFA):
return map(lambda e: (e, i), it)
A1 = self.fresh_symbol()
A2 = self.fresh_symbol()
A2 = other.fresh_symbol()
transitions = chain(
A1.transitions.iter_transitions(), A2.transitions.iter_transitions()
)
initial_states = chain(A1.initial_states, A2.initial_states)
final_states = chain(A1.initial_states, A2.initial_states)
final_states = chain(A1.final_states, A2.final_states)
return NFA(transitions, initial_states, final_states)
......@@ -478,7 +499,7 @@ class NFA(SemiNFA):
return NFA(transitions, initial_states, final_states)
def kleene_star(self, other) -> "NFA":
def kleene_star(self) -> "NFA":
eps = ((s1, ε, s2) for s1 in self.final_states for s2 in self.initial_states)
transitions = chain(self.transitions.iter_transitions(), eps)
initial_states = self.initial_states
......@@ -492,28 +513,18 @@ class NFA(SemiNFA):
def get_digraph_encoding(self) -> nx.DiGraph:
G = super().get_digraph_encoding()
state_marker = G.graph["state_marker"] # type: ignore[attr-defined]
for s in self.states:
t = G.nodes[(s, state_marker)]["type"]
G.nodes[(s, state_marker)]["type"] = [t]
for s in self.initial_states:
G.nodes[(s, state_marker)]["type"] = "initial_state"
G.nodes[(s, state_marker)]["type"].append("initial_state")
for s in self.final_states:
G.nodes[(s, state_marker)]["type"] = "final_state"
G.nodes[(s, state_marker)]["type"].append("final_state")
return G
class SemiDFA(SemiNFA):
def __init__(
self,
transitions: Iterable[Tuple[Hashable, Hashable, Hashable]] | Transitions,
initial_state: Hashable,
final_states: Iterable[Hashable],
):
if not isinstance(transitions, Transitions):
transitions = Transitions(transitions)
if not transitions.is_deterministic():
raise ValueError("Non deterministic transitions")
super().__init__(transitions)
class DFA(NFA, SemiDFA):
class DFA(NFA):
__slots__ = ("_initial_state", "_final_states", "_normalize")
def __init__(
......@@ -523,12 +534,14 @@ class DFA(NFA, SemiDFA):
final_states: Iterable[Hashable],
):
super().__init__(transitions, (initial_state,), final_states)
if initial_state not in self.states:
raise ValueError("Initial state is not in the state")
if not self.transitions.is_deterministic():
raise ValueError("Transitions is not deterministic")
self._normalize: Optional["DFA"] = None
def fresh_symbol(self) -> "DFA":
marker = object()
marker = Marker()
return DFA(
self.transitions.fresh_symbol(marker=marker),
(self.initial_state, marker),
......@@ -549,7 +562,7 @@ class DFA(NFA, SemiDFA):
def __eq__(self, other):
"""two DFA are equals if they are isomorph over the very same alphabet"""
N1 = self.normalize()
N2 = other.noramlize()
N2 = other.normalize()
return (
N1.transitions == N2.transitions
and N1.initial_state == N2.initial_state
......@@ -582,13 +595,13 @@ class DFA(NFA, SemiDFA):
assert None != label[state]
succs = []
for i, l in enumerate(order):
L = list(self.successors(state, l))
L = list(self.transitions.successors(state, l))
if not L:
continue
assert len(L) == 1
next_state = L[0]
next_label = label[next_state]
if label[next_label] is None:
if next_label is None:
label[next_state] = (*label[state], i)
succs.append(next_state)
for s in succs:
......@@ -603,7 +616,7 @@ class DFA(NFA, SemiDFA):
)
relabeling = {s: i for i, s in enumerate(new_states)}
self._normalize = DFA(
self.transitions.relabel(relabeling),
self.transitions.relabel_states(relabeling),
relabeling[self.initial_state],
map(lambda e: relabeling[e], self.final_states),
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment