74 lines
2.5 KiB
Python
74 lines
2.5 KiB
Python
"""kOREInference — Algorithm 4: iDRegEx (arXiv 1004.2372)."""
|
|
|
|
from .ikoa import ikoa
|
|
from .rwrsq import rwr_sq
|
|
from .idregex import is_deterministic
|
|
from .mdl import mdl_score
|
|
from .grammar import Epsilon, Empty, Symbol, Plus, Optional, Star, Concat, Alt, alphabet as ast_alphabet
|
|
|
|
|
|
def validate_k_ore(node, k, alphabet_set=None):
|
|
"""Check if a k-ORE satisfies the k-occurrence condition."""
|
|
if node is None or isinstance(node, (Empty, Epsilon)):
|
|
return True, "OK"
|
|
syms = alphabet_set or ast_alphabet(node)
|
|
counts = _count_symbol_occurrences(node)
|
|
violations = [f"{s}:{c}" for s, c in sorted(counts.items()) if c > k]
|
|
if violations:
|
|
return False, f"k={k} violations: {', '.join(violations)}"
|
|
return True, "OK"
|
|
|
|
|
|
def _count_symbol_occurrences(node):
|
|
"""Count how many times each symbol appears as a leaf in the AST."""
|
|
if isinstance(node, Symbol):
|
|
return {node.value: 1}
|
|
if isinstance(node, (Epsilon, Empty)):
|
|
return {}
|
|
if isinstance(node, (Plus, Optional, Star)):
|
|
return _count_symbol_occurrences(node.child)
|
|
if isinstance(node, (Concat, Alt)):
|
|
counts = {}
|
|
for p in node.parts:
|
|
for sym, cnt in _count_symbol_occurrences(p).items():
|
|
counts[sym] = counts.get(sym, 0) + cnt
|
|
return counts
|
|
return {}
|
|
|
|
|
|
def _count_nodes(node):
|
|
if isinstance(node, Symbol):
|
|
return 1
|
|
if isinstance(node, (Epsilon, Empty)):
|
|
return 1
|
|
if isinstance(node, (Plus, Optional, Star)):
|
|
return 1 + _count_nodes(node.child)
|
|
if isinstance(node, (Concat, Alt)):
|
|
return 1 + sum(_count_nodes(p) for p in node.parts)
|
|
return 1
|
|
|
|
|
|
class kOREInference:
|
|
def __init__(self, k_max=5, N=5):
|
|
self.k_max = k_max
|
|
self.N = N
|
|
|
|
def infer(self, sequences):
|
|
sequences = [s for s in sequences if s]
|
|
if not sequences:
|
|
return None
|
|
candidates = []
|
|
for k in range(1, self.k_max + 1):
|
|
for _ in range(self.N):
|
|
G = ikoa(sequences, k, num_trials=1)
|
|
if G is None:
|
|
continue
|
|
expr = rwr_sq(G)
|
|
if expr is not None and not isinstance(expr, (Empty, Epsilon)):
|
|
if is_deterministic(expr):
|
|
valid, _ = validate_k_ore(expr, k)
|
|
if valid:
|
|
candidates.append((G, expr, k))
|
|
if not candidates:
|
|
return None
|
|
return min(candidates, key=lambda c: mdl_score(c[1], sequences))
|