use std::collections::HashMap;
use crate::compiled::{CompiledKernelRaw, CompiledKernelPull, CompiledKernelPushPull};
use crate::kernel::{GkProgram, WireSource};
use crate::node::GkNode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProvMode {
Raw,
Pull,
PushPull,
}
#[derive(Debug, Clone)]
pub struct GraphAnalysis {
pub total_nodes: usize,
pub num_inputs: usize,
pub num_outputs: usize,
pub output_cone_sizes: Vec<(String, usize)>,
pub max_cone_ratio: f64,
pub avg_cone_ratio: f64,
pub node_provenance: Vec<u64>,
}
pub fn analyze_graph(
nodes: &[Box<dyn GkNode>],
wiring: &[Vec<WireSource>],
output_map: &HashMap<String, (usize, usize)>,
) -> GraphAnalysis {
let total_nodes = nodes.len();
let node_provenance = GkProgram::compute_provenance(nodes, wiring);
let mut output_cone_sizes = Vec::new();
for (name, &(node_idx, _port)) in output_map {
let cone_size = compute_cone_size(node_idx, wiring);
output_cone_sizes.push((name.clone(), cone_size));
}
let max_cone = output_cone_sizes.iter().map(|(_, s)| *s).max().unwrap_or(0);
let avg_cone: f64 = if output_cone_sizes.is_empty() { 0.0 }
else { output_cone_sizes.iter().map(|(_, s)| *s as f64).sum::<f64>() / output_cone_sizes.len() as f64 };
let max_cone_ratio = if total_nodes > 0 { max_cone as f64 / total_nodes as f64 } else { 1.0 };
let avg_cone_ratio = if total_nodes > 0 { avg_cone / total_nodes as f64 } else { 1.0 };
let mut max_input = 0usize;
for sources in wiring {
for s in sources {
if let WireSource::Input(idx) = s {
max_input = max_input.max(*idx + 1);
}
}
}
GraphAnalysis {
total_nodes,
num_inputs: max_input,
num_outputs: output_map.len(),
output_cone_sizes,
max_cone_ratio,
avg_cone_ratio,
node_provenance,
}
}
fn compute_cone_size(node_idx: usize, wiring: &[Vec<WireSource>]) -> usize {
let mut visited = vec![false; wiring.len()];
let mut stack = vec![node_idx];
let mut count = 0;
while let Some(idx) = stack.pop() {
if idx >= visited.len() || visited[idx] { continue; }
visited[idx] = true;
count += 1;
for source in &wiring[idx] {
if let WireSource::NodeOutput(upstream, _) = source {
if !visited[*upstream] {
stack.push(*upstream);
}
}
}
}
count
}
pub fn select_prov_mode(analysis: &GraphAnalysis) -> ProvMode {
if analysis.total_nodes < 15 && analysis.num_inputs <= 1 {
return ProvMode::Raw;
}
if analysis.num_inputs >= 2 {
return ProvMode::PushPull;
}
ProvMode::Pull
}
pub enum P2Engine {
Raw(CompiledKernelRaw),
Pull(CompiledKernelPull),
PushPull(CompiledKernelPushPull),
}
impl P2Engine {
#[inline]
pub fn eval_for_slot(&mut self, coords: &[u64], slot: usize) -> u64 {
match self {
P2Engine::Raw(k) => k.eval_for_slot(coords, slot),
P2Engine::Pull(k) => k.eval_for_slot(coords, slot),
P2Engine::PushPull(k) => k.eval_for_slot(coords, slot),
}
}
#[inline]
pub fn eval(&mut self, coords: &[u64]) {
match self {
P2Engine::Raw(k) => k.eval(coords),
P2Engine::Pull(k) => k.eval(coords),
P2Engine::PushPull(k) => k.eval(coords),
}
}
#[inline]
pub fn get_slot(&self, slot: usize) -> u64 {
match self {
P2Engine::Raw(k) => k.get_slot(slot),
P2Engine::Pull(k) => k.get_slot(slot),
P2Engine::PushPull(k) => k.get_slot(slot),
}
}
pub fn resolve_output(&self, name: &str) -> Option<usize> {
match self {
P2Engine::Raw(k) => k.resolve_output(name),
P2Engine::Pull(k) => k.resolve_output(name),
P2Engine::PushPull(k) => k.resolve_output(name),
}
}
pub fn coord_count(&self) -> usize {
match self {
P2Engine::Raw(k) => k.coord_count(),
P2Engine::Pull(k) => k.coord_count(),
P2Engine::PushPull(k) => k.coord_count(),
}
}
pub fn prov_mode(&self) -> ProvMode {
match self {
P2Engine::Raw(_) => ProvMode::Raw,
P2Engine::Pull(_) => ProvMode::Pull,
P2Engine::PushPull(_) => ProvMode::PushPull,
}
}
}
#[cfg(feature = "jit")]
pub enum P3Engine {
Raw(crate::jit::JitKernelRaw),
Pull(crate::jit::JitKernelPull),
PushPull(crate::jit::JitKernelPushPull),
}
#[cfg(feature = "jit")]
impl P3Engine {
#[inline]
pub fn eval_for_slot(&mut self, coords: &[u64], slot: usize) -> u64 {
match self {
P3Engine::Raw(k) => k.eval_for_slot(coords, slot),
P3Engine::Pull(k) => k.eval_for_slot(coords, slot),
P3Engine::PushPull(k) => k.eval_for_slot(coords, slot),
}
}
#[inline]
pub fn eval(&mut self, coords: &[u64]) {
match self {
P3Engine::Raw(k) => k.eval(coords),
P3Engine::Pull(k) => k.eval(coords),
P3Engine::PushPull(k) => k.eval(coords),
}
}
#[inline]
pub fn get_slot(&self, slot: usize) -> u64 {
match self {
P3Engine::Raw(k) => k.get_slot(slot),
P3Engine::Pull(k) => k.get_slot(slot),
P3Engine::PushPull(k) => k.get_slot(slot),
}
}
pub fn resolve_output(&self, name: &str) -> Option<usize> {
match self {
P3Engine::Raw(k) => k.resolve_output(name),
P3Engine::Pull(k) => k.resolve_output(name),
P3Engine::PushPull(k) => k.resolve_output(name),
}
}
pub fn coord_count(&self) -> usize {
match self {
P3Engine::Raw(k) => k.coord_count(),
P3Engine::Pull(k) => k.coord_count(),
P3Engine::PushPull(k) => k.coord_count(),
}
}
pub fn prov_mode(&self) -> ProvMode {
match self {
P3Engine::Raw(_) => ProvMode::Raw,
P3Engine::Pull(_) => ProvMode::Pull,
P3Engine::PushPull(_) => ProvMode::PushPull,
}
}
}