use rlx_ir::{Graph, NodeId};
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct Motif {
pub seq: Vec<String>,
pub count: usize,
pub examples: Vec<u32>,
}
impl Motif {
pub fn score(&self) -> usize {
self.count * self.seq.len()
}
}
fn kind_name(g: &Graph, id: NodeId) -> String {
format!("{:?}", g.node(id).op.kind())
}
pub fn linear_op_motifs(
graph: &Graph,
min_len: usize,
max_len: usize,
min_count: usize,
) -> Vec<Motif> {
let mut consumers: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for node in graph.nodes() {
for &inp in &node.inputs {
consumers.entry(inp).or_default().push(node.id);
}
}
let mut table: HashMap<String, Motif> = HashMap::new();
for node in graph.nodes() {
if node.inputs.is_empty() {
continue;
}
let mut chain: Vec<NodeId> = vec![node.id];
let mut cur = node.id;
while chain.len() < max_len {
match consumers.get(&cur) {
Some(cs) if cs.len() == 1 => {
cur = cs[0];
chain.push(cur);
}
_ => break, }
}
for len in min_len..=chain.len() {
let seq: Vec<String> = chain[..len]
.iter()
.map(|&id| kind_name(graph, id))
.collect();
let key = seq.join("→");
let entry = table.entry(key).or_insert_with(|| Motif {
seq,
count: 0,
examples: Vec::new(),
});
entry.count += 1;
if entry.examples.len() < 4 {
entry.examples.push(node.id.0);
}
}
}
let mut motifs: Vec<Motif> = table
.into_values()
.filter(|m| m.count >= min_count)
.collect();
motifs.sort_by(|a, b| b.score().cmp(&a.score()).then(b.count.cmp(&a.count)));
motifs
}