use crate::graph::{EdgeId, Graph, NodeId};
use crate::Result;
use std::collections::{HashMap, HashSet};
pub(crate) fn get_node_inputs(graph: &Graph, node_id: NodeId) -> Vec<NodeId> {
if let Some(node) = graph.get_node(node_id) {
node.inputs
.iter()
.filter_map(|&edge_id| graph.get_edge(edge_id).map(|edge| edge.from_node))
.collect()
} else {
Vec::new()
}
}
pub(crate) fn get_node_outputs(graph: &Graph, node_id: NodeId) -> Vec<NodeId> {
if let Some(node) = graph.get_node(node_id) {
node.outputs
.iter()
.filter_map(|&edge_id| graph.get_edge(edge_id).map(|edge| edge.to_node))
.collect()
} else {
Vec::new()
}
}
pub(super) fn unique_node_name(graph: &Graph, prefix: &str) -> String {
let mut counter = graph.next_node_id;
loop {
let candidate = format!("__optpass_{prefix}_{counter}");
if !graph.name_to_node.contains_key(&candidate) {
return candidate;
}
counter += 1;
}
}
pub(super) fn insert_scalar_constant(
graph: &mut Graph,
value: f32,
device: crate::device::Device,
name_hint: &str,
) -> Result<NodeId> {
let name = unique_node_name(graph, name_hint);
let mut attributes = HashMap::new();
attributes.insert(
"value".to_string(),
crate::graph::AttributeValue::Tensor(crate::tensor::Tensor::from_scalar(value)),
);
graph.add_node(name, crate::graph::NodeType::Constant, device, attributes)
}
pub(super) fn describe_input_edge(
graph: &Graph,
node_id: NodeId,
input_index: usize,
) -> Option<(NodeId, usize, crate::dtype::DType, crate::shape::Shape)> {
let node = graph.get_node(node_id)?;
let edge_id = *node.inputs.get(input_index)?;
let edge = graph.get_edge(edge_id)?;
Some((
edge.from_node,
edge.from_output,
edge.dtype,
edge.shape.clone(),
))
}
pub(super) fn rewrite_operation_node(
graph: &mut Graph,
node_id: NodeId,
new_op_name: &str,
new_inputs: &[(NodeId, usize, crate::dtype::DType, crate::shape::Shape)],
) -> Result<()> {
let old_input_edges: Vec<EdgeId> = graph
.get_node(node_id)
.map(|node| node.inputs.clone())
.unwrap_or_default();
for edge_id in old_input_edges {
graph.remove_edge(edge_id)?;
}
for (input_index, (from_node, from_output, dtype, shape)) in new_inputs.iter().enumerate() {
graph.add_edge(
*from_node,
node_id,
*from_output,
input_index,
*dtype,
shape.clone(),
false,
)?;
}
if let Some(node) = graph.get_node_mut(node_id) {
node.op_type = crate::graph::NodeType::Operation(new_op_name.to_string());
}
Ok(())
}
pub(super) fn is_exact_power_of_two(value: f32) -> bool {
if !value.is_finite() || value == 0.0 {
return false;
}
let bits = value.to_bits();
let mantissa = bits & 0x007F_FFFF;
let exponent = (bits >> 23) & 0xFF;
mantissa == 0 && exponent != 0 && exponent != 0xFF
}
pub trait OptimizationPass {
fn apply(&self, graph: &mut Graph) -> Result<bool>;
fn name(&self) -> &str;
fn is_applicable(&self, graph: &Graph) -> bool;
fn priority(&self) -> u32 {
100
}
fn set_outputs(&self, _outputs: &HashSet<NodeId>) {}
}