use super::super::config::DEFAULT_DEGREE_FALLBACK;
use super::CostCalculator;
impl CostCalculator {
pub fn calculate_expand_cost(
&self,
space: &str,
start_nodes: u64,
edge_type: Option<&str>,
) -> f64 {
let (avg_degree, is_super_node) = match edge_type {
Some(et) => self
.stats_manager
.get_edge_stats(space, et)
.map(|s| {
let is_super = s.avg_out_degree > self.config.super_node_threshold as f64;
(s.avg_out_degree, is_super)
})
.unwrap_or((DEFAULT_DEGREE_FALLBACK, false)),
None => (DEFAULT_DEGREE_FALLBACK, false),
};
let output_rows = (start_nodes as f64 * avg_degree) as u64;
let io_cost = self.calculate_io_cost(output_rows);
let cpu_cost = output_rows as f64 * self.config.edge_traversal_cost;
let base_cost = io_cost + cpu_cost;
if is_super_node {
base_cost * self.config.super_node_penalty
} else {
base_cost
}
}
pub fn calculate_expand_all_cost(
&self,
space: &str,
start_nodes: u64,
edge_type: Option<&str>,
) -> f64 {
let base_cost = self.calculate_expand_cost(space, start_nodes, edge_type);
base_cost * 1.5
}
pub fn calculate_traverse_cost(
&self,
space: &str,
start_nodes: u64,
edge_type: Option<&str>,
steps: u32,
) -> f64 {
let avg_degree = match edge_type {
Some(et) => self
.stats_manager
.get_edge_stats(space, et)
.map(|s| (s.avg_out_degree + s.avg_in_degree) / 2.0)
.unwrap_or(DEFAULT_DEGREE_FALLBACK),
None => DEFAULT_DEGREE_FALLBACK,
};
let mut total_cost = 0.0;
let mut current_rows = start_nodes as f64;
for step in 0..steps {
current_rows *= avg_degree;
let step_penalty = self.config.multi_hop_penalty.powi(step as i32);
let step_cost = current_rows * self.config.edge_traversal_cost * step_penalty;
let io_cost = self.calculate_io_cost(current_rows as u64);
total_cost += step_cost + io_cost;
}
total_cost
}
pub fn calculate_append_vertices_cost(&self, input_rows: u64) -> f64 {
input_rows as f64 * self.config.cpu_tuple_cost * 2.0
}
pub fn calculate_get_neighbors_cost(
&self,
space: &str,
start_nodes: u64,
edge_type: Option<&str>,
) -> f64 {
let avg_degree = match edge_type {
Some(et) => self
.stats_manager
.get_edge_stats(space, et)
.map(|s| s.avg_out_degree)
.unwrap_or(DEFAULT_DEGREE_FALLBACK),
None => DEFAULT_DEGREE_FALLBACK,
};
let neighbor_count = (start_nodes as f64 * avg_degree) as u64;
let lookup_cost = neighbor_count as f64 * self.config.neighbor_lookup_cost;
let io_cost = self.calculate_io_cost(neighbor_count);
lookup_cost + io_cost
}
pub fn calculate_get_vertices_cost(&self, vid_count: u64) -> f64 {
if vid_count == 0 {
return 0.0;
}
vid_count as f64 * (self.config.cpu_index_tuple_cost + self.config.cpu_tuple_cost)
+ self.config.seq_page_cost
}
pub fn calculate_get_edges_cost(&self, edge_count: u64) -> f64 {
if edge_count == 0 {
return 0.0;
}
edge_count as f64 * (self.config.cpu_index_tuple_cost + self.config.cpu_tuple_cost)
+ self.config.seq_page_cost
}
pub fn average_branching_factor(&self, space: Option<&str>, edge_types: &[String]) -> f64 {
let Some(space) = space else {
return self.config.strategy_thresholds.default_branching_factor;
};
if edge_types.is_empty() {
return self.config.strategy_thresholds.default_branching_factor;
}
let mut total = 0.0;
let mut count = 0u32;
for edge_type in edge_types {
if let Some(stats) = self.stats_manager.get_edge_stats(space, edge_type) {
if stats.avg_out_degree > 0.0 {
total += stats.avg_out_degree;
count += 1;
}
}
}
if count > 0 {
total / f64::from(count)
} else {
self.config.strategy_thresholds.default_branching_factor
}
}
pub fn calculate_shortest_path_cost(&self, start_nodes: u64, max_depth: u32) -> f64 {
let avg_branching = self.config.strategy_thresholds.default_branching_factor;
self.calculate_shortest_path_cost_with_branching(start_nodes, max_depth, avg_branching)
}
pub fn calculate_shortest_path_cost_for_edges(
&self,
space: Option<&str>,
edge_types: &[String],
start_nodes: u64,
max_depth: u32,
) -> f64 {
let avg_branching = self.average_branching_factor(space, edge_types);
self.calculate_shortest_path_cost_with_branching(start_nodes, max_depth, avg_branching)
}
pub fn calculate_shortest_path_cost_with_branching(
&self,
start_nodes: u64,
max_depth: u32,
avg_branching: f64,
) -> f64 {
let explored_nodes = start_nodes as f64 * avg_branching.powf(max_depth as f64);
let traversal_cost = explored_nodes * self.config.edge_traversal_cost;
let io_cost = self.calculate_io_cost(explored_nodes as u64);
traversal_cost + io_cost + self.config.shortest_path_base_cost
}
pub fn calculate_all_paths_cost(&self, start_nodes: u64, max_depth: u32) -> f64 {
let base_cost = self.calculate_shortest_path_cost(start_nodes, max_depth);
base_cost * self.config.path_enumeration_factor
}
pub fn calculate_all_paths_cost_for_edges(
&self,
space: Option<&str>,
edge_types: &[String],
start_nodes: u64,
max_depth: u32,
) -> f64 {
let base_cost =
self.calculate_shortest_path_cost_for_edges(space, edge_types, start_nodes, max_depth);
base_cost * self.config.path_enumeration_factor
}
pub fn calculate_multi_shortest_path_cost(&self, source_count: u64, max_depth: u32) -> f64 {
self.calculate_shortest_path_cost(source_count, max_depth) * 1.5
}
}