use crate::graph::EdgeAttribute;
use crate::operation::OperationResult;
use crate::operation::marker::{MarkerSet, SkipMarkers};
use crate::operation::signature::parameter::{
GraphWithSubstitution, OperationArgument, OperationParameter, ParameterSubstitution,
};
use crate::semantics::{AbstractGraph, AbstractMatcher, ConcreteGraph, Semantics};
use crate::util::bimap::BiMap;
use crate::util::{InternString, log};
use crate::{NodeKey, interned_string_newtype};
use derive_more::From;
use derive_more::with_trait::Into;
use petgraph::algo::general_subgraph_monomorphisms_iter;
use petgraph::visit::NodeIndexable;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
pub trait BuiltinQuery {
type S: Semantics;
fn parameter(&self) -> OperationParameter<Self::S>;
fn apply_abstract(&self, g: &mut GraphWithSubstitution<AbstractGraph<Self::S>>);
fn query(&self, g: &mut GraphWithSubstitution<ConcreteGraph<Self::S>>) -> ConcreteQueryOutput;
}
pub struct ConcreteQueryOutput {
pub taken: bool,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, From, Into)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ShapeNodeIdentifier(InternString);
interned_string_newtype!(ShapeNodeIdentifier);
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(bound = "S: crate::serde::SemanticsSerde")
)]
pub struct GraphShapeQuery<S: Semantics> {
pub parameter: OperationParameter<S>,
pub expected_graph: AbstractGraph<S>,
pub node_keys_to_shape_idents: BiMap<NodeKey, ShapeNodeIdentifier>,
pub skip_markers: SkipMarkers,
}
impl<S: Semantics> Clone for GraphShapeQuery<S> {
fn clone(&self) -> Self {
GraphShapeQuery {
parameter: self.parameter.clone(),
expected_graph: self.expected_graph.clone(),
node_keys_to_shape_idents: self.node_keys_to_shape_idents.clone(),
skip_markers: self.skip_markers.clone(),
}
}
}
impl<S: Semantics> GraphShapeQuery<S> {
pub fn new(
parameter: OperationParameter<S>,
expected_graph: AbstractGraph<S>,
node_keys_to_shape_idents: BiMap<NodeKey, ShapeNodeIdentifier>,
) -> Self {
GraphShapeQuery {
parameter,
expected_graph,
node_keys_to_shape_idents,
skip_markers: SkipMarkers::default(),
}
}
pub fn with_skip_markers(mut self, skip_markers: SkipMarkers) -> Self {
self.skip_markers = skip_markers;
self
}
}
pub struct ConcreteShapeQueryResult {
pub shape_idents_to_node_keys: Option<HashMap<ShapeNodeIdentifier, NodeKey>>,
}
pub(crate) fn run_builtin_query<S: Semantics>(
g: &mut ConcreteGraph<S>,
query: &S::BuiltinQuery,
arg: OperationArgument,
) -> OperationResult<ConcreteQueryOutput> {
let mut gws = GraphWithSubstitution::new(g, &arg.subst);
let output = query.query(&mut gws);
Ok(output)
}
pub(crate) fn run_shape_query<S: Semantics>(
g: &mut ConcreteGraph<S>,
query: &GraphShapeQuery<S>,
selected_inputs: &[NodeKey],
hidden_nodes: &HashSet<NodeKey>,
marker_set: &MarkerSet,
) -> OperationResult<ConcreteShapeQueryResult> {
let abstract_graph = S::concrete_to_abstract(g);
let subst = ParameterSubstitution::infer_explicit_for_param(selected_inputs, &query.parameter)?;
let mut hidden_nodes_incl_marker_hidden = hidden_nodes.clone();
hidden_nodes_incl_marker_hidden.extend(marker_set.skipped_nodes(&query.skip_markers));
get_shape_query_substitution(
query,
&abstract_graph,
&subst,
&hidden_nodes_incl_marker_hidden,
)
}
fn get_shape_query_substitution<S: Semantics>(
query: &GraphShapeQuery<S>,
dynamic_graph: &AbstractGraph<S>,
subst: &ParameterSubstitution,
hidden_nodes: &HashSet<NodeKey>,
) -> OperationResult<ConcreteShapeQueryResult> {
log::trace!(
"Running shape query with hidden nodes {:?} and parameter substitution {:?}",
hidden_nodes,
subst
);
let desired_shape = &query.expected_graph;
let desired_shape_ref = &desired_shape.graph;
let dynamic_graph_ref = &dynamic_graph.graph;
let mut enforced_desired_to_dynamic: HashMap<NodeKey, NodeKey> = HashMap::new();
for (subst_marker, dynamic_node_key) in &subst.mapping {
let desired_node_key = query
.parameter
.node_keys_to_subst
.get_right(subst_marker)
.expect("internal error: parameter substitution incorrect");
enforced_desired_to_dynamic.insert(*desired_node_key, *dynamic_node_key);
}
let mut nm = |desired_shape_node_key: &NodeKey, dynamic_graph_node_key: &NodeKey| {
if let Some(expected_dynamic_node_key) =
enforced_desired_to_dynamic.get(desired_shape_node_key)
{
if expected_dynamic_node_key != dynamic_graph_node_key {
return false;
}
} else {
if hidden_nodes.contains(dynamic_graph_node_key) {
log::info!(
"Skipping hidden node {:?} in dynamic graph for shape query",
dynamic_graph_node_key
);
return false;
}
}
let desired_shape_attr = desired_shape
.get_node_attr(*desired_shape_node_key)
.unwrap();
let dynamic_graph_attr = dynamic_graph
.get_node_attr(*dynamic_graph_node_key)
.unwrap();
S::NodeMatcher::matches(dynamic_graph_attr, desired_shape_attr)
};
let mut em =
|desired_shape_edge_attr_wrapper: &EdgeAttribute<S::EdgeAbstract>,
dynamic_graph_edge_attr_wrapper: &EdgeAttribute<S::EdgeAbstract>| {
let desired_shape_edge_attr = &desired_shape_edge_attr_wrapper.edge_attr;
let dynamic_graph_edge_attr = &dynamic_graph_edge_attr_wrapper.edge_attr;
S::EdgeMatcher::matches(dynamic_graph_edge_attr, desired_shape_edge_attr)
};
let Some(isos) = general_subgraph_monomorphisms_iter(
&desired_shape_ref,
&dynamic_graph_ref,
&mut nm,
&mut em,
) else {
return Ok(ConcreteShapeQueryResult {
shape_idents_to_node_keys: None,
});
};
let opt_mapping = isos
.filter_map(|iso| {
let mapping = iso
.iter()
.enumerate()
.filter_map(|(desired_shape_idx, &dynamic_graph_idx)| {
let desired_shape_node_key = desired_shape_ref.from_index(desired_shape_idx);
let dynamic_graph_node_key = dynamic_graph_ref.from_index(dynamic_graph_idx);
Some((
query
.node_keys_to_shape_idents
.get_left(&desired_shape_node_key)?
.clone(),
dynamic_graph_node_key,
))
})
.collect::<HashMap<_, _>>();
Some(mapping)
})
.next();
Ok(ConcreteShapeQueryResult {
shape_idents_to_node_keys: opt_mapping,
})
}