Mentions légales du service
Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
pysemigroup
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Admin message
GitLab upgrade completed. Current version is 17.8.2.
Show more breadcrumbs
PAPERMAN Charles
pysemigroup
Commits
7718ce4d
Commit
7718ce4d
authored
1 year ago
by
cha
Browse files
Options
Downloads
Patches
Plain Diff
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
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
makefile
+0
-20
0 additions, 20 deletions
makefile
pysemigroup/automata.py
+76
-63
76 additions, 63 deletions
pysemigroup/automata.py
with
76 additions
and
83 deletions
makefile
deleted
100644 → 0
+
0
−
20
View file @
fec098f5
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
This diff is collapsed.
Click to expand it.
pysemigroup/automata.py
+
76
−
63
View file @
7718ce4d
...
...
@@ -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
isomorph
e
(
self
,
other
:
"
SemiNFA
"
,
relabel_alphabet
=
True
)
->
bool
:
def
isomorph
ism
(
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
.
in
iti
al_states
,
A2
.
in
iti
al_states
)
final_states
=
chain
(
A1
.
f
inal_states
,
A2
.
f
inal_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
.
nor
a
mlize
()
N2
=
other
.
norm
a
lize
()
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
),
)
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment