Skip to content

Commit 1d5b1e6

Browse files
committed
implemented is tree like
1 parent 928414a commit 1d5b1e6

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

grape/automaton/tree_automaton.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def is_unbounded(self) -> bool:
409409
"""
410410
Returns true if the grammar produces unbounded programs.
411411
"""
412-
reachable_from: dict[U, set[V]] = defaultdict(set)
412+
reachable_from: dict[U, set[U]] = defaultdict(set)
413413
for (P, args), dst in self.rules.items():
414414
reachable_from[dst].update(args)
415415

@@ -428,22 +428,8 @@ def is_unbounded(self) -> bool:
428428

429429
def compute_max_size_and_depth(self) -> tuple[int, int]:
430430
"""
431-
Return max size and max depth
431+
Return max size and max depth, this will loop endlessly if this grammar is unbounded.
432432
"""
433-
# Compute transitive closure
434-
reachable_from: dict[U, set[V]] = defaultdict(set)
435-
for (P, args), dst in self.rules.items():
436-
reachable_from[dst].update(args)
437-
438-
updated = True
439-
while updated:
440-
updated = False
441-
for dst, reachables in reachable_from.copy().items():
442-
before = len(reachables)
443-
for S in reachables.copy():
444-
reachables.update(reachable_from[S])
445-
if len(reachables) != before:
446-
updated = True
447433
max_size_by_state: dict[U, int] = {state: 0 for state in self.states}
448434
max_depth_by_state: dict[U, int] = {state: 0 for state in self.states}
449435
for (P, args), dst in self.rules.items():
@@ -481,3 +467,58 @@ def __str__(self) -> str:
481467
lines.append(f"{dst} <- '{P}' {add}")
482468

483469
return s + "\n".join(sorted(lines))
470+
471+
def has_unproductive_rules(self) -> bool:
472+
"""
473+
Returns true iff a rule is unproductive (including unreachable).
474+
"""
475+
new_states = self.states
476+
new_rules = {
477+
(letter, args): dst
478+
for (letter, args), dst in self.rules.items()
479+
if dst in new_states and all(s in new_states for s in args)
480+
}
481+
if new_rules != self.rules:
482+
return True
483+
consumed = self.__get_consumed__()
484+
for _, dst in list(self.rules.items()):
485+
if dst not in consumed:
486+
return True
487+
488+
return False
489+
490+
def __has_cloning_derivation__(self) -> bool:
491+
# Compute transitive closure
492+
reachable_from: dict[U, set[U]] = defaultdict(set)
493+
for (P, args), dst in self.rules.items():
494+
reachable_from[dst].update(args)
495+
496+
updated = True
497+
while updated:
498+
updated = False
499+
for dst, reachables in reachable_from.copy().items():
500+
before = len(reachables)
501+
for S in reachables.copy():
502+
reachables.update(reachable_from[S])
503+
if len(reachables) != before:
504+
updated = True
505+
if dst in reachables and any(
506+
len(args) > 1
507+
for S in reachables
508+
for _, args in self.reversed_rules[S]
509+
):
510+
return True
511+
return False
512+
513+
def is_tree_like(self) -> bool:
514+
"""
515+
Returns true iff all of the following are true:
516+
- |derivations| >= |states| + 2
517+
- there is no unusable production rules
518+
- there is a derivation by transitive closure S ->* w S S
519+
"""
520+
return (
521+
self.size() >= len(self.all_states) + 2
522+
and not self.has_unproductive_rules()
523+
and self.__has_cloning_derivation__()
524+
)

grape/cli/info.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ def main():
3333
print("rules:", dfta.size())
3434
print("symbols:", dfta.symbols())
3535
print("reduced:", old_rules == dfta.rules)
36-
print("specialied:", specialized)
36+
print("specialised:", specialized)
3737
if not dfta.is_unbounded():
3838
size, depth = dfta.compute_max_size_and_depth()
3939
print("max program size:", size)
4040
print("max program depth:", depth)
4141
else:
42-
print("unbounded programs:", dfta.is_unbounded())
42+
print("grammar produces unbounded programs")
43+
print("tree like:", dfta.is_tree_like())
4344

4445

4546
if __name__ == "__main__":

0 commit comments

Comments
 (0)