grammar-inference-engine/bex/crx_refined.py

81 lines
2.5 KiB
Python

"""CRX Refined — Cluster-then-infer CRX with over-approximation detection."""
from collections import defaultdict
from .crx import CRX
from .grammar import Epsilon
def _cluster_by_structure(sequences):
"""Cluster sequences by structural features."""
groups = defaultdict(list)
for seq in sequences:
if not seq:
continue
length = len(seq)
if length <= 3:
length_bucket = 'short'
elif length <= 8:
length_bucket = 'med'
else:
length_bucket = 'long'
key = (seq[0], seq[-1], length_bucket)
groups[key].append(seq)
return dict(groups)
def crx_refined(sequences, min_cluster=2):
"""Cluster-then-infer CRX."""
S = [list(s) for s in sequences if s]
if not S:
return Epsilon()
clusters = _cluster_by_structure(S)
valid_clusters = {k: v for k, v in clusters.items() if len(v) >= min_cluster}
if not valid_clusters:
return CRX().infer(S)
best_key = max(valid_clusters, key=lambda k: len(valid_clusters[k]))
best_cluster = valid_clusters[best_key]
return CRX().infer(best_cluster)
def crx_with_confidence(sequences, min_cluster=2):
"""Cluster-then-infer CRX with confidence metrics."""
S = [list(s) for s in sequences if s]
if not S:
return {
'grammar': Epsilon(),
'confidence': 1.0,
'n_clusters': 0,
'largest_cluster': 0,
'n_sequences': 0,
}
clusters = _cluster_by_structure(S)
valid_clusters = {k: v for k, v in clusters.items() if len(v) >= min_cluster}
if not valid_clusters:
g = CRX().infer(S)
return {
'grammar': g,
'confidence': 0.5,
'n_clusters': len(clusters),
'largest_cluster': max((len(v) for v in clusters.values()), default=0),
'n_sequences': len(S),
}
best_key = max(valid_clusters, key=lambda k: len(valid_clusters[k]))
best_cluster = valid_clusters[best_key]
g = CRX().infer(best_cluster)
captured = sum(1 for s in S if any(_matches_cluster(g, s) for c in valid_clusters.values() for s in c))
confidence = captured / max(len(S), 1)
return {
'grammar': g,
'confidence': round(confidence, 3),
'n_clusters': len(valid_clusters),
'largest_cluster': len(best_cluster),
'n_sequences': len(S),
}
def _matches_cluster(grammar, seq):
from .grammar import match as grammar_match
try:
return grammar_match(grammar, seq)
except Exception:
return False