diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml index a22ca5932b9a23c395a9157e29bb62f7a84dd829..6c2f32a758257dc017fb20b1c918452323fabc09 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src-tauri/Cargo.toml @@ -19,6 +19,5 @@ ahash = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true parser = { path = "../../rust/parser" } -layout = { path = "../../rust/layout" } subgraph = { path = "../../rust/subgraph" } anyhow = { workspace = true } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..6dd3a9c82628553db84619c0ed66ae78cfe22150 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "file-ext" +version = "0.1.0" +edition = "2024" + +[dependencies] +libc.workspace = true + +[dependencies.windows] +workspace = true +features = [ + "Win32_Security", + "Win32_Security_Authorization", + "Win32_System_Threading" +] \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..107c6c5e5480fe6d99147a9dfcb814c4010665a3 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/file-ext/src/lib.rs @@ -0,0 +1,175 @@ +//! ### Logging Unimplemented +//! Many error handling in the current module should be logged, +//! and since the log module has not yet been completed, +//! it will return false or mark it as unimplemented + +#![feature(io_error_more)] + +use std::{fs, fs::Metadata, io::ErrorKind}; + +#[cfg(windows)] +use windows::{ + Win32::{ + Foundation::{CloseHandle, ERROR_SUCCESS, HANDLE, HLOCAL, LocalFree}, + Security::{ + Authorization::{GetNamedSecurityInfoA, SE_FILE_OBJECT}, + EqualSid, GetTokenInformation, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSID, + TOKEN_QUERY, TOKEN_USER, TokenUser, + }, + System::Threading::{GetCurrentProcess, OpenProcessToken}, + }, + core::PCSTR, +}; + +use self::FileErrorKind::*; + +enum FileErrorKind<'a> { + ErrorNone, + InvalidFileName(&'a str), + NoSuchFile(&'a str), + DirNotSupported(&'a str), + UnknownError(String, &'a str), + UnsupportedFIleType(&'a str), + BadPermission(&'a str), + InconsistentOwner(&'a str), + BadFile(&'a str), +} + +struct FileExt; + +impl FileExt { + fn validate(path: &str) -> FileErrorKind { + let mut metadata = fs::metadata(path); + + match metadata { + Ok(ref mut meta) => { + if meta.is_dir() { + return DirNotSupported(path); + } + + if meta.is_symlink() { + let path = fs::read_link(path).unwrap(); + *meta = fs::metadata(path).unwrap(); + } + + #[cfg(unix)] + { + use std::os::unix::fs::MetadataExt; + + let mode = meta.mode(); + if mode & 0o400 == 0 || mode & 0o022 != 0 { + return BadPermission(path); + } + } + + #[cfg(windows)] + if !check_owner(path) { + return InconsistentOwner(path); + } + + #[cfg(unix)] + if !check_owner(meta) { + return InconsistentOwner(path); + } + } + Err(e) => + return match e.kind() { + ErrorKind::InvalidFilename => InvalidFileName(path), + ErrorKind::NotFound => NoSuchFile(path), + _ => UnknownError(e.to_string(), path), + }, + } + + ErrorNone + } +} + +#[cfg(unix)] +fn check_owner(meta: &Metadata) -> bool { + use std::os::unix::fs::MetadataExt; + + let uid = meta.uid(); + let euid = unsafe { libc::geteuid() }; + + uid == euid +} + +#[cfg(windows)] +fn check_owner(path: &str) -> bool { + use std::ptr::null_mut; + + unsafe { + let psd: *mut PSECURITY_DESCRIPTOR = null_mut(); + let psid_owner: *mut PSID = null_mut(); + + if GetNamedSecurityInfoA( + PCSTR(path.as_ptr()), + SE_FILE_OBJECT, + OWNER_SECURITY_INFORMATION, + Some(psid_owner), + None, + None, + None, + psd, + ) != ERROR_SUCCESS + { + return false; + } + + let mut token_handle = HANDLE::default(); + if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle).is_err() { + return false; + } + + let mut token_info_len = 0; + if GetTokenInformation(token_handle, TokenUser, None, 0, &mut token_info_len).is_err() { + return false; + } + + let token_user = libc::malloc(token_info_len as usize); + + if GetTokenInformation( + token_handle, + TokenUser, + Some(token_user), + token_info_len, + &mut token_info_len, + ) + .is_err() + { + libc::free(token_user); + local_free_rs(psd); + close_handle_rs(token_handle); + + return false; + } + + let process_sid = (*(token_user as *mut TOKEN_USER)).User.Sid; + + let res = EqualSid(*psd.cast(), process_sid).is_ok(); + + libc::free(token_user); + local_free_rs(psd); + close_handle_rs(token_handle); + + res + } +} + +#[cfg(windows)] +fn local_free_rs(ptr: *mut T) { + if !ptr.is_null() { + unsafe { + LocalFree(Some(HLOCAL(ptr.cast()))); + } + } +} + +#[cfg(windows)] +fn close_handle_rs(handle: HANDLE) { + unsafe { + if CloseHandle(handle).is_err() { + unimplemented!() + } + } +} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml deleted file mode 100644 index 34459ef3e8ae6475babd889136eae33d7eb4b1bb..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "layout" -version = "0.0.1" -edition = "2024" - -[dependencies] -mimalloc = { workspace = true } -smallvec = { version = "2.0.0-alpha.11", features = ["serde", "std"] } -ahash = { workspace = true, features = ["serde"] } -anyhow = { workspace = true } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs deleted file mode 100644 index 78469c618da188cc67afddf17edf4dc70aa65a97..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/acyclic.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Graph acyclification utilities for removing and restoring cycles. -//! -//! This module provides algorithms to convert cyclic graphs to DAGs (Directed Acyclic Graphs) -//! by finding feedback arc sets (FAS), with support for cycle restoration. Implemented algorithms: -//! - Depth-First Search (DFS) based FAS detection (`dfs_fas`) -//! - Greedy FAS algorithm (unimplemented placeholder) -//! -//! # Key Concepts -//! - **Feedback Arc Set**: Set of edges whose removal makes the graph acyclic -//! - **Edge Reversal**: Strategy to maintain graph connectivity while breaking cycles - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; -use std::cmp::Reverse; - -use super::{Edge, Graph, Key}; -use crate::Acyclicer::*; - -impl Graph { - /// Converts the graph to a directed acyclic graph (DAG) by reversing edges. - /// - /// The algorithm used depends on the configured `acyclicer`: - /// - Greedy: Uses greedy heuristic (currently unimplemented) - /// - Default: Uses depth-first search (DFS) to find feedback arc set - /// - /// # Behavior - /// 1. Identifies feedback edges using selected algorithm - /// 2. Removes feedback edges from original orientation - /// 3. Reinserts edges in reversed orientation with `reversed` flag set - /// - /// # Notes - /// - Original graph structure can be restored with [`restore_cycles`] - /// - Modified edges maintain their metadata with `reversed` marker - pub(super) fn make_acyclic(&mut self) { - let Some(edges) = (match self.config.acyclicer { - Greedy => Some(self.greedy_fas()), - Dfs | NoAcyclicer => Some(self.dfs_fas()), - }) else { - return; - }; - - for edge in edges { - if let Some(mut graph_edge) = self.edge1(edge).cloned() { - self.remove_edge1(edge); - graph_edge.reversed = true; - self.set_edge(edge.target, edge.source, Some(graph_edge)); - } - } - } - - /// Finds feedback arc set using depth-first search traversal. - /// - /// # Algorithm - /// 1. Maintains two visitation states: global and current traversal - /// 2. Tracks back edges during DFS that connect to already-visited nodes - /// 3. Collects these back edges as the feedback arc set - /// - /// # Complexity - /// - Time: O(V + E) - /// - Space: O(V) - fn dfs_fas(&mut self) -> Vec { - let mut fas: Vec = vec![]; - let mut visited: HashSet = HashSet::new(); - /// (node_id, out_edges, edge_index) - let mut stack: Vec<(Key, Vec, usize)> = vec![]; - - for &key in self.nodes.keys() { - if visited.contains(&key) { - continue; - } - - let mut local_stack: Vec = vec![key]; - let mut local_visited: HashSet = HashSet::new(); - local_visited.insert(key); - - while let Some(current_node) = local_stack.pop() { - if visited.insert(current_node) { - let out_edges = self.out_edges(¤t_node); - stack.push((current_node, out_edges, 0)); - } - - if let Some((_, out_edges, edge_index)) = stack.last_mut() { - if *edge_index < out_edges.len() { - let edge = out_edges[*edge_index]; - *edge_index += 1; - match local_visited.insert(edge.target) { - true => local_stack.push(edge.target), - false => fas.push(edge), - } - } else { - stack.pop(); - } - } - } - } - - fas - } - - /// Restores graph to the original cyclic state - /// by reversing previously modified edges. - /// - /// # Operation - /// 1. Scans all edges in graph - /// 2. Flip any edge with `reversed = true` back to original orientation - /// 3. Maintains all edge attributes during reversal - /// - /// # Invariants - /// - After execution, all edges will have `reversed = false` - /// - Graph topology returns to pre-acyclification state - pub(super) fn restore_cycles(&mut self) -> Option<()> { - for e in self.edges() { - let edge = self.edge_mut1(e)?; - if edge.reversed { - let mut label = edge.clone(); - label.reversed = false; - self.set_edge(e.target, e.source, Some(label)); - } - } - - None - } - - fn greedy_fas(&mut self) -> Vec { - let mut in_deg: HashMap = HashMap::new(); - let mut out_deg: HashMap = HashMap::new(); - let mut src_edges: HashMap> = HashMap::new(); - - for edge in self.edges() { - *out_deg.entry(edge.source).or_insert(0) += 1; - *in_deg.entry(edge.target).or_insert(0) += 1; - src_edges.entry(edge.source).or_default().push(edge); - } - - let mut nodes: Vec = self.nodes.keys().copied().collect(); - nodes.sort_by_cached_key(|&n| { - let out = out_deg.get(&n).unwrap_or(&0); - let ins = in_deg.get(&n).unwrap_or(&0); - ((*out as isize - *ins as isize).abs(), Reverse(n)) - }); - - let mut fas = Vec::new(); - let mut removed = HashSet::new(); - - while let Some(node) = nodes.pop() { - if removed.contains(&node) || !out_deg.contains_key(&node) { - continue; - } - - let out = *out_deg.get(&node).unwrap_or(&0); - let ins = *in_deg.get(&node).unwrap_or(&0); - - if out > ins { - if let Some(edges) = src_edges.get(&node) { - for edge in edges { - if !removed.contains(&edge.target) { - fas.push(*edge); - in_deg.entry(edge.target).and_modify(|x| *x -= 1); - } - } - } - } else { - for edge in self.in_edges(&node) { - if !removed.contains(&edge.source) { - fas.push(edge); - out_deg.entry(edge.source).and_modify(|x| *x -= 1); - } - } - } - - removed.insert(node); - out_deg.remove(&node); - in_deg.remove(&node); - - nodes.sort_by_cached_key(|&n| { - let out = out_deg.get(&n).unwrap_or(&0); - let ins = in_deg.get(&n).unwrap_or(&0); - ((*out as isize - *ins as isize).abs(), Reverse(n)) - }); - } - - fas - } - -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs deleted file mode 100644 index 619096132e79e41bb5e5110a8b65c4d52090f4ac..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/algo/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashSetExt, HashSet}; - -use crate::{Graph, Key}; - -impl Graph { - #[inline] - pub(super) fn preorder(&self, keys: &[Key]) -> Vec { - self.traverse(keys, false) - } - - #[inline] - pub(super) fn postorder(&self, keys: &[Key]) -> Vec { - self.traverse(keys, true) - } - - fn traverse(&self, keys: &[Key], postorder: bool) -> Vec { - let mut acc: Vec = Vec::with_capacity(keys.len() * 2); - let mut visited: HashSet = HashSet::new(); - let mut stack = vec![]; - - for &key in keys { - if visited.contains(&key) { - continue; - } - stack.push(key); - - while let Some(curr) = stack.pop() { - if visited.insert(curr) { - if !postorder { - acc.push(curr); - } - - let mut neighbors = self.navigation(&curr); - if !postorder { - neighbors.reverse(); - } - stack.extend(neighbors); - } - } - } - - if postorder { - acc.reverse(); - } - - acc - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs deleted file mode 100644 index 8630a3a2104368daf87de34918b11a8f184d2842..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/coordinate_system.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::mem; - -use crate::{graph::Graph, RankDir::*}; - -impl Graph { - pub fn coordinate_adjust(&mut self) { - let rank_dir = self.config.rankdir; - - if rank_dir == LR || rank_dir == RL { - self.swap_width_height(); - } - } - - pub fn undo_coordinate_adjust(&mut self) { - let rank_dir = self.config.rankdir; - - if rank_dir == BT || rank_dir == RL { - self.reverse_y(); - } - - if rank_dir == LR || rank_dir == RL { - self.swap_xy(); - self.swap_width_height(); - } - } - - fn swap_width_height(&mut self) { - for node in self.nodes.values_mut() { - mem::swap(&mut node.width, &mut node.height); - } - } - - fn reverse_y(&mut self) { - for node in self.nodes.values_mut() { - node.y = -node.y; - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - point.y = -point.y; - } - } - } - } - - fn swap_xy(&mut self) { - for node in self.nodes.values_mut() { - mem::swap(&mut node.x, &mut node.y); - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - mem::swap(&mut point.x, &mut point.y); - } - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs deleted file mode 100644 index b8222df9237daeebd1c880a51b5472e17456869f..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{Acyclicer, Acyclicer::NoAcyclicer, RankDir, RankDir::TB, Ranker, Ranker::TightTree}; - -#[derive(Debug, Copy, Clone)] -pub struct GraphConfig { - pub nodesep: f32, - pub edgesep: f32, - pub ranksep: f32, - pub rankdir: RankDir, - pub acyclicer: Acyclicer, - pub ranker: Ranker, - pub node_rank_factor: f32, -} - -impl Default for GraphConfig { - fn default() -> Self { - Self { - nodesep: 20.0, - edgesep: 20.0, - ranksep: 20.0, - rankdir: TB, - acyclicer: NoAcyclicer, - ranker: TightTree, - node_rank_factor: 0.0, - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs deleted file mode 100644 index b8576f7cbc85d054d1bb4c3ba881d19d403192c1..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/key.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// ## Performance -/// #### String -/// it's challenging to avoid a large number of clone operations, -/// and performance deteriorates sharply. -/// #### SmartString -/// [`SmartString::clone`] usually copies 24 bytes with [`clone`] everywhere. -/// In addition, [`SmartString`] doesn't derive [`Copy`] trait. -/// #### usize -/// only 8 bytes need to be copied. -/// All parameters are passed by value, -/// and the number of clones in the code is greatly reduced. -/// At the same time, most struct can also derive the [`Copy`] trait. -pub type Key = usize; - -#[inline] -pub fn normalize_st(s: Key, t: Key) -> (Key, Key) { - if t < s { (t, s) } else { (s, t) } -} - -pub trait KeyCodecExt { - fn of(source: Key, target: Key) -> Key; - - fn source(self) -> Key; - - fn target(self) -> Key; - - fn decode(self) -> (Key, Key); -} - -/// # Memory Layout -/// -/// The **Key** is represented using 64 bits, with the following layout: -/// -/// | Field | Size (bits) | Description | -/// |-----------------|-------------|--------------------------------------------------| -/// | Reserved | 16 | Reserved and unused.| -/// | Usable | 48 | Used to store keys.| -/// | | | - For `NodeKey`, it occupies the higher 24 bits. | -/// | | | - For `EdgeKey`, source and target each occupy 24 bits.| -/// -/// This allows handling up to 16 million nodes, which is enough for all scenarios. -impl KeyCodecExt for Key { - #[inline] - fn of(source: Key, target: Key) -> Key { - source.wrapping_shl(24) + target - } - - #[inline] - fn source(self) -> Key { - self >> 24 - } - - #[inline] - fn target(self) -> Key { - self & 0xFFFFFF - } - - #[inline] - fn decode(self) -> (Key, Key) { - let s = self >> 24; - let t = self & 0xFFFFFF; - - (s, t) - } -} - -pub const EMPTY_KEY: Key = 1 << 48; -pub const EMPTY_ROOT: Key = EMPTY_KEY + 1; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs deleted file mode 100644 index e3a09e01ce8bdf98cce7c1fc2182244bae5e2d56..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/mod.rs +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod config; -mod key; -mod node_edge; - -use std::{cmp::PartialEq, fmt::Debug}; - -use ahash::{HashMap, HashMapExt}; -pub use config::*; -pub use key::*; -pub use node_edge::*; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct Edge { - pub source: Key, - pub target: Key, -} - -impl Edge { - #[inline] - pub fn of(source: Key, target: Key) -> Self { - Self { source, target } - } - - #[inline] - pub fn to_key(self) -> Key { - Key::of(self.source, self.target) - } -} - -/// ### Compiler -/// Currently all hashMaps are stored in this structure. -/// -/// It's challenging to avoid -/// **can't borrow as mutable while borrowed as immutable**. -/// -/// ### Performance -/// Currently, almost all data is stored in [`HashMap`]. -/// There may be a better solution. -/// Node's Key is usize, which is naturally an array index. -/// If store all Nodes in Vec, -/// which can achieve extremely high performance. -/// -/// At the same time, there are some points to note: -/// -/// 1. Vec should store [`Option`] instead of Node, -/// or add an [`is_deleted`] field to Node, -/// so that better cache affinity and performance can be obtained, -/// and the code is more concise. -/// -/// 2. Under the premise of the first point, -/// we can convert all insert operations into push, -/// so that there's no [`Memory-Move`]. -/// -/// 3. Implement [`Indexing`] is needed -/// while the expansion of 10w+ Nodes is a disaster. -/// In our scenario, only one level of indexing is needed. -/// The maximum capacity of each array can be 1024. -/// Use [`LinkedList`] to store these arrays. -/// -/// 4. After implementing [`Indexing`] -/// almost all operations have a [`branch-judgment`]. -/// We should think of some ways to eliminate this performance consumption. -/// Cumbersome and repetitive code is also acceptable. -/// Or use [`likely-unlikely`] to prompt the compiler -#[derive(Debug, Default)] -pub struct Graph { - pub is_directed: bool, - pub is_compound: bool, - pub config: GraphConfig, - pub width: f32, - pub height: f32, - pub nodes: HashMap, - pub in_map: HashMap>, - pub predecessors: HashMap>, - pub out_map: HashMap>, - pub successors: HashMap>, - pub edges: HashMap, - pub edge_values: HashMap, - pub parent_map: HashMap, - pub children_map: HashMap>, - pub selfedge_map: HashMap>, - pub nesting_root: Option, - pub root: Option, - pub dummy_chains: Option>, -} - -impl Graph { - #[inline] - fn of() -> Self { - Self { is_directed: true, ..Self::default() } - } -} - -impl Graph { - #[inline] - pub fn new(directed: bool, compound: bool) -> Self { - let mut graph = Self::of(); - - graph.is_directed = directed; - graph.is_compound = compound; - - if compound { - graph.parent_map = HashMap::new(); - graph.children_map = HashMap::new(); - graph.children_map.insert(EMPTY_ROOT, vec![]); - } - - graph - } - - #[inline] - pub fn nodes(&self) -> Vec { - self.nodes.keys().copied().collect() - } - - #[inline] - pub fn sources(&self) -> Vec { - self.nodes - .keys() - .filter(|&n| self.in_map.get(n).map_or(true, |m| m.is_empty())) - .copied() - .collect() - } - - #[inline] - pub fn sinks(&self) -> Vec { - self.nodes - .keys() - .filter(|&n| self.out_map.get(n).map_or(true, |m| m.is_empty())) - .copied() - .collect() - } - - #[inline] - pub fn set_node(&mut self, key: Key, value: Option) -> &mut Self { - if self.nodes.contains_key(&key) { - value.map(|new_node| self.nodes.insert(key, new_node)); - return self; - } - - self.nodes.insert(key, value.unwrap_or_default()); - - if self.is_compound { - self.parent_map.insert(key, EMPTY_ROOT); - self.children_map.insert(key, vec![]); - self.children_map.entry(EMPTY_ROOT).or_default(); - } - - self.in_map.insert(key, vec![]); - self.predecessors.insert(key, HashMap::new()); - self.out_map.insert(key, vec![]); - self.successors.insert(key, HashMap::new()); - - self - } - - #[inline] - pub fn node(&self, id: &Key) -> Option<&GraphNode> { - self.nodes.get(id) - } - - #[inline] - pub fn node_mut(&mut self, id: &Key) -> Option<&mut GraphNode> { - self.nodes.get_mut(id) - } - - #[inline] - pub fn has_node(&self, id: &Key) -> bool { - self.nodes.contains_key(id) - } - - pub fn remove_node(&mut self, id: &Key) { - if let Some(_) = self.nodes.remove(id) { - if self.is_compound { - self.remove_from_parents_child_list(id); - self.parent_map.remove(id); - for child_id in self.children(id) { - self.set_parent(child_id, None); - } - self.children_map.remove(id); - } - - self.in_map.remove(id).map(|in_edges| { - for edge in in_edges { - self.remove_edge1(edge); - } - }); - - self.predecessors.remove(id); - - self.out_map.remove(id).map(|out_edges| { - for edge in out_edges { - self.remove_edge1(edge); - } - }); - - self.successors.remove(id); - } - } - - pub fn set_parent(&mut self, id: Key, parent: Option) -> &mut Self { - let ancestor = match parent { - Some(p) => { - let mut current = p; - while let Some(new_ancestor) = self.parent(¤t) { - current = new_ancestor; - } - current - } - None => EMPTY_ROOT, - }; - - self.set_node(id, None); - self.remove_from_parents_child_list(&id); - self.parent_map.insert(id, ancestor); - self.children_map.entry(ancestor).or_default().push(id); - - self - } - - #[inline] - fn remove_from_parents_child_list(&mut self, id: &Key) { - self.parent_map - .get(id) - .map(|p| self.children_map.get_mut(p).map(|c| c.retain(|c| c != id))); - } - - #[inline] - pub fn parent(&self, id: &Key) -> Option { - self.is_compound - .then(|| self.parent_map.get(id).filter(|&&p| p != EMPTY_ROOT).copied())? - } - - pub fn children(&self, id: &Key) -> Vec { - match (self.is_compound, id == &EMPTY_ROOT) { - (true, _) => self - .children_map - .get(id) - .map_or(vec![], |children| children.iter().copied().collect()), - (false, true) => self.nodes.keys().copied().collect(), - _ => vec![], - } - } - - #[inline] - pub fn predecessors(&self, id: &Key) -> Vec { - self.predecessors.get(id).unwrap().keys().copied().collect() - } - - #[inline] - pub fn successors(&self, id: &Key) -> Vec { - self.successors.get(id).unwrap().keys().copied().collect() - } - - #[inline] - pub fn neighbors(&self, id: &Key) -> Vec { - let mut ret = self.predecessors(id); - ret.extend(self.successors(id)); - ret - } - - #[inline] - pub fn navigation(&self, id: &Key) -> Vec { - if self.is_directed { self.successors(id) } else { self.neighbors(id) } - } - - #[inline] - pub fn edges(&self) -> Vec { - self.edges.values().copied().collect() - } - - pub fn set_edge(&mut self, source: Key, target: Key, edge: Option) -> &mut Self { - let key = Key::of(source, target); - if self.edge_values.contains_key(&key) { - if let Some(edge) = edge { - self.edge_values.insert(key, edge); - } - return self; - } - - self.set_node(source, None); - self.set_node(target, None); - - if let Some(mut edge) = edge { - edge.source = source; - edge.target = target; - self.edge_values.insert(key, edge); - } else { - self.edge_values.insert(key, GraphEdge::of(source, target)); - } - - let edge = Edge::of(source, target); - - self.edges.insert(key, edge); - if let Some(preds) = self.predecessors.get_mut(&target) { - preds.entry(source).and_modify(|c| *c += 1).or_insert(1); - } - if let Some(succ) = self.successors.get_mut(&source) { - succ.entry(target).and_modify(|c| *c += 1).or_insert(1); - } - - self.in_map.entry(target).or_default().push(edge); - self.out_map.entry(source).or_default().push(edge); - - self - } - - #[inline] - pub fn set_edge_undirected( - &mut self, - source: Key, - target: Key, - edge: Option, - ) -> &mut Self { - let (source, target) = normalize_st(source, target); - - self.set_edge(source, target, edge) - } - - #[inline] - pub fn edge(&self, source: Key, target: Key) -> Option<&GraphEdge> { - let key = Key::of(source, target); - self.edge_values.get(&key) - } - - #[inline] - pub fn edge_mut(&mut self, source: Key, target: Key) -> Option<&mut GraphEdge> { - let key = Key::of(source, target); - self.edge_values.get_mut(&key) - } - - #[inline] - pub fn has_edge(&self, source: Key, target: Key) -> bool { - let key = Key::of(source, target); - self.edge_values.contains_key(&key) - } - - pub fn remove_edge(&mut self, source: Key, target: Key) -> &mut Self { - let key = Key::of(source, target); - - if let Some(edge) = self.edges.get(&key) { - let s = &edge.source; - let t = &edge.target; - - if let Some(in_edges) = self.in_map.get_mut(t) { - in_edges.retain(|e| e != edge) - } - if let Some(out_edges) = self.out_map.get_mut(s) { - out_edges.retain(|e| e != edge) - } - - if let Some(pred) = self.predecessors.get_mut(t) { - decrement_or_remove(pred, &s) - } - - if let Some(suc) = self.successors.get_mut(s) { - decrement_or_remove(suc, &t) - } - - self.edge_values.remove(&key); - self.edges.remove(&key); - } - - self - } - - #[inline] - pub fn edge1(&self, edge: Edge) -> Option<&GraphEdge> { - self.edge(edge.source, edge.target) - } - - #[inline] - pub fn edge_mut1(&mut self, edge: Edge) -> Option<&mut GraphEdge> { - self.edge_mut(edge.source, edge.target) - } - - #[inline] - pub fn set_edge1( - &mut self, - Edge { source, target }: Edge, - edge: Option, - ) -> &mut Self { - self.set_edge(source, target, edge) - } - - #[inline] - pub fn remove_edge1(&mut self, edge: Edge) -> &mut Self { - self.remove_edge(edge.source, edge.target) - } - - #[inline] - pub fn in_edges(&self, key: &Key) -> Vec { - self.in_map[key].clone() - } - - #[inline] - pub fn out_edges(&self, key: &Key) -> Vec { - self.out_map[key].clone() - } - - #[inline] - pub fn node_edges(&self, key: &Key) -> Vec { - let mut ret = self.in_edges(key); - ret.extend(self.out_edges(key)); - ret - } -} - -#[inline] -fn decrement_or_remove(map: &mut HashMap, k: &Key) { - if let Some(value) = map.get_mut(k) { - *value -= 1; - if *value <= 0 { - map.remove(k); - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs deleted file mode 100644 index 1c3553076099b938cdf47f1b252e6bcce0c23fb0..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/graph/node_edge.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::SmallVec; - -use super::{Edge, Key, EMPTY_KEY}; - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Dummy { - Root, - Border, - Edge, - EdgeProxy, - SelfEdge, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Ranker { - TightTree, - LongestPath, - NetworkSimplex, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum RankDir { - LR, - RL, - TB, - BT, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Acyclicer { - Greedy, - Dfs, - NoAcyclicer, -} - -#[derive(Debug, Copy, Clone, Default)] -pub struct GraphNode { - pub x: f32, - pub y: f32, - pub width: f32, - pub height: f32, - pub dummy: Option, - pub rank: Option, - pub min_rank: Option, - pub max_rank: Option, - pub order: Option, - pub border_top: Option, - pub border_bottom: Option, - pub low: Option, - pub lim: Option, - pub parent: Option, - pub edge: Option, -} - -impl GraphNode { - pub fn of(x: f32, y: f32, width: f32, height: f32) -> Self { - Self { x, y, width, height, ..GraphNode::default() } - } -} - -#[derive(Debug, Copy, Clone, Default)] -pub struct Point { - pub x: f32, - pub y: f32, -} - -impl Point { - #[inline] - pub fn of(x: f32, y: f32) -> Self { - Self { x, y } - } - - #[inline] - pub fn from(node: &GraphNode) -> Self { - Self { x: node.x, y: node.y } - } -} - -#[derive(Debug, Clone)] -pub struct GraphEdge { - pub source: Key, - pub target: Key, - pub reversed: bool, - pub minlen: Option, - pub weight: Option, - pub rank: Option, - pub nesting: bool, - pub cutvalue: Option, - /// Move this field out of the structure and manage it uniformly, - /// so GraphEdge can derive the Copy trait - pub points: Option>, -} - -impl Default for GraphEdge { - fn default() -> Self { - Self { - source: EMPTY_KEY, - target: EMPTY_KEY, - reversed: false, - minlen: Some(1), - weight: Some(1.0), - rank: None, - nesting: false, - cutvalue: None, - points: None, - } - } -} - -impl GraphEdge { - pub fn of(source: Key, target: Key) -> Self { - Self { source, target, ..Self::default() } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs deleted file mode 100644 index bf91c16a290017f15e2428eb38bd940f3dddf13d..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![feature(let_chains)] -#![allow(unused_doc_comments)] - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod acyclic; -mod algo; -mod coordinate_system; -pub mod graph; -mod nesting_graph; -mod normalize; -mod order; -mod parent_dummy_chains; -mod position; -mod rank; -mod selfedge; -mod utils; - -pub use graph::*; -use mimalloc::MiMalloc; -use utils::*; - -/// ### Performance -/// When there are many nodes, most of the performance consumption -/// is Vec, HashMap memory allocation and memory transfer when expanding. -/// -/// And it's often memory allocation of very large objects. -/// -/// [`mimalloc`] is also challenging to achieve large performance improvement, -/// but at the initial stage of expansion, it can Improve performance -#[global_allocator] -static GLOBAL: MiMalloc = MiMalloc; - -pub fn layout(graph: &mut Graph) { - graph.make_space_for_edge_labels(); - graph.remove_self_edges(); - graph.make_acyclic(); - graph.nesting_run(); - let mut ncg: Graph = graph.as_non_compound(); - ncg.rank(); - ncg.transfer_node_edges(graph); - graph.remove_empty_ranks(); - graph.nesting_cleanup(); - graph.normalize_ranks(); - graph.assign_rank_min_max(); - graph.remove_edge_proxies(); - graph.normalize(); - graph.parent_dummy_chains(); - graph.order(); - graph.insert_self_edges(); - graph.coordinate_adjust(); - graph.position(); - graph.position_self_edges(); - graph.denormalize(); - graph.undo_coordinate_adjust(); - graph.translate_graph(); - graph.assign_node_intersects(); - graph.reverse_points_for_reversed_edges(); - graph.restore_cycles(); -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs deleted file mode 100644 index 18d26d256fada6d25813cc0d5faf7849587e37a5..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/nesting_graph.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! ### Idea from Sander's "Layout of Compound Directed Graphs." -//! -//! A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, -//! adds appropriate edges to ensure that all cluster nodes are placed between -//! these boundaries, and ensures that the graph is connected. -//! -//! In addition, through the use of the minlen property, that nodes -//! and subgraph border nodes to not end up on the same rank. -//! -//! Pre-Conditions: -//! -//! 1. Input graph is a DAG -//! 2. Nodes in the input graph has a minlen attribute -//! -//! Post-Conditions: -//! -//! 1. The Input graph is connected. -//! 2. Dummy nodes are added for the tops and bottoms of subgraphs. -//! 3. The minlen attribute for nodes is adjusted to ensure nodes do not -//! get placed on the same rank as subgraph border nodes. - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{ - Dummy::{Border, Root}, - EMPTY_ROOT, Graph, GraphEdge, GraphNode, Key, -}; - -impl Graph { - pub(super) fn nesting_run(&mut self) { - let root = self.init_nesting(); - let node_sep = self.calculate_node_sep(); - self.adjust_edge_minlen(node_sep); - self.process_children(root, node_sep); - self.config.node_rank_factor = node_sep as f32; - } - - pub(super) fn nesting_cleanup(&mut self) { - if let Some(root) = &self.nesting_root.clone() { - self.remove_node(root); - } - self.nesting_root = None; - - for edge in self.edges() { - if let Some(ge) = self.edge1(edge) { - ge.nesting.then(|| self.remove_edge1(edge)); - } - } - } - - fn init_nesting(&mut self) -> Key { - let root = self.add_dummy_node(Root, GraphNode::default()); - self.nesting_root = Some(root); - root - } - - fn calculate_node_sep(&self) -> i32 { - let (_, max_height) = self.tree_depths(); - (2 * max_height + 1) as i32 - } - - fn adjust_edge_minlen(&mut self, node_sep: i32) { - for edge in self.edge_values.values_mut() { - edge.minlen = Some(edge.minlen.unwrap() * node_sep); - } - } - - fn process_children(&mut self, root: Key, node_sep: i32) { - let weight = self.sum_weights() + 1.0; - let mut stack: Vec = self.children(&EMPTY_ROOT).into_iter().collect(); - - let (depths, max_height) = self.tree_depths(); - - while let Some(key) = stack.pop() { - if self.children(&key).is_empty() { - if key != root { - self.set_edge(root, key, Some(GraphEdge::with_minlen(node_sep))); - } - continue; - } - - let (top, bottom) = self.link_border_nodes(key); - self.update_node_borders(key, top, bottom); - - for k in self.children(&key) { - let (child_top, child_bottom, this_weight) = self.nodes[&k].top_bottom(k, weight); - let minlen = - if child_top == child_bottom { max_height - depths[&k] + 1 } else { 1 }; - self.add_border_edge(minlen, this_weight, top, bottom, child_top, child_bottom); - stack.push(k); - } - - if self.parent(&key).is_none() { - self.set_edge( - root, - top, - Some(GraphEdge::with_minlen_nesting(depths[&key] + max_height)), - ); - } - } - } - - fn link_border_nodes(&mut self, key: Key) -> (Key, Key) { - let top = self.add_border_node(); - let bottom = self.add_border_node(); - self.set_parent(top, Some(key)); - self.set_parent(bottom, Some(key)); - (top, bottom) - } - - fn update_node_borders(&mut self, key: Key, top: Key, bottom: Key) { - if let Some(node) = self.node_mut(&key) { - node.border_top = Some(top); - node.border_bottom = Some(bottom); - } - } -} - -impl GraphEdge { - fn with_minlen(minlen: i32) -> Self { - GraphEdge { minlen: Some(minlen), weight: Some(0.0), ..GraphEdge::default() } - } - - fn with_minlen_nesting(minlen: usize) -> Self { - GraphEdge { - minlen: Some(minlen as i32), - weight: Some(0.0), - nesting: true, - ..GraphEdge::default() - } - } -} - -impl Graph { - fn tree_depths(&self) -> (HashMap, usize) { - let mut depths: HashMap = HashMap::new(); - let mut stack: Vec<(Key, usize)> = Vec::new(); - let mut max_depth: usize = 0; - - for node_id in self.children(&EMPTY_ROOT) { - stack.push((node_id, 1)); - } - - while let Some((node_id, depth)) = stack.pop() { - for child_id in self.children(&node_id) { - stack.push((child_id, depth + 1)); - } - depths.insert(node_id, depth); - max_depth = max_depth.max(depth); - } - - if max_depth > 0 { - max_depth -= 1; - } - - (depths, max_depth) - } - - fn sum_weights(&self) -> f32 { - let mut sum_weight: f32 = 0.0; - - for edge in self.edge_values.values() { - if let Some(weight) = edge.weight { - sum_weight += weight; - } - } - - sum_weight - } -} - -impl Graph { - fn add_border_node(&mut self) -> Key { - self.add_dummy_node(Border, GraphNode::default()) - } - - fn add_border_edge( - &mut self, - minlen: usize, - weight: f32, - top: Key, - bottom: Key, - child_top: Key, - child_bottom: Key, - ) { - let top_edge = GraphEdge { - minlen: Some(minlen as i32), - weight: Some(weight), - nesting: true, - ..GraphEdge::default() - }; - self.set_edge(top, child_top, Some(top_edge)); - - let bottom_edge = GraphEdge { - minlen: Some(minlen as i32), - weight: Some(weight), - nesting: true, - ..GraphEdge::default() - }; - self.set_edge(child_bottom, bottom, Some(bottom_edge)); - } -} - -impl GraphNode { - fn top_bottom(&self, key: Key, weight: f32) -> (Key, Key, f32) { - let (top, this_weight) = if let Some(border_top) = self.border_top { - (border_top, 2.0 * weight) - } else { - (key, weight) - }; - - let bottom = if let Some(border_bottom) = self.border_bottom { border_bottom } else { key }; - - (top, bottom, this_weight) - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs deleted file mode 100644 index 36e598960e9a1761e006b3b6bf5e98f26093eb15..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/normalize.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::smallvec; - -use crate::{Dummy, Dummy::EdgeProxy, Edge, Graph, GraphEdge, GraphNode, Point, EMPTY_KEY}; - -impl Graph { - pub(super) fn normalize(&mut self) { - self.dummy_chains = Some(vec![]); - for edge in self.edges() { - self.normalize_edge(edge); - } - } - - fn normalize_edge(&mut self, e: Edge) -> Option<()> { - let mut s = e.source; - let t = e.target; - let mut s_rank = self.node(&s)?.rank?; - let t_rank = self.node(&t)?.rank?; - - let edge = self.edge_mut1(e)?; - edge.points = Some(smallvec![]); - let weight = edge.weight; - let rank = edge.rank.unwrap_or(0); - - self.remove_edge1(e); - - let mut i = 0; - s_rank += 1; - while s_rank < t_rank { - let mut dummy_node = - GraphNode { edge: Some(e), rank: Some(s_rank), ..GraphNode::default() }; - if s_rank == rank { - dummy_node.dummy = Some(EdgeProxy); - } - let dummy_id = self.add_dummy_node(Dummy::Edge, dummy_node); - let dummy_edge = GraphEdge { weight, ..GraphEdge::default() }; - self.set_edge(s, dummy_id, Some(dummy_edge)); - if i == 0 { - let dummy_chains = &mut self.dummy_chains; - dummy_chains.get_or_insert(vec![]).push(dummy_id); - } - s = dummy_id; - i += 1; - s_rank += 1; - } - - let graph_edge = GraphEdge { weight, ..GraphEdge::default() }; - self.set_edge(s, t, Some(graph_edge)); - - None - } - - pub(super) fn denormalize(&mut self) -> Option<()> { - if let Some(dummy_chains) = self.dummy_chains.clone() { - for &dummy_id in &dummy_chains { - let Some(mut node) = self.node(&dummy_id).copied() else { - continue; - }; - - let edge_obj = node.edge?; - let mut prev_edge = self - .edge1(edge_obj) - .cloned() - .unwrap_or(GraphEdge { points: Some(smallvec![]), ..GraphEdge::default() }); - let mut curr_dummy = dummy_id; - while node.dummy.is_some() { - let new_dummy = - self.successors(&curr_dummy).first().copied().unwrap_or(EMPTY_KEY); - self.remove_node(&curr_dummy); - prev_edge.points.as_mut()?.push(Point::of(node.x, node.y)); - - curr_dummy = new_dummy; - node = self.node(&curr_dummy).cloned()?; - } - - self.set_edge1(edge_obj, Some(prev_edge)); - } - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs deleted file mode 100644 index a4e84755a111574703d25ae4913f0e31c0bad634..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/build_layer_graph.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Constructs a graph that can be used to sort a layer of nodes. -//! The graph contains all base and subgraph nodes from the request -//! layer in their original hierarchy and any edges -//! that are incident on these nodes and is of the type -//! requested by the "relationship" parameter. -//! -//! Nodes from the requested rank that don't have parents are assigned a root -//! node in the output graph, which is set in the root graph attribute. -//! This makes it easy to walk the hierarchy of movable nodes during ordering. -//! -//! Pre-conditions: -//! -//! 1. Input graph is a DAG -//! 2. Base nodes in the input graph have rank attribute -//! 3. Subgraph nodes in the input graph have minRank and maxRank attributes -//! 4. Edges have an assigned weight -//! -//! Post-conditions: -//! -//! 1. The Output graph has all nodes in the movable rank with preserved -//! hierarchy. -//! 2. Root nodes in the movable layer are made children of the node -//! indicated by the root attribute of the graph. -//! 3. Non-movable nodes incident on movable nodes, selected by the -//! relationship parameter, are included in the graph without a hierarchy. -//! 4. Edges incident on movable nodes, selected by the relationship -//! parameter, are added to the output graph. -//! 5. The weights for copied edges are aggregated as needed, since the output -//! graph isn't a multi-graph. - -use crate::{unique_key, Graph, GraphEdge, GraphNode, Key}; - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone, PartialEq)] -pub(super) enum EdgeRelation { - In, - Out, -} - -impl Graph { - pub(super) fn build_layer_graph(&mut self, rank: i32, relation: EdgeRelation) -> Graph { - let root = self.create_root_node(); - let mut lg = Graph::new(true, false); - lg.root = Some(root); - - for (&key, &node) in &self.nodes { - if node.is_in_rank(Some(rank)) { - lg.set_node(key, Some(node)); - lg.set_layer_parent(key, self.parent(&key), root); - - self.process_relations(&mut lg, key, relation); - } - } - - lg - } - - fn set_layer_parent(&mut self, key: Key, parent: Option, root: Key) { - match parent { - Some(p) => self.set_parent(key, Some(p)), - _ => self.set_parent(key, Some(root)), - }; - } - - fn process_relations(&self, lg: &mut Graph, key: Key, relation: EdgeRelation) -> Option<()> { - let edges = match relation { - EdgeRelation::In => &self.in_map[&key], - EdgeRelation::Out => &self.out_map[&key], - }; - for &edge in edges { - let source = if edge.source == key { edge.target } else { edge.source }; - let weight = lg.edge(source, key).and_then(|e| e.weight).unwrap_or(0.0); - let new_weight = self.edge1(edge)?.weight.unwrap_or(0.0) + weight; - lg.set_edge( - source, - key, - Some(GraphEdge { weight: Some(new_weight), ..Default::default() }), - ); - } - - None - } - - fn create_root_node(&self) -> Key { - loop { - let key = unique_key(); - - if !self.has_node(&key) { - return key; - } - } - } -} - -impl GraphNode { - #[inline] - fn is_in_rank(&self, rank: Option) -> bool { - self.rank == rank || self.min_rank <= rank && rank <= self.max_rank - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs deleted file mode 100644 index a390e804ad027ca81aeaef80d3b02caa122afc31..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/cross_count.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! ### Algorithm derived from Barth et al., "Bilayer Cross Counting" -//! A function that takes a layering (an array of layers, each with an array of -//! ordered nodes) and a graph and returns a weighted crossing count. -//! -//! Pre-Conditions: -//! -//! 1. Input graph must be non-multigraph, directed, and include only simple edges. -//! 2. Edges in the input graph must have assigned weights. -//! -//! Post-Conditions: -//! -//! 1. The graph and layering matrix are left unchanged. - -use ahash::HashMap; - -use crate::{Graph, Key}; - -impl Graph { - pub fn cross_count(&mut self, matrix: &mut [Vec]) -> usize { - let mut count = 0; - - /// Sort all the edges between the north and south layers by their position - /// in the north layer and then the south. - /// Map these edges to the position of their head in the south layer. - for idx in 1..matrix.len() { - let north_idx = idx - 1; - let south_idx = idx; - - let south_layer = &matrix[south_idx]; - let south_pos: HashMap = - south_layer.iter().enumerate().map(|(idx, val)| (*val, idx)).collect(); - - let mut south_entries: Vec<(usize, usize)> = matrix[north_idx] - .iter() - .flat_map(|k| { - self.out_map[k] - .iter() - .map(|&e| { - let pos = south_pos[&e.target]; - let weight = self.edge1(e).unwrap().weight.unwrap(); - (pos, weight as usize) - }) - .collect::>() - }) - .collect(); - - south_entries.sort_by_key(|e| e.0); - - let mut first_index = south_layer.len().next_power_of_two(); - - let tree_size = 2 * first_index - 1; - first_index -= 1; - - let mut tree = vec![0; tree_size]; - let mut c = 0; - - for &(f, s) in &south_entries { - let mut idx = f + first_index; - tree[idx] += s; - - let mut weight_sum = 0; - while idx > 0 { - if idx % 2 != 0 { - weight_sum += tree[idx + 1]; - } - idx = (idx - 1) >> 1; - tree[idx] += s; - } - c += s * weight_sum; - } - - count += c - } - - count - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs deleted file mode 100644 index 6e8411699311207956af41069accb1334782a10d..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/init_order.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! ### Gansner et al., "A Technique for Drawing Directed Graphs" -//! Assigns an initial order value for each node by performing a DFS search -//! starting from nodes in the first rank. -//! Nodes are assigned an order in their rank as they're first visited. -//! -//! Returns a layering matrix with an array per layer and each layer sorted by -//! the order of its nodes. - -use ahash::{HashSet, HashSetExt}; - -use crate::{Graph, Key}; - -impl Graph { - pub(super) fn init_order(&self) -> Option>> { - let mut visited: HashSet = HashSet::new(); - let mut simple_nodes: Vec = - self.nodes.keys().filter(|k| self.children(k).is_empty()).copied().collect(); - - let mut max_rank = 0; - for id in &simple_nodes { - if let Some(rank) = self.nodes[id].rank { - max_rank = max_rank.max(rank) - } - } - - let mut layers: Vec> = vec![Vec::new(); max_rank as usize + 1]; - - simple_nodes.sort_by_key(|id| Some(self.nodes[id].rank?)); - - for id in simple_nodes { - let mut stack = vec![id]; - - while let Some(id) = stack.pop() { - if !visited.insert(id) { - continue; - } - - let rank = self.nodes[&id].rank? as usize; - layers[rank].push(id); - - stack.extend(self.successors(&id)); - } - } - - Some(layers) - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs deleted file mode 100644 index bc7ae1435882557bfedbb3c6055e6e231904ce4b..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -mod build_layer_graph; -mod cross_count; -mod init_order; -mod subgraph; - -use std::ops::Range; - -use build_layer_graph::EdgeRelation; - -use crate::{Graph, Key, EMPTY_KEY}; - -impl Graph { - pub fn order(&mut self) -> Option<()> { - let mut matrix = self.init_order()?; - self.assign_order(&matrix); - - let mut best_cc = f64::INFINITY; - let mut best: Vec> = Vec::new(); - - let mut last_best = 0; - loop { - if last_best >= 4 { - break; - } - - matrix = self.key_matrix(); - let cc = self.cross_count(&mut matrix) as f64; - - if cc < best_cc { - last_best = 0; - best = matrix; - best_cc = cc; - } else { - last_best += 1; - } - } - - self.assign_order(&best) - } - - #[allow(dead_code)] - #[inline] - fn build_layer_graphs(&mut self, ranks: Range, relationship: EdgeRelation) -> Vec { - ranks.map(|rank| self.build_layer_graph(rank, relationship)).collect() - } - - fn assign_order(&mut self, matrix: &[Vec]) -> Option<()> { - for keys in matrix { - for (i, key) in keys.iter().enumerate() { - self.node_mut(key)?.order = Some(i); - } - } - - None - } -} - -#[allow(dead_code)] -fn sweep_graphs(layer_graphs: &mut [Graph], bias_right: bool) -> Option<()> { - let mut graph = Graph::new(true, false); - - for lg in layer_graphs { - let root = lg.root.unwrap_or(EMPTY_KEY); - let sorted = lg.sort_subgraph(&graph, root, bias_right)?; - - for (i, key) in sorted.keys.iter().enumerate() { - if let Some(node) = lg.node_mut(key) { - node.order = Some(i); - } - } - - lg.add_constraints(&mut graph, &sorted.keys); - } - - None -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs deleted file mode 100644 index 0450567e03eb6dc8833d74f65fd7c08a21429475..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/add_constraints.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{Graph, Key}; - -impl Graph { - pub(crate) fn add_constraints(&self, cg: &mut Graph, keys: &[Key]) { - let mut prev_map: HashMap = HashMap::new(); - let mut prev_root: Option = None; - - for key in keys { - let mut current = self.parent(key); - - while let Some(child) = current { - match self.parent(&child) { - Some(parent) => { - if let Some(&prev_child) = prev_map.get(&parent) { - if prev_child != child { - cg.set_edge(prev_child, child, None); - return; - } - } - prev_map.insert(parent, child); - } - None => { - if let Some(prev_child) = prev_root { - if prev_child != child { - cg.set_edge(prev_child, child, None); - return; - } - } - prev_root = Some(child); - } - } - current = self.parent(&child); - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs deleted file mode 100644 index 60cd153eef8577c8b771f1803a39ec0737ff5c32..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/barycenters.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::Barycenter; -use crate::{Graph, Key}; - -impl Graph { - pub(super) fn barycenters(&self, movable: &[Key]) -> Vec { - movable - .iter() - .map(|&key| { - if self.edge_values.is_empty() { - return Barycenter { key, barycenter: None, weight: None }; - } - - let (sum, weight) = self - .edge_values - .values() - .try_fold((0.0, 0.0), |(sum, weight), edge| { - let w = edge.weight?; - let order = self.node(&edge.source)?.order? as f32; - Some((sum + w * order, weight + w)) - }) - .unwrap(); - - return Barycenter { key, barycenter: Some(sum / weight), weight: Some(weight) }; - }) - .collect() - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs deleted file mode 100644 index 3491ec7a22b752f0fb5532517639f98686bbd504..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/context.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use crate::Key; - -#[derive(Copy, Clone)] -pub(super) struct Barycenter { - pub key: Key, - pub barycenter: Option, - pub weight: Option, -} - -#[derive(Copy, Clone)] -pub struct ResolvedEntry { - /// ## Layout - /// This struct has 3 bytes of padding. If set either [`idx`] - /// or [`indegree`] to u8, the size of the struct be reduced - /// by 4 bytes, but *2^8-1* may not be enough. - /// - /// ## Another Solution - /// Stores these three fields into u32. [`merged`] occupies - /// 1 byte, [`idx`] and [`degree`] each occupies 15 bytes. - pub idx: u16, - pub indegree: u16, - pub merged: bool, - pub barycenter: Option, - pub weight: Option, -} - -impl ResolvedEntry { - #[inline] - pub(super) fn of(idx: u16) -> Self { - Self { idx, indegree: 0, merged: false, barycenter: None, weight: None } - } -} - -/// A context for managing [`ResolvedEntry`] sources, sinks, and keys. -/// -/// Storing these fields directly within `ResolvedEntry` would prevent -/// it from deriving the [`Copy`] trait. Additionally, it'd lead to -/// excessive cloning when accessing these fields. -/// -/// ## Performance -/// There are three more solutions, Raw Pointer preferred. -/// - **Reference** Safe Rust, Manual lifecycle management, High Performance -/// - **RC/RefCell** Safe Rust, with extremely cumbersome boilerplate code. -/// - **NonNull** The best Performance, but Unsafe Rust, -#[derive(Default)] -pub(super) struct Context { - pub entries: HashMap, - pub sources_map: HashMap>, - pub sinks_map: HashMap>, - pub keys_map: HashMap>, - pub keys: Vec, - pub index: usize, -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs deleted file mode 100644 index 77b545d9859be41efb9e922ac6b2a26850140342..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod add_constraints; -mod barycenters; -mod context; -mod resolve_conflict; -mod sort; - -use ahash::{HashMap, HashMapExt}; -pub use context::*; - -use crate::{Graph, Key}; - -#[derive(Debug, Clone, Default)] -pub struct Subgraph { - pub keys: Vec, - pub barycenter: f32, - pub weight: f32, -} - -impl Graph { - pub fn sort_subgraph(&self, cg: &Graph, start_key: Key, bias_right: bool) -> Option { - let mut stack: Vec<(Key, Subgraph)> = vec![(start_key, Subgraph::default())]; - let mut subgraphs: HashMap = HashMap::new(); - - while let Some((curr_key, mut subgraph)) = stack.pop() { - let movable = self.children(&curr_key); - - let mut barycenters = self.barycenters(&movable); - for entry in &mut barycenters { - if self.children(&entry.key).len() > 0 { - stack.push((entry.key, Subgraph::default())); - } - } - - let mut ctx = Context::default(); - ctx.resolve(cg, &barycenters); - ctx.expand_subgraph(&subgraphs); - - subgraph = ctx.next_subgraph(bias_right)?; - - if stack.is_empty() { - return Some(subgraph); - } - - subgraphs.insert(curr_key, subgraph); - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs deleted file mode 100644 index dc7cb9151141e8ef6b566538cb63debf573bf05d..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/resolve_conflict.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! ### Implementation Based on the description in Forster. -//! ### "A Fast and Simple Heuristic for Constrained Two-Level Crossing Reduction" -//! Thought differs in some specific details. -//! -//! Given a list of entries with the form {key, barycenter, weight} and a -//! constraint graph, this function resolves any conflicts between the -//! constraint graph and the barycenters for the entries. -//! If the barycenters for an entry violate a constraint in the constraint graph, -//! then coalesce the nodes in the conflict into a new node that respects the -//! constraint and aggregates barycenter and weight information. -//! -//! Pre-Conditions: -//! -//! 1. Each entry has the form {key, barycenter, weight}, or if the node has -//! no barycenter, then {key}. -//! -//! Returns: -//! -//! A new list of entries with the form {keys, idx, barycenter, weight}. -//! The list `keys` may either be a singleton or it may be aggregation of nodes -//! ordered such that they don't violate constraints from the constraint graph. -//! The property `idx` is the lowest original index of the elements in `keys`. - -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use super::{Barycenter, Context, ResolvedEntry, Subgraph}; -use crate::{Edge, Graph, Key}; - -impl Context { - pub(super) fn resolve(&mut self, cg: &Graph, entries: &[Barycenter]) -> Option<()> { - for (idx, entry) in entries.iter().enumerate() { - let mut tmp = ResolvedEntry::of(idx as u16); - if entry.barycenter.is_some() { - tmp.barycenter = entry.barycenter; - tmp.weight = entry.weight; - } - self.entries.insert(entry.key, tmp); - self.keys_map.insert(entry.key, vec![entry.key]); - } - - for &Edge { source, target } in cg.edges.values() { - let flag = self.entries.get(&source).is_some(); - let te = self.entries.get_mut(&target); - if flag && let Some(te) = te { - te.indegree += 1; - self.sinks_map.entry(source).or_default().push(target); - } - } - - let mut source_array: Vec = self - .entries - .iter() - .filter(|(k, _)| self.sources_map.get(k).is_none()) - .map(|(&k, _)| k) - .collect(); - - self.do_resolve(&mut source_array) - } - - fn handle_in(&mut self, source: Key, target: Key) -> Option<()> { - let source_ent = self.entries[&target]; - if source_ent.merged { - return None; - } - - let target_ent = self.entries[&source]; - - if source_ent.barycenter.is_none() - || target_ent.barycenter.is_none() - || source_ent.barycenter >= target_ent.barycenter - { - self.merge_entries(target, source); - } - - None - } - - fn handle_out(&mut self, source: Key, target: Key) -> Option<()> { - self.sources_map.get_mut(&target)?.push(source); - - let target_ent = self.entries.get_mut(&target)?; - target_ent.indegree -= 1; - - if target_ent.indegree == 0 { - self.sinks_map.get_mut(&source)?.push(target); - } - - None - } - - fn do_resolve(&mut self, keys: &mut Vec) -> Option<()> { - while let Some(key) = keys.pop() { - for &source_key in self.sources_map[&key].clone().iter().rev() { - self.handle_in(source_key, key); - } - - for sink_key in self.sinks_map.get(&key).cloned()? { - self.handle_out(key, sink_key); - } - } - - None - } - - fn merge_entries(&mut self, source_key: Key, target_key: Key) -> Option<()> { - let mut sum = 0.0; - let mut weight = 0.0; - - let source = &self.entries[&source_key]; - if let Some(w) = source.weight { - let source_barycenter = source.barycenter?; - sum += source_barycenter * w; - weight += w; - } - let source_idx = source.idx; - - let target = &self.entries[&target_key]; - if let Some(w) = target.weight { - let target_barycenter = target.barycenter?; - sum += target_barycenter * w; - weight += w; - } - - let source_keys = self.keys_map[&target_key].clone(); - let target_keys = self.keys_map.get_mut(&source_key)?; - target_keys.extend(source_keys); - - let target = self.entries.get_mut(&target_key)?; - target.barycenter = Some(sum / weight); - target.weight = Some(weight); - target.idx = source_idx.min(target.idx); - - self.entries.get_mut(&source_key)?.merged = true; - - None - } - - #[inline] - pub(super) fn expand_subgraph(&mut self, subgraphs: &HashMap) { - for (key, keys) in &mut self.keys_map { - if let Some(subgraph) = subgraphs.get(key) { - keys.extend(&subgraph.keys); - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs deleted file mode 100644 index 6ccdbf796fb39502719de8c510aa961a7267eb52..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/order/subgraph/sort.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{cmp, cmp::Ordering}; - -use super::{Context, ResolvedEntry, Subgraph}; -use crate::Key; - -impl Context { - pub fn next_subgraph(&mut self, bias_right: bool) -> Option { - let (mut sortable, mut unsortable) = self - .entries - .iter() - .map(|(&k, &e)| (k, e)) - .partition::, _>(|(_, ent)| ent.barycenter.is_some()); - - sortable.sort_by(|(_, lhs), (_, rhs)| cmp_with_bias(lhs, rhs, bias_right)); - unsortable.sort_by_key(|(_, entry)| cmp::Reverse(entry.idx)); - - let mut sum = 0.0; - let mut weight = 0.0; - - self.consume(&mut sortable); - - for (k, entry) in sortable { - let keys = &self.keys_map[&k]; - self.index += keys.len(); - self.keys.extend(keys); - let entry_weight = entry.weight?; - sum += entry.barycenter? * entry_weight; - weight += entry_weight; - self.consume(&mut unsortable); - } - - let mut subgraph = Subgraph::default(); - let mut keys = vec![]; - keys.extend(&self.keys); - subgraph.keys = keys; - if weight > 0.0 { - subgraph.barycenter = sum / weight; - subgraph.weight = weight; - } - - Some(subgraph) - } - - fn consume(&mut self, consumable: &mut Vec<(Key, ResolvedEntry)>) -> Option<()> { - if consumable.is_empty() { - return None; - } - - while let Some((last, _)) = consumable.last() { - let idx = self.entries[last].idx; - if idx > self.index as u16 { - break; - } - - let keys = &self.keys_map[last]; - - self.keys.extend(keys); - - consumable.pop(); - self.index += 1; - } - - None - } -} - -fn cmp_with_bias(lhs: &ResolvedEntry, rhs: &ResolvedEntry, bias: bool) -> Ordering { - let lb = lhs.barycenter; - let rb = rhs.barycenter; - if lb < rb { - return Ordering::Greater; - } else if lb > rb { - return Ordering::Less; - } - - if !bias { lhs.idx.cmp(&rhs.idx) } else { rhs.idx.cmp(&lhs.idx) } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs deleted file mode 100644 index 9d11943941a77ab5af9d3b7027ff1e676e28f0ee..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/parent_dummy_chains.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use ahash::{HashMap, HashMapExt}; - -use crate::{Edge, Graph, Key, EMPTY_KEY, EMPTY_ROOT}; - -impl Graph { - /// Processes dummy chains in the graph - /// to establish proper parent-child relationships. - /// - /// This method performs a post-order traversal to determine node limits, - /// then processes each - /// dummy chain to find the lowest common ancestor - /// (LCA) and adjust parent relationships along - /// the path between edge endpoints. - /// - /// # Returns - /// - `Option<()>`: always returns None, - pub(super) fn parent_dummy_chains(&mut self) -> Option<()> { - let lims = postorder(self); - let dummy_chains = self.dummy_chains.clone().unwrap_or(vec![]); - - for mut dummy_id in dummy_chains { - let edge = self.node(&dummy_id)?.edge?; - let (path, ref lca) = self.find_lca(&lims, edge.source, edge.target); - - if !path.is_empty() { - self.traverse_path(&mut dummy_id, &path, lca, edge)?; - } - } - - None - } - - /// Traverses a path between nodes - /// while setting parent relationships for dummy nodes. - /// - /// # Arguments - /// * `dummy_id` - Mutable reference to the current dummy node key - /// * `path` - Path through which to establish relationships - /// * `lca` - Lowest common ancestor of the edge endpoints - /// * `edge` - Original edge being processed - fn traverse_path( - &mut self, - dummy_id: &mut Key, - path: &[Key], - lca: &Key, - edge: Edge, - ) -> Option<()> { - let mut path_iter = path.iter().peekable(); - let mut ascending = true; - - while *dummy_id != edge.target { - let node = self.node(dummy_id)?; - let mut current = (if ascending { - path_iter.find(|k| self.node(k).unwrap().max_rank > node.rank) - } else { - path_iter.rfind(|k| self.node(k).unwrap().min_rank < node.rank) - }) - .unwrap_or(lca); - - if ascending && current == lca { - ascending = false; - path_iter = path.iter().peekable(); - current = path_iter.next_back()?; - } - - self.set_parent(*dummy_id, Some(*current)); - *dummy_id = self.successors(dummy_id).first().copied().unwrap_or(EMPTY_KEY) - } - - None - } - - - /// Finds the lowest common ancestor (LCA) and constructs a path between two nodes. - /// - /// # Arguments - /// * `lims` - Post-order traversal limits map - /// * `source` - Source node key - /// * `target` - Target node key - /// - /// # Returns - /// - `(Vec, Key)`: tuple containing: - /// - Full path from source to target through LCA - /// - Lowest common ancestor key - fn find_lca(&self, lims: &HashMap, source: Key, target: Key) -> (Vec, Key) { - let mut s_path: Vec = vec![]; - let mut t_path: Vec = vec![]; - - let lim = lims[&source].min(lims[&target]); - - let mut lca = source; - while let Some(parent) = self.parent(&lca) { - lca = parent; - s_path.push(parent); - if lims.get(&parent).map_or(false, |&l| lim == l) { - break; - } - } - - let mut parent = self.parent(&target).unwrap_or(lca); - while parent != lca { - t_path.push(parent); - parent = self.parent(&parent).unwrap_or(lca); - } - - t_path.reverse(); - s_path.extend(t_path); - - (s_path, lca) - } -} - -/// Generates post-order traversal limits for graph nodes. -fn postorder(g: &Graph) -> HashMap { - let mut ret: HashMap = HashMap::new(); - let mut lim = 0; - - for child in g.children(&EMPTY_ROOT) { - let mut stack = vec![child]; - - while let Some(node) = stack.pop() { - if ret.contains_key(&node) { - continue; - } - - ret.insert(node, lim); - lim += 1; - stack.extend(g.children(&node)) - } - } - - ret -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs deleted file mode 100644 index f45dc79abfff1f7cc1f3c2edc7e75609c05a989d..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/align.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt, HashSet, HashSetExt}; - -use super::{BlockGraph, Context, Vertical, Vertical::*}; -use crate::{Graph, Key, KeyCodecExt}; - -impl Graph { - pub(super) fn vertical_alignment( - &self, - ctx: &mut Context, - matrix: &[Vec], - vertical: Vertical, - ) { - let mut pos: HashMap = HashMap::new(); - - for keys in matrix { - for (order, &key) in keys.iter().enumerate() { - ctx.root.insert(key, key); - ctx.align.insert(key, key); - pos.insert(key, order); - } - } - - for keys in matrix { - let mut prev_idx: i32 = -1; - for &key in keys { - let mut neighbors: Vec = match vertical { - Top => self.predecessors(&key), - Bottom => self.successors(&key), - }; - - if neighbors.is_empty() { - continue; - } - - /// Here we can improve performance a little by **unwrap** - neighbors.sort_by_key(|id| pos.get(id)); - let mid = (neighbors.len() as f32 - 1.0) / 2.0000001; - let start = mid.floor() as usize; - let end = mid.ceil() as usize; - for idx in start..=end { - let neighbor = neighbors[idx]; - if ctx.align[&key] == key - && prev_idx < (pos[&neighbor] as i32) - && !ctx.has_conflict(key, neighbor) - { - let x = ctx.root[&neighbor]; - ctx.align.insert(neighbor, key); - ctx.align.insert(key, x); - ctx.root.insert(key, x); - - prev_idx = pos[&neighbor] as i32; - } - } - } - } - } - - pub(super) fn horizontal_compaction( - &self, - matrix: &[Vec], - ctx: &Context, - ) -> HashMap { - let mut compact: HashMap = HashMap::new(); - let block: BlockGraph = self.build_block_graph(matrix, &ctx.root); - - let mut stack = block.nodes(); - let mut visited: HashSet = HashSet::new(); - while let Some(k) = stack.pop() { - if visited.contains(&k) { - let in_edges = &block.in_edges[&k]; - let mut val: f32 = 0.0; - for key in in_edges { - let source = key.source(); - let ev: f32 = compact[&source] + block.edges[key]; - val = val.max(ev) - } - - compact.insert(k, val); - } else { - visited.insert(k); - stack.push(k); - stack.extend(block.predecessors(&k)); - } - } - - for &k in ctx.align.values() { - compact.insert(k, compact[&ctx.root[&k]]); - } - - compact - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs deleted file mode 100644 index 61c1c798603f5b8ecf953e06488f399bbafcbb74..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/block_graph.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; - -use crate::{Graph, Key, KeyCodecExt, EMPTY_KEY}; - -#[derive(Default)] -pub struct BlockGraph { - nodes: HashMap, - pub(super) in_edges: HashMap>, - predecessors: HashMap>, - pub(super) edges: HashMap, -} - -impl BlockGraph { - pub fn set_node(&mut self, key: Key) -> &mut Self { - if self.nodes.contains_key(&key) { - return self; - } - - self.nodes.insert(key, EMPTY_KEY); - self.in_edges.insert(key, vec![]); - self.predecessors.insert(key, HashMap::new()); - - self - } - - #[inline] - pub fn nodes(&self) -> Vec { - self.nodes.keys().copied().collect() - } - - #[inline] - pub fn predecessors(&self, k: &Key) -> Vec { - self.predecessors.get(k).map_or(vec![], |p| p.keys().copied().collect()) - } - - pub fn set_edge(&mut self, source: Key, target: Key, val: f32) -> &mut Self { - let key = Key::of(source, target); - if self.edges.contains_key(&key) { - self.edges.insert(key, val); - return self; - } - - self.set_node(source); - self.set_node(target); - - self.edges.insert(key, val); - - self.predecessors - .get_mut(&target) - .map(|preds| preds.entry(source).and_modify(|c| *c += 1).or_insert(1)); - - self.in_edges.entry(target).or_default().push(key); - - self - } -} - -impl Graph { - pub(super) fn build_block_graph( - &self, - matrix: &[Vec], - root: &HashMap, - ) -> BlockGraph { - let mut block_graph: BlockGraph = BlockGraph::default(); - - for keys in matrix { - let mut target: Option = None; - for &key in keys { - let source = root[&key]; - block_graph.set_node(source); - if let Some(t) = target { - let target = root[&t]; - let prev_max = match block_graph.edges.get(&Key::of(target, source)) { - Some(&x) => x, - None => 0.0, - }; - - let max = self.sep(key, t).max(prev_max); - block_graph.set_edge(target, source, max); - } - target = Some(key); - } - } - - block_graph - } - - fn sep(&self, source: Key, target: Key) -> f32 { - let nodesep = self.config.nodesep; - let edgesep = self.config.edgesep; - - let source_node = &self.nodes[&source]; - let target_node = &self.nodes[&target]; - - let mut sum = source_node.width / 2.0; - sum += if source_node.dummy.is_some() { edgesep } else { nodesep } / 2.0; - sum += if target_node.dummy.is_some() { edgesep } else { nodesep } / 2.0; - sum += target_node.width / 2.0; - - sum - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs deleted file mode 100644 index 93823fa1f43067b95bd948963c5552a539f88263..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/conflict.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::Context; -use crate::{normalize_st, Dummy::Border, Graph, Key}; - -#[derive(Copy, Clone)] -struct ScanCtx { - south_start: usize, - south_end: usize, - prev_north: i32, - next_north: i32, -} - -impl ScanCtx { - fn of(south_start: usize, south_end: usize, prev_north: i32, next_north: i32) -> Self { - Self { south_start, south_end, prev_north, next_north } - } -} - -impl Graph { - pub(super) fn find_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) { - if !matrix.is_empty() { - self.find_type1_conflict(ctx, matrix); - self.find_type2_conflict(ctx, matrix); - } - } - - fn find_type1_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) -> Option<()> { - let mut prev_len = matrix.first()?.len(); - - for layer in matrix.iter().skip(1) { - let mut k0 = 0; - let mut scan_pos = 0; - let last_key = layer.last()?; - - for (i, key) in layer.iter().enumerate() { - let w = self.other_inner_segment(key); - - let k1 = match (w, key == last_key) { - (None, false) => continue, - (Some(w), _) => self.node(&w)?.order?, - (None, true) => prev_len, - }; - - for scan_key in &layer[scan_pos..i + 1] { - let scan_node = self.node(scan_key)?; - for pre_key in &self.predecessors(scan_key) { - let pre_node = self.node(pre_key)?; - let pos = pre_node.order?; - let both_dummy = pre_node.dummy.is_some() && scan_node.dummy.is_some(); - if (pos < k0 || k1 < pos) && !both_dummy { - ctx.add_conflict(*pre_key,*scan_key) - } - } - } - - scan_pos = i + 1; - k0 = k1; - prev_len = layer.len(); - } - } - - None - } - - fn find_type2_conflict(&mut self, ctx: &mut Context, matrix: &[Vec]) -> Option<()> { - let mut north_len = matrix.first()?.len(); - - for south in matrix.iter().skip(1) { - let mut prev_north_pos = -1; - let mut next_north_pos = 0; - let mut south_pos = 0; - let south_len = south.len(); - - for (south_ahead, key) in south.iter().enumerate() { - if self.node(key)?.dummy == Some(Border) { - let preds = self.predecessors(key); - if let [pk] = preds[..] { - next_north_pos = self.node(&pk)?.order? as i32; - let scan = - ScanCtx::of(south_pos, south_ahead, prev_north_pos, next_north_pos); - self.scan(ctx, south, scan); - south_pos = south_ahead; - prev_north_pos = next_north_pos; - } - } - - let scan = ScanCtx::of(south_pos, south_len, next_north_pos, north_len as i32); - self.scan(ctx, south, scan); - } - - north_len = south_len; - } - - None - } - - fn scan(&mut self, ctx: &mut Context, south: &[Key], scan_ctx: ScanCtx) -> Option<()> { - let ScanCtx { - south_start, - south_end, - prev_north: prev_north_border, - next_north: next_north_border, - } = scan_ctx; - for sid in &south[south_start..south_end] { - if self.node(sid)?.dummy.is_none() { - continue; - } - - for id in self.predecessors(sid) { - let Some(node) = self.node(&id) else { continue }; - - let order = node.order.unwrap_or(0) as i32; - let has_conflict = order < prev_north_border || order > next_north_border; - if node.dummy.is_some() && has_conflict { - ctx.add_conflict(id,*sid); - } - } - } - - None - } - - fn other_inner_segment(&mut self, key: &Key) -> Option { - match self.node(key)?.dummy { - Some(_) => { - /// ### Functional style - /// ```asm - /// .iter().find(|u| g.node(u).unwrap().dummy.is_some()).copied() - /// ``` - /// However, there's [`unwrap`] and [`copied`] - for k in self.predecessors(key) { - if self.node(&k)?.dummy.is_some() { - return Some(k); - } - } - - None - } - None => None, - } - } -} - -impl Context { - fn add_conflict(&mut self, source: Key, target: Key) { - let (s, t) = normalize_st(source, target); - - self.conflicts.entry(s).or_default().push(t); - } - - pub(super) fn has_conflict(&self, source: Key, target: Key) -> bool { - let (s, t) = normalize_st(source, target); - - match self.conflicts.get(&s) { - Some(set) => set.contains(&t), - _ => false, - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs deleted file mode 100644 index 1e01243b016bc801d64f1ff89515ea638d7803b7..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/context.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{ - ops, - ops::{Index, IndexMut}, -}; - -use ahash::HashMap; - -use self::{Direction::*, Horizontal::*, Vertical::*}; -use crate::Key; - -#[derive(Copy, Clone, PartialEq)] -pub(super) enum Horizontal { - Left, - Right, -} - -#[derive(Copy, Clone, PartialEq)] -pub(super) enum Vertical { - Top, - Bottom, -} - -#[derive(Copy, Clone, Hash, Eq, PartialEq)] -pub(super) enum Direction { - TopLeft, - TopRight, - BottomLeft, - BottomRight, -} - -impl From for Direction { - fn from(value: usize) -> Self { - match value { - 0 => TopLeft, - 1 => TopRight, - 2 => BottomLeft, - 3 => BottomRight, - _ => unreachable!(), - } - } -} - -impl ops::Add for Vertical { - type Output = Direction; - - fn add(self, rhs: Horizontal) -> Self::Output { - match (self, rhs) { - (Top, Left) => TopLeft, - (Top, Right) => TopRight, - (Bottom, Left) => BottomLeft, - (Bottom, Right) => BottomRight, - } - } -} - -impl Direction { - pub(super) fn horizon(self) -> Horizontal { - match self { - TopLeft | TopRight => Left, - BottomLeft | BottomRight => Right, - } - } -} - -type DirectionMap = [HashMap; 4]; - -impl Index for DirectionMap { - type Output = HashMap; - - fn index(&self, index: Direction) -> &Self::Output { - match index { - TopLeft => &self[0], - TopRight => &self[1], - BottomLeft => &self[2], - BottomRight => &self[3], - } - } -} - -impl IndexMut for DirectionMap { - fn index_mut(&mut self, index: Direction) -> &mut Self::Output { - match index { - TopLeft => &mut self[0], - TopRight => &mut self[1], - BottomLeft => &mut self[2], - BottomRight => &mut self[3], - } - } -} - -#[derive(Default)] -pub(super) struct Context { - pub conflicts: HashMap>, - pub direction_map: DirectionMap, - pub root: HashMap, - pub align: HashMap, - pub balanced: HashMap -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs deleted file mode 100644 index bab053a421484e00f79e3a7361d30c94b23292d9..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod align; -mod block_graph; -mod conflict; -mod context; -mod xy; - -use block_graph::*; -use context::*; - -use crate::Graph; - -impl Graph { - pub(super) fn position(&mut self) -> Option<()> { - let mut ncg: Graph = self.as_non_compound(); - - let mut ctx = Context::default(); - ncg.position_y(); - ncg.position_x(&mut ctx); - - for (key, &x) in &ctx.balanced { - let node = self.node_mut(key)?; - node.x = x; - node.y = ncg.nodes[key].y; - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs deleted file mode 100644 index edda8fc6dd8db562fc1e9797bcfaed5d8f4f5fbd..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/position/xy.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::HashMap; - -use super::{Context, Direction, Direction::*, Horizontal::*, Vertical::*}; -use crate::{Graph, Key}; - -trait ExtentExt { - fn extent(&self) -> (f32, f32); -} - -impl ExtentExt for HashMap { - fn extent(&self) -> (f32, f32) { - let iter = self.values().copied(); - - let (mut min, mut max) = (f32::INFINITY, f32::NEG_INFINITY); - - for value in iter { - min = min.min(value); - max = max.max(value); - } - - (min, max) - } -} - -impl Context { - fn balance(&mut self) -> Option<()> { - self.balanced = self.direction_map[TopLeft].clone(); - let keys: Vec = self.balanced.keys().copied().collect(); - - for key in &keys { - let mut vals: Vec = - self.direction_map.iter().map(|dirs| dirs.get(key).copied().unwrap()).collect(); - vals.sort_by_key(|f| f.to_bits()); - let x1 = vals[1]; - let x2 = vals[2]; - let mid = self.balanced.get_mut(key)?; - *mid = (x1 + x2) / 2.0; - } - - None - } - - fn min_alignment(&self, graph: &Graph) -> Option<(Direction, f32, f32)> { - let (idx, align) = self.direction_map.iter().enumerate().min_by_key(|(_, keys)| { - let mut max = f32::NEG_INFINITY; - let mut min = f32::INFINITY; - - for (key, x) in keys.iter() { - let half_width = graph.nodes[key].width / 2.0; - max = max.max(x + half_width); - min = min.min(x - half_width); - } - - (max - min).to_bits() - })?; - - let (min, max) = align.extent(); - - Some((Direction::from(idx), min, max)) - } - - fn align_coordinates(&mut self, graph: &Graph) -> Option<()> { - let (min_direction, min, max) = self.min_alignment(graph)?; - - for direction in [TopLeft, TopRight, BottomLeft, BottomRight] { - if direction != min_direction { - let vals = &mut self.direction_map[direction]; - let (vals_min, vals_max) = vals.extent(); - - let delta = match direction.horizon() { - Left => min - vals_min, - Right => max - vals_max, - }; - - if delta != 0.0 { - for x in vals.values_mut() { - *x += delta; - } - } - } - } - - None - } -} - -impl Graph { - pub(super) fn position_x(&mut self, ctx: &mut Context) -> Option<()> { - let matrix = self.key_matrix(); - - self.find_conflict(ctx, &matrix); - let mut matrix = matrix; - - for vertical in [Top, Bottom] { - if vertical == Bottom { - matrix = self.key_matrix(); - matrix.reverse() - } - - for horizontal in [Left, Right] { - if horizontal == Right { - matrix.iter_mut().for_each(|inner| inner.reverse()); - } - - self.vertical_alignment(ctx, &matrix, vertical); - let mut compact = self.horizontal_compaction(&matrix, ctx); - - if horizontal == Right { - compact.values_mut().for_each(|x| *x = -*x); - } - - ctx.direction_map[vertical + horizontal] = compact; - } - } - - ctx.align_coordinates(self); - ctx.balance() - } - - pub(super) fn position_y(&mut self) -> Option<()> { - let matrix = self.key_matrix(); - let rank_sep = self.config.ranksep; - let mut y = 0.0; - for keys in matrix { - let mut max_height = f32::NEG_INFINITY; - - for key in &keys { - max_height = max_height.max(self.node(key)?.height) - } - - for key in &keys { - let node = self.node_mut(key)?; - node.y = y + max_height / 2.0; - } - - y += max_height + rank_sep; - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs deleted file mode 100644 index 78921409fc97caf7ac914f82443a33001e7adb16..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/feasible_tree.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::{Edge, Graph, GraphEdge, GraphNode, Key, EMPTY_KEY}; - -impl Graph { - pub(super) fn feasible_tree(&mut self) -> Graph { - let mut t: Graph = Graph::new(false, false); - - let start = self.nodes().first().copied().unwrap_or(EMPTY_KEY); - let size = self.nodes.len(); - t.set_node(start, Some(GraphNode::default())); - - while tight_tree(&mut t, self) < size { - if let Some(edge) = find_min_stack_edge(&t, self) { - let delta = - if t.has_node(&edge.source) { self.slack(edge) } else { -self.slack(edge) }; - shift_ranks(&t, self, delta); - } - } - - t - } -} - -fn tight_tree(t: &mut Graph, g: &Graph) -> usize { - let mut stack: Vec = t.nodes(); - - while let Some(curr) = stack.pop() { - for edge in g.node_edges(&curr) { - let source = edge.source; - let key = if curr == source { edge.target } else { edge.source }; - if !t.has_node(&key) && g.slack(edge) == 0 { - t.set_node(key, Some(GraphNode::default())); - t.set_edge_undirected(curr, key, Some(GraphEdge::default())); - stack.push(key); - } - } - } - - t.nodes.len() -} - -fn find_min_stack_edge(t: &Graph, g: &Graph) -> Option { - g.edges - .values() - .filter_map(|&e| (t.has_node(&e.source) != t.has_node(&e.target)).then(|| (e, g.slack(e)))) - .min_by_key(|(_, slack)| *slack) - .map(|(e, _)| e) -} - -fn shift_ranks(t: &Graph, g: &mut Graph, delta: i32) { - for node_id in t.nodes.keys() { - if let Some(node) = g.node_mut(node_id) { - node.rank = Some(node.rank.unwrap_or(0).wrapping_add(delta)); - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs deleted file mode 100644 index a5a13334e8ea95902607e01cf38de4afd9311261..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/longest_path.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashSet, HashSetExt}; - -use self::Variant::*; -use crate::{Graph, Key}; - -enum Variant { - Array(Vec), - Single(Key), -} - -impl Graph { - pub(super) fn longest_path(&mut self) { - let mut visited = HashSet::new(); - let init = self.in_map.iter().filter(|(_, vec)| vec.is_empty()).map(|(&k, _)| k).collect(); - let mut stack = vec![Array(init)]; - - while stack.len() > 0 { - let curr = stack.last_mut().unwrap(); - - match curr { - Array(arr) => { - let k = arr.pop().unwrap(); - if arr.is_empty() { - stack.pop(); - } - - if !visited.contains(&k) { - visited.insert(k); - let children: Vec = - self.out_edges(&k).iter().map(|e| e.target).rev().collect(); - if children.len() > 0 { - stack.push(Single(k)); - stack.push(Array(children)) - } else { - self.node_mut(&k).unwrap().rank = Some(0); - } - } - } - Single(k) => { - let k = k.clone(); - stack.pop(); - let mut rank = i32::MAX; - - for &edge in &self.out_map[&k] { - let minlen = self.edge1(edge).unwrap().minlen.unwrap(); - let target_rank = self.nodes[&edge.target].rank.unwrap(); - - rank = rank.min(target_rank - minlen); - } - - self.node_mut(&k).unwrap().rank = Some(rank); - } - } - } - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs deleted file mode 100644 index f91afa087ecdf9128d6837e46c72f48d4b5864b3..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod feasible_tree; -mod longest_path; -mod network_simplex; -mod slack; - -use crate::{Graph, Ranker::*}; - -impl Graph { - #[inline] - pub(super) fn rank(&mut self) { - match self.config.ranker { - NetworkSimplex => self.network_simplex(), - TightTree => self.tight_tree(), - LongestPath => self.longest_path(), - } - } - - #[inline] - fn tight_tree(&mut self) { - self.longest_path(); - self.feasible_tree(); - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs deleted file mode 100644 index 384528283503c183c9ee74c4638b21d529b381dc..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/network_simplex.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::mem; - -use ahash::{HashSet, HashSetExt}; - -use crate::{Edge, Graph, GraphEdge, GraphNode, Key, EMPTY_KEY}; - -impl Graph { - pub(super) fn network_simplex(&mut self) { - self.simplify_ref(); - self.longest_path(); - - let mut t: Graph = self.feasible_tree(); - init_low_lim_values(&mut t); - init_cut_values(&mut t, self); - - while let Some(e) = leave_edge(&t) { - if let Some(f) = enter_edge(&t, &self, e) { - exchange_edges(&mut t, self, e, f); - } - } - } - - fn simplify_ref(&mut self) { - for edge in self.edge_values.values_mut() { - edge.weight.get_or_insert(0.0); - edge.minlen.get_or_insert(1); - } - } -} - -fn init_cut_values(t: &mut Graph, g: &mut Graph) { - let keys = g.postorder(&g.nodes()); - for &key in keys.iter().skip(1) { - assign_cut_value(t, g, key); - } -} - -fn assign_cut_value(t: &mut Graph, g: &mut Graph, child: Key) { - let cutvalue = calc_cut_value(t, g, child); - if let Some(node) = t.node_mut(&child) { - let parent = node.parent.unwrap_or(EMPTY_KEY); - if let Some(edge) = t.edge_mut(child, parent) { - edge.cutvalue = Some(cutvalue); - } - } -} - -fn calc_cut_value(t: &mut Graph, g: &mut Graph, child: Key) -> f32 { - let Some(node) = t.node_mut(&child) else { return 0.0 }; - - let parent = node.parent.unwrap_or(EMPTY_KEY); - let mut child_is_tail = true; - let mut graph_edge = g.edge_mut(child, parent); - - if graph_edge.is_none() { - child_is_tail = false; - graph_edge = g.edge_mut(parent, child); - } - - let mut cut_value = graph_edge.and_then(|e| e.weight).unwrap_or(0.0); - - for edge in g.node_edges(&child) { - let is_out_edge = edge.source == child; - let other = if is_out_edge { edge.target } else { edge.source }; - - if other == parent { - continue; - } - - let points_to_head = is_out_edge == child_is_tail; - let other_weight = g.edge1(edge).and_then(|e| e.weight).unwrap_or(0.0); - - cut_value += if points_to_head { other_weight } else { -other_weight }; - - if is_tree_edge(t, child, other) { - let out_cut_value = t.edge(child, other).and_then(|e| e.cutvalue).unwrap_or(0.0); - cut_value += if points_to_head { -out_cut_value } else { out_cut_value } - } - } - - cut_value -} - -fn init_low_lim_values(tree: &mut Graph) { - let root = tree.nodes().first().copied().unwrap_or(EMPTY_KEY); - let mut visited: HashSet = HashSet::new(); - assign_low_lim(tree, &mut visited, 1, root); -} - -fn assign_low_lim( - tree: &mut Graph, - visited: &mut HashSet, - mut next_lim: usize, - start_key: Key, -) -> usize { - let mut stack: Vec<(Key, usize, Option)> = vec![(start_key, next_lim, None)]; - - while let Some((k, low, parent)) = stack.pop() { - if !visited.insert(k) { - continue; - } - - let neighbors = tree.neighbors(&k); - let unvisited_neighbors = - neighbors.into_iter().filter(|w| !visited.contains(w)).collect::>(); - - if !unvisited_neighbors.is_empty() { - stack.push((k, low, parent)); - - for t in unvisited_neighbors { - stack.push((t, next_lim, Some(k))); - } - } else { - if let Some(node) = tree.node_mut(&k) { - node.low = Some(low); - node.lim = Some(next_lim); - next_lim += 1; - - node.parent = parent; - } - } - } - - next_lim -} - -fn leave_edge(tree: &Graph) -> Option { - tree.edges - .values() - .find(|&&edge_obj| tree.edge1(edge_obj).map(|e| e.cutvalue) < Some(Some(0.0))) - .copied() -} - -fn enter_edge(t: &Graph, g: &Graph, edge: Edge) -> Option { - let mut source = edge.source; - let mut target = edge.target; - - if !g.has_edge(source, target) { - mem::swap(&mut source, &mut target); - } - - let source_node = t.node(&source); - let target_node = t.node(&target); - let mut tail_node = source_node; - let mut flip = false; - - if source_node?.lim > target_node?.lim { - tail_node = target_node; - flip = true; - } - - g.edges - .values() - .filter(|edge_obj| { - let v_node = t.node(&edge_obj.source); - let w_node = t.node(&edge_obj.target); - flip == is_descendant(v_node, tail_node) && flip != is_descendant(w_node, tail_node) - }) - .min_by_key(|&&e| g.slack(e)) - .copied() -} - -fn exchange_edges(t: &mut Graph, g: &mut Graph, e: Edge, f: Edge) { - t.remove_edge(e.source, e.target); - t.set_edge(f.source, f.target, Some(GraphEdge::default())); - init_low_lim_values(t); - init_cut_values(t, g); - update_ranks(t, g); -} - -fn update_ranks(t: &mut Graph, g: &mut Graph) { - let root = t - .nodes - .keys() - .find(|k| !g.node(k).map_or(true, |n| n.parent.is_none())) - .copied() - .unwrap_or(EMPTY_KEY); - let keys = t.preorder(&vec![root]); - for &k in keys.iter().skip(1) { - let parent = t.node(&k).and_then(|n| n.parent).unwrap_or(EMPTY_KEY); - let mut edge = g.edge(k, parent); - let mut flipped = false; - if edge.is_none() { - edge = g.edge(parent, k); - flipped = true; - } - - let mut minlen = edge.and_then(|e| e.minlen).unwrap_or(0); - if !flipped { - minlen = -minlen - } - - let parent_rank = g.node(&parent).and_then(|n| n.rank).unwrap_or(0); - if let Some(node) = g.node_mut(&k) { - node.rank = Some(parent_rank + (minlen)); - } - } -} - -#[inline] -fn is_tree_edge(tree: &Graph, source: Key, target: Key) -> bool { - tree.has_edge(source, target) -} - -fn is_descendant(node: Option<&GraphNode>, root_node: Option<&GraphNode>) -> bool { - let root_node = root_node.unwrap(); - let lim = node.unwrap().lim; - root_node.low <= lim && lim <= root_node.lim -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs deleted file mode 100644 index c89790255807b4983d7bd68b020b8a56851b0cb8..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/rank/slack.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::{Edge, Graph}; - -/// The slack is defined as the -/// difference between the length of the edge and its minlen. -impl Graph { - pub(super) fn slack(&self, edge: Edge) -> i32 { - let source_rank = self.nodes[&edge.source].rank.unwrap(); - let target_rank = self.nodes[&edge.target].rank.unwrap(); - let minlen = self.edge_values[&edge.to_key()].minlen.unwrap(); - target_rank - source_rank - minlen - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs deleted file mode 100644 index 280b5e1a0501a7112d93178032939227e647d080..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/selfedge.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use smallvec::smallvec; - -use crate::{Dummy::SelfEdge, Graph, GraphNode, Point}; - -/// Self-edge Processing Module -/// -/// ## Overview -/// This module implements a three-phase process to layout -/// self-edges properly in hierarchical graph diagrams. -/// Self-edge is edge where source == target -/// -/// The implementation handles: -/// - Temporary removal of original edges -/// - Insertion of layout markers -/// - Final path calculation and cleanup -/// -/// ## Operation Phases -/// 1. **Edge Removal** (`remove_self_edges`): -/// - Detects and removes self-edges from the main graph structure -/// - Stores original edges in a temporary map for later processing -/// -/// 2. **Marker Insertion** (`insert_self_edges`): -/// - Creates dummy nodes in the layout matrix to reserve space -/// - Maintains proper node ordering through order shifting -/// - Preserves edge metadata for final rendering -/// -/// 3. **Path Positioning** (`position_self_edges`): -/// - Calculates smooth Bézier curve control points -/// - Creates an elliptical path around source node -/// - Removes temporary dummy nodes after path generation -/// -/// ## Key Characteristics -/// - Maintains layout integrity through temporary dummy nodes -/// - Generates consistent elliptical paths for visual clarity -/// - Preserves original-edge data while modifying visual representation -impl Graph { - pub(super) fn remove_self_edges(&mut self) { - for edge in self.edges() { - if edge.source == edge.target { - self.selfedge_map.entry(edge.source).or_default().push(edge); - self.remove_edge1(edge); - } - } - } - - pub(super) fn insert_self_edges(&mut self) -> Option<()> { - let matrix = self.key_matrix(); - for layer in matrix { - let mut order_shift = 0; - for (i, id) in layer.iter().enumerate() { - let node = self.node_mut(id)?; - node.order = Some(i + order_shift); - let rank = node.rank; - - let self_edges = self.selfedge_map.get(id)?.clone(); - for edge in self_edges { - order_shift += 1; - let graph_node = GraphNode { - rank, - order: Some(i + order_shift), - edge: Some(edge), - ..GraphNode::default() - }; - self.add_dummy_node(SelfEdge, graph_node); - } - } - } - - None - } - - pub(super) fn position_self_edges(&mut self) -> Option<()> { - for key in &mut self.nodes() { - let node = &mut self.node(&key)?; - if node.dummy == Some(SelfEdge) { - let self_node = &mut self.node(&node.edge?.source)?; - let x = self_node.x + self_node.width / 2.0; - let y = self_node.y; - let dx = node.x - x; - let dy = self_node.height / 2.0; - let graph_edge = &mut self.edge_mut1(node.edge?)?; - graph_edge.points = Some(smallvec![ - Point::of(x + 2.0 * dx / 3.0, y - dy), - Point::of(x + 2.0 * dx / 3.0, y - dy), - Point::of(x + 5.0 * dx / 6.0, y - dy), - Point::of(x + dx, y), - Point::of(x + 5.0 * dx / 6.0, y + dy), - Point::of(x + 2.0 * dx / 3.0, y + dy), - ]); - self.remove_node(&key); - } - } - - None - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs deleted file mode 100644 index 465fd7e9581424fc01ddf52ace4b93e702ffefaf..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/layout/src/utils.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) 2025, Huawei Technologies Co., Ltd. -// All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ahash::{HashMap, HashMapExt}; -use smallvec::smallvec; - -use crate::{Dummy, Dummy::EdgeProxy, Graph, GraphNode, Key, Point}; - -static mut KEY_COUNTER: Key = 2; - -#[inline] -pub(crate) fn unique_key() -> Key { - unsafe { - KEY_COUNTER += 1; - KEY_COUNTER - } -} - -impl Graph { - fn max_rank(&self) -> usize { - self.nodes.values().map(|n| n.rank.unwrap()).max().unwrap_or(0) as usize - } - - pub(super) fn add_dummy_node(&mut self, dummy: Dummy, mut node: GraphNode) -> Key { - let mut node_id = unique_key(); - while self.has_node(&node_id) { - node_id = unique_key(); - } - - node.dummy = Some(dummy); - self.set_node(node_id, Some(node)); - node_id - } - - pub(super) fn as_non_compound(&mut self) -> Graph { - let mut simplified: Graph = Graph::new(true, false); - simplified.config = self.config; - - self.transfer_node_edges(&mut simplified); - - simplified - } - - pub(super) fn transfer_node_edges(&mut self, dst: &mut Graph) { - for (&node_id, &node) in &self.nodes { - if self.children(&node_id).is_empty() { - dst.set_node(node_id, Some(node)); - } - } - - for &edge in self.edges.values() { - dst.set_edge1(edge, self.edge1(edge).cloned()); - } - } - - /// Adjusts rank for all nodes, then all node sources have - /// **rank(source) >= 0** - /// and at least one node target has **rank(target) = 0**. - pub(super) fn normalize_ranks(&mut self) { - let min = self.nodes.values().map(|n| n.rank.unwrap_or(0)).min().unwrap_or(0); - - for node in &mut self.nodes.values_mut() { - if let Some(rank) = node.rank { - node.rank = Some(rank - min) - } - } - } - - pub(super) fn remove_empty_ranks(&mut self) { - /// Ranks may not start at 0, so we need to offset them - let offset = self.nodes.values().map(|n| n.rank.unwrap_or(0)).min().unwrap_or(0); - - let mut layers: HashMap> = HashMap::new(); - for (&node_id, node) in &self.nodes { - let rank = node.rank.unwrap_or(0).wrapping_sub(offset); - layers.entry(rank).or_default().push(node_id) - } - - let mut delta = 0; - let node_rank_factor = self.config.node_rank_factor as i32; - for (rank, keys) in &layers { - if keys.is_empty() && rank % node_rank_factor != 0 { - delta -= 1; - } else if delta != 0 { - for k in keys { - self.node_mut(k).map(|n| n.rank = Some(n.rank.unwrap_or(0) + delta)); - } - } - } - } - - /// Given a DAG with each node assigned "rank" and "order" properties, this - /// function produces a matrix with the keys of each node. - pub(super) fn key_matrix(&self) -> Vec> { - let mut matrix: Vec> = vec![Vec::new(); self.max_rank() + 1]; - - for (&key, node) in &self.nodes { - if let Some(rank) = node.rank { - matrix[rank as usize].push((node.order.unwrap(), key)); - } - } - - matrix - .iter_mut() - .map(|layer| { - layer.sort_by_key(|&(order, _)| order); - layer.iter().map(|&(_, key)| key).collect() - }) - .collect() - } - - pub(super) fn make_space_for_edge_labels(&mut self) { - let config = &mut self.config; - config.ranksep /= 2.0; - - for edge in self.edge_values.values_mut() { - // In fact, minlen is usually Some(x)! - if let Some(minlen) = &mut edge.minlen { - *minlen *= 2; - } - } - } - - pub(super) fn assign_rank_min_max(&mut self) -> Option<()> { - for key in self.nodes().iter() { - let node = self.node(key)?; - if let (Some(border_top), Some(border_bottom)) = (&node.border_top, &node.border_bottom) - { - let min_rank = self.node(border_top).and_then(|n| n.rank).unwrap_or(0); - let max_rank = self.node(border_bottom).and_then(|n| n.rank).unwrap_or(0); - - let node = self.node_mut(key)?; - node.min_rank = Some(min_rank); - node.max_rank = Some(max_rank); - } - } - - None - } - - pub(super) fn translate_graph(&mut self) { - let mut min_x = f64::INFINITY as f32; - let mut max_x: f32 = 0.0; - let mut min_y = f64::INFINITY as f32; - let mut max_y: f32 = 0.0; - - for GraphNode { x, y, width, height, .. } in self.nodes.values() { - min_x = min_x.min(x - width / 2.0); - max_x = max_x.max(x + width / 2.0); - min_y = min_y.min(y - height / 2.0); - max_y = max_y.max(y + height / 2.0); - } - - for node in self.nodes.values_mut() { - node.x -= min_x; - node.y -= min_y; - } - - for edge in self.edge_values.values_mut() { - if let Some(points) = &mut edge.points { - for point in points { - point.x -= min_x; - point.y -= min_y; - } - } - } - - self.width = max_x - min_x; - self.height = max_y - min_y; - } - - pub(super) fn assign_node_intersects(&mut self) -> Option<()> { - for e in self.edges() { - let ((source_p, source_bbox), (target_p, target_bbox)) = { - let source_node = self.node(&e.source)?; - let target_node = self.node(&e.target)?; - (source_node.coord_bbox(), target_node.coord_bbox()) - }; - - let edge = self.edge_mut1(e)?; - - if let Some(points) = &mut edge.points { - let p1 = source_bbox.intersect_point(Point::of(points[0].x, points[0].y)); - points.insert(0, p1); - let p2 = target_bbox.intersect_point(Point::of( - points[points.len() - 1].x, - points[points.len() - 1].y, - )); - points.push(p2); - } else { - let p1 = source_bbox.intersect_point(target_p); - let p2 = target_bbox.intersect_point(source_p); - edge.points = Some(smallvec![p1, p2]); - }; - } - - None - } - - pub(super) fn remove_edge_proxies(&mut self) -> Option<()> { - for key in &self.nodes() { - let node = self.node(key)?; - if node.dummy == Some(EdgeProxy) { - let rank = node.rank.unwrap_or(0); - if let Some(graph_edge) = self.edge_mut1(node.edge?) { - graph_edge.rank = Some(rank); - } - self.remove_node(key); - } - } - - None - } - - pub(super) fn reverse_points_for_reversed_edges(&mut self) { - for edge in self.edge_values.values_mut() { - if edge.reversed - && let Some(points) = &mut edge.points - { - points.reverse(); - } - } - } -} - -#[derive(Copy, Clone)] -pub(crate) struct Rect { - pub x: f32, - pub y: f32, - pub width: f32, - pub height: f32, -} - -impl GraphNode { - #[inline] - fn bbox(&self) -> Rect { - Rect { x: self.x, y: self.y, width: self.width, height: self.height } - } - - #[inline] - fn coord_bbox(&self) -> (Point, Rect) { - (Point::of(self.x, self.y), self.bbox()) - } -} - -impl Rect { - /// Finds where a line starting at point {x, y} would intersect a rectangle - /// {x, y, width, height} if it were pointing at the rectangle's center. - pub(crate) fn intersect_point(self, point: Point) -> Point { - let x = self.x; - let y = self.y; - - /// Rectangle intersection algorithm - /// [math.stackoverflow](http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes): - let dx = point.x - x; - let dy = point.y - y; - let w = self.width / 2.0; - let h = self.height / 2.0; - - let (sx, sy) = if (dy.abs() * w) > (dx.abs() * h) { - if dy < 0.0 { (-h * dx / dy, -h) } else { (h * dx / dy, h) } - } else { - if dx < 0.0 { (-w, -w * dy / dx) } else { (w, w * dy / dx) } - }; - - Point::of(x + sx, y + sy) - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/gspan.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/gspan.rs index 710161e9f3f0ecf2bf232cd494b27942b73b0118..54ce00a22d3116c6eb1d8728ec8eb3049e2717d0 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/gspan.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/gspan.rs @@ -1,12 +1,7 @@ /* * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ -use std::{ - collections::BTreeMap - - , - usize, -}; +use std::collections::BTreeMap; use rustc_hash::FxHashSet; use smartstring::alias::String; @@ -41,12 +36,17 @@ impl GSpan { max_pat_max: usize, directed: bool, ) -> GSpan { - GSpan { trans: graphs, min_sup, inner_min_sup, max_pat_min, max_pat_max, directed } + GSpan { + trans: graphs, + min_sup, + inner_min_sup, + max_pat_min, + max_pat_max, + directed, + } } - pub fn run( - &self, - ) -> (usize, MaxDFSCodeGraphResult) { + pub fn run(&self) -> (usize, MaxDFSCodeGraphResult) { // 0. Prepare the Result let mut result = MaxDFSCodeGraphResult::default(); result.set_config( @@ -59,9 +59,9 @@ impl GSpan { // 1. Find single node frequent subgraph, if requested let mut single_vertex_graph_map: BTreeMap< usize, - BTreeMap, usize)>, + BTreeMap, usize)>, > = BTreeMap::new(); - let mut single_vertex_label_frequent_map: BTreeMap = BTreeMap::new(); // 一个 graph 内重复的 vertex 的频繁度只记录一次 + let mut single_vertex_label_frequent_map: BTreeMap = BTreeMap::new(); // 一个 graph 内重复的 vertex 的频繁度只记录一次 if self.max_pat_min <= 1 { self.find_frequent_single_vertex( &mut single_vertex_graph_map, @@ -72,15 +72,9 @@ impl GSpan { // 2. Report the single vertex subgraphs let mut next_gid: usize = 0; - self.print_frequent_single_vertex( - &mut single_vertex_graph_map, - &mut single_vertex_label_frequent_map, - &mut next_gid, - ); - // 3. Subgraphs > Vertices // root: [from_label][e_label][to_label] -> Projected - let mut root: BTreeMap>> = + let mut root: BTreeMap>> = BTreeMap::new(); for g in &self.trans { for from in &g.vertices { @@ -89,33 +83,28 @@ impl GSpan { continue; } for edge in &edges { - let key_1 = from.label.clone(); + let key_1 = from.label; let root_1 = root.entry(key_1).or_default(); let key_2 = edge.e_label.clone(); let root_2 = root_1.entry(key_2).or_default(); - let key_3 = g.find_vertex(&edge.to).unwrap().label.clone(); + let key_3 = g.get_vertex(edge.to).unwrap().label; let root_3 = root_2.entry(key_3).or_insert(Projected::new()); root_3.push(g.id, edge, None); } } } let mut dfs_code = DFSCode::new(); - for (from_label_key, from_label_value) in root.iter() { + for (&from_label_key, from_label_value) in root.iter() { for (e_label_key, e_label_value) in from_label_value.iter() { - for (to_label_key, to_label_value) in e_label_value.iter() { + for (&to_label_key, to_label_value) in e_label_value.iter() { dfs_code.push( 0, 1, - from_label_key.clone(), + from_label_key, e_label_key.clone(), - to_label_key.clone(), - ); - self.sub_mining( - to_label_value, - &mut dfs_code, - &mut next_gid, - &mut result, + to_label_key, ); + self.sub_mining(to_label_value, &mut dfs_code, &mut next_gid, &mut result); dfs_code.pop_with_set_result(to_label_value, &mut result); } } @@ -125,64 +114,29 @@ impl GSpan { fn find_frequent_single_vertex( &self, - single_vertex_graph_map: &mut BTreeMap, usize)>>, - single_vertex_label_frequent_map: &mut BTreeMap, + single_vertex_graph_map: &mut BTreeMap, usize)>>, + single_vertex_label_frequent_map: &mut BTreeMap, ) { for graph in &self.trans { for vertex in &graph.vertices { - let key = &vertex.label; + let key = vertex.label; let d = single_vertex_graph_map.entry(graph.id).or_default(); - if d.get(key).is_none() { + if d.get(&key).is_none() { single_vertex_label_frequent_map - .entry(key.clone()) + .entry(key) .and_modify(|f| *f += 1) .or_insert(1); } - d.entry(key.clone()) + d.entry(key) .and_modify(|v| { - v.0.insert(vertex.name.clone()); + v.0.insert(vertex.name); v.1 += 1; }) - .or_insert(([vertex.name.clone()].iter().cloned().collect(), 1)); + .or_insert(([vertex.name].iter().copied().collect(), 1)); } } } - fn print_frequent_single_vertex( - &self, - single_vertex_graph_map: &BTreeMap, usize)>>, - single_vertex_label_frequent_map: &BTreeMap, - next_gid: &mut usize, - ) { - for (frequent_label, sup) in single_vertex_label_frequent_map.iter() { - // 判断图之间的支持度 - if sup < &self.min_sup { - continue; - } - - let mapped: Vec<(FxHashSet, usize)> = single_vertex_graph_map - .iter() - .map(|entry| { - entry.1.get(frequent_label).unwrap_or(&(FxHashSet::default(), 0)).clone() - }) - .collect(); - - // 计算图内部最小、最大支持度 - let mut min = usize::MAX; - let mut max = usize::MIN; - for (_, v) in mapped.iter() { - min = min.min(*v); - max = max.max(*v); - } - - if max < self.inner_min_sup { - continue; - } - - *next_gid += 1; - } - } - fn sub_mining( &self, projected: &Projected, @@ -199,11 +153,11 @@ impl GSpan { * mingt be its (n+1)-extension-graphs, hence we enumerate them all. */ let min_rm_path = dfs_code.build_rm_path(); - let min_label = dfs_code.get_dfs(0).from_label.clone(); - let max_to_code = dfs_code.get_dfs(*min_rm_path.get(0).unwrap()).to.clone(); + let min_label = dfs_code.get_dfs(0).from_label; + let max_to_code = dfs_code.get_dfs(*min_rm_path[0]).to; let (new_fwd_root, new_bck_root) = - self.generate_next_root(projected, dfs_code, &min_rm_path, &min_label, max_to_code); + self.generate_next_root(projected, dfs_code, &min_rm_path, min_label, max_to_code); // Test all extended substructures.. // .. backward @@ -212,9 +166,9 @@ impl GSpan { dfs_code.push( max_to_code, *to_key, - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, e_label_key.clone(), - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, ); self.sub_mining(e_label_value, dfs_code, next_gid, result); dfs_code.pop_with_set_result(e_label_value, result); @@ -223,13 +177,13 @@ impl GSpan { // .. forward for (from_key, from_value) in new_fwd_root.iter().rev() { for (e_label_key, e_label_value) in from_value.iter() { - for (to_label_key, to_label_value) in e_label_value.iter() { + for (&to_label_key, to_label_value) in e_label_value.iter() { dfs_code.push( *from_key, max_to_code + 1, - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, e_label_key.clone(), - to_label_key.clone(), + to_label_key, ); self.sub_mining(to_label_value, dfs_code, next_gid, result); dfs_code.pop_with_set_result(to_label_value, result); @@ -243,14 +197,14 @@ impl GSpan { projected: &'a Projected<'a>, dfs_code: &DFSCode, min_rm_path: &Vec, - min_label: &str, + min_label: usize, max_to_code: usize, ) -> ( - BTreeMap>>>, + BTreeMap>>>, BTreeMap>>, ) { // [from][e_label][to_label] -> Projected - let mut new_fwd_root: BTreeMap>> = + let mut new_fwd_root: BTreeMap>> = BTreeMap::new(); // [to][e_label] -> Projected let mut new_bck_root: BTreeMap> = BTreeMap::new(); @@ -264,12 +218,12 @@ impl GSpan { for i in (0..min_rm_path.len()).rev() { let e = get_backward( self.trans.get(gid).unwrap(), - history.histories.get(*min_rm_path.get(i).unwrap()).unwrap(), - history.histories.get(*min_rm_path.get(0).unwrap()).unwrap(), + history.histories.get(min_rm_path[i]).unwrap(), + history.histories.get(min_rm_path[0]).unwrap(), &history, ); if let Some(e) = e { - let key_1 = dfs_code.get_dfs(*min_rm_path.get(i).unwrap()).from; + let key_1 = dfs_code.get_dfs(min_rm_path[i]).from; let root_1 = new_bck_root.entry(key_1).or_default(); let key_2: &String = &e.e_label; let root_2 = root_1.entry(key_2.clone()).or_insert(Projected::new()); @@ -278,28 +232,27 @@ impl GSpan { } // pure forward let edges: Vec<&Edge> = get_forward_pure( - self.trans.get(gid).unwrap(), - history.histories.get(*min_rm_path.get(0).unwrap()).unwrap(), - &min_label, + &self.trans[gid], + history.histories[min_rm_path[0]], + min_label, &history, ); if !edges.is_empty() { for it in &edges { let root_1 = new_fwd_root.entry(max_to_code).or_default(); - let key_2: &String = &it.e_label; + let key_2 = &it.e_label; let root_2 = root_1.entry(key_2.clone()).or_default(); - let key_3: &String = - &self.trans.get(gid).unwrap().find_vertex(&it.to).unwrap().label; - let root_3 = root_2.entry(key_3.clone()).or_insert(Projected::new()); + let key_3 = self.trans[gid].get_vertex(it.to).unwrap().label; + let root_3 = root_2.entry(key_3).or_insert(Projected::new()); root_3.push(gid, it, Some(&a_projected)); } } // backtracked forward for a_rm_path in min_rm_path { let edges: Vec<&Edge> = get_forward_rm_path( - self.trans.get(gid).unwrap(), - history.histories.get(*a_rm_path).unwrap(), - &min_label, + &self.trans[gid], + history.histories[a_rm_path], + min_label, &history, ); if edges.is_empty() { @@ -308,11 +261,10 @@ impl GSpan { for it in &edges { let key_1 = dfs_code.get_dfs(*a_rm_path).from; let root_1 = new_fwd_root.entry(key_1).or_default(); - let key_2: &String = &it.e_label; + let key_2 = &it.e_label; let root_2 = root_1.entry(key_2.clone()).or_default(); - let key_3: &String = - &self.trans.get(gid).unwrap().find_vertex(&it.to).unwrap().label; - let root_3 = root_2.entry(key_3.clone()).or_insert(Projected::new()); + let key_3 = self.trans[gid].get_vertex(it.to).unwrap().label; + let root_3 = root_2.entry(key_3).or_insert(Projected::new()); root_3.push(gid, it, Some(&a_projected)); } } @@ -350,7 +302,7 @@ impl GSpan { if self.max_pat_max >= self.max_pat_min && dfs_code.count_node() > self.max_pat_max { return true; } - + *next_gid += 1; false @@ -371,18 +323,18 @@ impl GSpan { // [from_label][e_label][to_label] -> Projected // BTreeMap 在 Rust 中会自动根据键进行排序 - let mut root: BTreeMap>> = + let mut root: BTreeMap>> = BTreeMap::new(); for from in &graph_is_min.vertices { let edges: Vec<&Edge> = get_forward_edges(&graph_is_min, from); for it in &edges { - let key_1 = &it.from_label; - let root_1 = root.entry(key_1.clone()).or_default(); + let key_1 = it.from_label; + let root_1 = root.entry(key_1).or_default(); let key_2 = &it.e_label; let root_2 = root_1.entry(key_2.clone()).or_default(); - let key_3 = &it.to_label; - let root_3 = root_2.entry(key_3.clone()).or_insert(Projected::new()); + let key_3 = it.to_label; + let root_3 = root_2.entry(key_3).or_insert(Projected::new()); // 创建初始化子图:一个 Edge 就是一个最小子图 root_3.push(graph_is_min.id, it, None); } @@ -398,12 +350,17 @@ impl GSpan { dfs_code_is_min.push( 0, 1, - from_label_map_entry.0.clone(), + *from_label_map_entry.0, e_label_map_entry.0.clone(), - to_label_map_entry.0.clone(), + *to_label_map_entry.0, ); - self.is_min_dfscode(to_label_map_value, dfs_code, &mut dfs_code_is_min, &graph_is_min) + self.is_min_dfscode( + to_label_map_value, + dfs_code, + &mut dfs_code_is_min, + &graph_is_min, + ) } /** @@ -419,7 +376,9 @@ impl GSpan { graph_is_min: &Graph, ) -> bool { let min_rm_path = dfs_code_is_min.build_rm_path(); - let max_to_code = dfs_code_is_min.get_dfs(*min_rm_path.get(0).unwrap()).to.clone(); + let max_to_code = dfs_code_is_min + .get_dfs(min_rm_path[0]) + .to; { // backward 情况下是否最小: [e_label] -> Projected @@ -431,12 +390,15 @@ impl GSpan { dfs_code_is_min.push( max_to_code, new_to, - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, e_label_map_entry.key().clone(), - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, ); let len = dfs_code_is_min.dfs_vec.len(); - if dfs_code.get_dfs(len - 1).ne(dfs_code_is_min.get_dfs(len - 1)) { + if dfs_code + .get_dfs(len - 1) + .ne(dfs_code_is_min.get_dfs(len - 1)) + { return false; } return self.is_min_dfscode( @@ -455,7 +417,7 @@ impl GSpan { dfs_code_is_min, graph_is_min, &min_rm_path, - &max_to_code, + max_to_code, ); if let Some(root) = root { @@ -464,13 +426,16 @@ impl GSpan { dfs_code_is_min.push( new_from, max_to_code + 1, - Vertex::NIL_V_LABEL.into(), + Vertex::NIL_V_LABEL, e_label_map_key.into(), to_label_map_key.into(), ); let len: usize = dfs_code_is_min.dfs_vec.len(); - if dfs_code.get_dfs(len - 1).ne(dfs_code_is_min.get_dfs(len - 1)) { + if dfs_code + .get_dfs(len - 1) + .ne(dfs_code_is_min.get_dfs(len - 1)) + { return false; } return self.is_min_dfscode( @@ -498,7 +463,7 @@ impl GSpan { for i in (1..min_rm_path.len()).rev() { root = self.generate_e_p_map(projected, graph_is_min, &min_rm_path, i); if root.is_some() { - new_to = dfs_code_is_min.get_dfs(*min_rm_path.get(i).unwrap()).from; + new_to = dfs_code_is_min.get_dfs(min_rm_path[i]).from; break; } } @@ -511,34 +476,36 @@ impl GSpan { dfs_code_is_min: &mut DFSCode, graph_is_min: &'a Graph, min_rm_path: &Vec, - max_to_code: &usize, - ) -> (Option>>>, usize) { - let min_label = dfs_code_is_min.get_dfs(0).from_label.clone(); + max_to_code: usize, + ) -> ( + Option>>>, + usize, + ) { + let min_label = dfs_code_is_min.get_dfs(0).from_label; let mut new_from: usize = 0; - let mut root: Option>> = self + let mut root: Option>> = self .generate_e_to_p_map(projected, graph_is_min, |history| { - let last_rm_path_edge = - history.histories.get(*min_rm_path.get(0).unwrap()).unwrap(); - get_forward_pure(graph_is_min, last_rm_path_edge, &min_label, &history) + let last_rm_path_edge = history.histories[min_rm_path[0]]; + get_forward_pure(graph_is_min, last_rm_path_edge, min_label, &history) }); if root.is_some() { - new_from = max_to_code.clone(); + new_from = max_to_code; } else { // min_rm_path 是从大到小的 dfs_vec 索引 for i in 0..min_rm_path.len() { root = self.generate_e_to_p_map(projected, graph_is_min, |history| { - let cur_rm_path_edge = - history.histories.get(*min_rm_path.get(i).unwrap()).unwrap(); - get_forward_rm_path(graph_is_min, cur_rm_path_edge, &min_label, &history) + let cur_rm_path_edge = history.histories[min_rm_path[i]]; + get_forward_rm_path(graph_is_min, cur_rm_path_edge, min_label, &history) }); if root.is_some() { - new_from = dfs_code_is_min.get_dfs(*min_rm_path.get(i).unwrap()).from; + new_from = dfs_code_is_min.get_dfs(min_rm_path[i]).from; break; } } } + (root, new_from) } @@ -559,8 +526,8 @@ impl GSpan { // 获取最尾边重点到当前边起点的反向边 let backward_edge = get_backward( graph_is_min, - history.histories.get(*min_rm_path.get(i).unwrap()).unwrap(), // 路径当前边 - history.histories.get(*min_rm_path.get(0).unwrap()).unwrap(), // 路径最尾边 + history.histories[min_rm_path[i]], // 路径当前边 + history.histories[min_rm_path[0]], // 路径最尾边 &history, ); if let Some(backward_edge) = backward_edge { @@ -578,12 +545,12 @@ impl GSpan { projected: &'a Projected, graph_is_min: &'a Graph, generate_edges: F, - ) -> Option>>> + ) -> Option>>> where F: Fn(&History) -> Vec<&'a Edge>, { // [e_label][to_label] -> Projected - let mut root: BTreeMap> = BTreeMap::new(); + let mut root: BTreeMap> = BTreeMap::new(); for cur in projected.projections.iter() { let history: History = History::build(cur); @@ -592,8 +559,8 @@ impl GSpan { for it in edges { let key_1 = it.e_label.clone(); let root_1 = root.entry(key_1).or_default(); - let key_2 = graph_is_min.vertex_name_label_map.get(&it.to).unwrap(); - let root_2 = root_1.entry(key_2.clone()).or_insert(Projected::new()); + let key_2 = graph_is_min.key_opkey[it.to]; + let root_2 = root_1.entry(key_2).or_insert(Projected::new()); root_2.push(0, it, Some(cur)); } } @@ -603,12 +570,16 @@ impl GSpan { fn get_first_entry_of_e_to_p<'a>( &self, - root: &'a BTreeMap>, - ) -> (&'a str, &'a str, &'a Projected<'a>) { + root: &'a BTreeMap>, + ) -> (&'a str, usize, &'a Projected<'a>) { let e_label_map_entry = root.first_key_value().unwrap(); let e_label_map_value = e_label_map_entry.1; let to_label_map_entry = e_label_map_value.first_key_value().unwrap(); let to_label_map_value = to_label_map_entry.1; - (e_label_map_entry.0, to_label_map_entry.0, to_label_map_value) + ( + e_label_map_entry.0, + *to_label_map_entry.0, + to_label_map_value, + ) } } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/misc.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/misc.rs index c05071b3306d96962a49b5a3be4cb00784cebf45..156a4b052ec8facaf3e32b575f9714d47d60904d 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/misc.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/misc.rs @@ -34,15 +34,21 @@ pub fn inner_support(projected: &Projected) -> (usize, usize) { let mut unify_vertices_list: Vec> = vec![]; for cur in projected.projections.iter() { - let set: FxHashSet = - cur.get_vertex_names().iter().map(|f| format!("{}/{}", &f.0, &f.1).into()).collect(); + let set: FxHashSet = cur + .get_vertex_names() + .iter() + .map(|f| format!("{}/{}", &f.0, &f.1).into()) + .collect(); // 如果存在 if unify_vertices_list.contains(&set) { continue; } unify_vertices_list.push(set); - count_map.entry(cur.gid).and_modify(|v| *v += 1).or_insert(1); + count_map + .entry(cur.gid) + .and_modify(|v| *v += 1) + .or_insert(1); } let mut min = usize::MAX; @@ -59,7 +65,7 @@ pub fn inner_support(projected: &Projected) -> (usize, usize) { pub fn get_forward_edges<'a>(g: &Graph, v: &'a Vertex) -> Vec<&'a Edge> { let mut result: Vec<&Edge> = Vec::with_capacity(8); for edge in &v.edges { - if v.label <= g.vertex_name_label_map.get(&edge.to).unwrap().clone() { + if v.label <= g.key_opkey[edge.to] { result.push(edge); } } @@ -75,38 +81,35 @@ pub fn get_backward<'a, 'b>( e2: &'a Edge, history: &'b History, ) -> Option<&'a Edge> { - if e1 == e2 { + if *e1 == *e2 { return None; } // 遍历从e2的终点出发的所有边 - for edge in &g.find_vertex(&e2.to).unwrap().edges { + for edge in &g.get_vertex(e2.to)?.edges { if history.has_edge(&edge.id) || edge.to != e1.from { continue; } // 找到一个边的终点是e1的起点 if e1.e_label < edge.e_label || (e1.e_label == edge.e_label - && g.vertex_name_label_map.get(&e1.to).unwrap() - <= g.vertex_name_label_map.get(&e2.to).unwrap()) + && g.key_opkey[&e1.to] + <= g.key_opkey[&e2.to]) { return Some(&edge); } } - return None; + None } -// 获取图中最右下节点引出的所有 Forward 边 pub fn get_forward_pure<'a, 'b>( g: &'a Graph, e: &'b Edge, - min_label: &str, + min_label: usize, history: &'b History, ) -> Vec<&'a Edge> { let mut result: Vec<&Edge> = Vec::with_capacity(8); - for edge in &g.find_vertex(&e.to).unwrap().edges { - if min_label > g.vertex_name_label_map.get(&edge.to).unwrap().as_str() - || history.has_vertex(&edge.to) - { + for edge in &g.get_vertex(e.to).unwrap().edges { + if min_label > g.key_opkey[&edge.to] || history.has_vertex(&edge.to) { continue; } result.push(&edge); @@ -118,14 +121,14 @@ pub fn get_forward_pure<'a, 'b>( pub fn get_forward_rm_path<'a, 'b>( g: &'a Graph, e: &'b Edge, - min_label: &str, + min_label: usize, history: &'b History, ) -> Vec<&'a Edge> { let mut result: Vec<&Edge> = Vec::with_capacity(8); - let to_label = g.vertex_name_label_map.get(&e.to).unwrap().as_str(); + let to_label = g.key_opkey[&e.to]; - for edge in &g.find_vertex(&e.from).unwrap().edges { - let to_label_2 = g.vertex_name_label_map.get(&edge.to).unwrap().as_str(); + for edge in &g.get_vertex(e.from).unwrap().edges { + let to_label_2 = g.key_opkey[&edge.to]; if e.to == edge.to || min_label > to_label_2 || history.has_vertex(&edge.to) { continue; } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs.rs index eac379db002b66ccaa4fa7303159081c115591a8..4881f75370d44acaaa1628352fc11901f21cfb0b 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs.rs @@ -11,18 +11,18 @@ use smartstring::alias::String; pub struct DFS { pub from: usize, pub to: usize, - pub from_label: String, + pub from_label: usize, pub e_label: String, - pub to_label: String, + pub to_label: usize, } impl DFS { pub fn from( from: usize, to: usize, - from_label: String, + from_label: usize, e_label: String, - to_label: String, + to_label: usize, ) -> DFS { DFS { from, to, from_label, e_label, to_label } } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs_code.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs_code.rs index 673dbf136b34b6e1f941ce1a2679926e0c5f2b99..26dad76e4706d46c8f96716f6118f00b65ad2420 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs_code.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/dfs_code.rs @@ -1,9 +1,8 @@ /* * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ -use std::collections::HashSet; -use std::fmt::format; use smartstring::alias::String; +use std::collections::HashSet; use super::projected::Projected; use crate::gspan::{ @@ -24,19 +23,23 @@ pub struct DFSCode { impl DFSCode { pub fn new() -> DFSCode { - DFSCode { is_push_result: false, dfs_vec: Vec::with_capacity(32) } + DFSCode { + is_push_result: false, + dfs_vec: Vec::with_capacity(32), + } } pub fn push( &mut self, from: usize, to: usize, - from_label: String, + from_label: usize, e_label: String, - to_label: String, + to_label: usize, ) { self.is_push_result = false; - self.dfs_vec.push(DFS::from(from, to, from_label, e_label, to_label)); + self.dfs_vec + .push(DFS::from(from, to, from_label, e_label, to_label)); } pub fn pop_with_set_result( @@ -46,10 +49,10 @@ impl DFSCode { ) -> Option { if !self.is_push_result { // 记录尽可能远的深度搜索的结果 - + self.is_push_result = result.add_value(self, projected); } - return self.dfs_vec.pop(); + self.dfs_vec.pop() } pub fn get_dfs(&self, index: usize) -> &DFS { @@ -58,19 +61,17 @@ impl DFSCode { pub fn to_graph(&self, graph_id: usize, directed: bool) -> Graph { let mut g = Graph::new(graph_id, directed); - let mut edge_data = Vec::<(String, String, Option)>::with_capacity(8); + let mut edge_data = Vec::<(usize, usize, Option)>::with_capacity(8); for it in &self.dfs_vec { - let from_name = String::from(format!("{}", it.from)); - if it.from_label != "" && !g.vertex_name_label_map.contains_key(&from_name) { - g.insert_vertex(&from_name, &it.from_label); + if it.from_label != usize::MAX && !g.key_opkey.contains_key(&it.from) { + g.insert_vertex(it.from, it.from_label); } - let to_name = String::from(format!("{}", it.to)); - if it.to_label != "" && !g.vertex_name_label_map.contains_key(&to_name) { - g.insert_vertex(&to_name, &it.to_label); + if it.to_label != usize::MAX && !g.key_opkey.contains_key(&it.to) { + g.insert_vertex(it.to, it.to_label); } // build_edge - edge_data.push((from_name, to_name, Some(it.e_label.clone()))); + edge_data.push((it.from, it.to, Some(it.e_label.clone()))); } g.build_edge(edge_data); g @@ -82,7 +83,7 @@ impl DFSCode { let mut rm_path: Vec = Vec::new(); let mut old_from = usize::MAX; for i in (0..self.dfs_vec.len()).rev() { - let dfs = self.dfs_vec.get(i).unwrap(); + let dfs = &self.dfs_vec[i]; if dfs.from < dfs.to && (rm_path.is_empty() || old_from == dfs.to) { rm_path.push(i); old_from = dfs.from; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/edge.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/edge.rs index 5d6d483b16a11e38e1227eabcc16ae278f8123f4..1bdf52ed6e474410a64732c72d7440f954a2474e 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/edge.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/edge.rs @@ -4,13 +4,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use smartstring::alias::String; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Edge { pub id: usize, - pub from: String, - pub to: String, - pub from_label: String, - pub to_label: String, + pub from: usize, + pub to: usize, + pub from_label: usize, + pub to_label: usize, pub e_label: String, } @@ -18,10 +18,10 @@ impl Edge { pub const NIL_E_LABEL: &'static str = ""; pub fn new( - from: String, - to: String, - from_label: String, - to_label: String, + from: usize, + to: usize, + from_label: usize, + to_label: usize, e_label: Option, ) -> Edge { static COUNTER: AtomicUsize = AtomicUsize::new(1); @@ -38,11 +38,3 @@ impl Edge { } } } - -impl PartialEq for Edge { - fn eq(&self, other: &Self) -> bool { - self.from_label == other.from_label - && self.to_label == other.to_label - && self.e_label == other.e_label - } -} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/graph.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/graph.rs index c396933d2aea2be7ec66350eb3090507ab56e265..f82291fdece83877be58d70c5107e2874fdb00a2 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/graph.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/graph.rs @@ -3,13 +3,11 @@ */ use std::collections::HashMap; +use crate::Context; +use crate::gspan::models::{edge::Edge, vertex::Vertex}; +use parser::Model; use smartstring::alias::String; -use crate::{ - gspan::models::{edge::Edge, vertex::Vertex}, - io::{model_graph::ModelGraph, node::Node}, -}; - #[derive(Debug, Clone)] pub struct Graph { pub id: usize, @@ -17,7 +15,7 @@ pub struct Graph { pub edge_size: usize, pub directed: bool, pub vertices: Vec, - pub vertex_name_label_map: HashMap, + pub key_opkey: HashMap, } impl Graph { @@ -28,54 +26,53 @@ impl Graph { edge_size: 0, directed, vertices: Vec::with_capacity(32), - vertex_name_label_map: HashMap::new(), + key_opkey: HashMap::new(), } } - pub fn insert_vertex(&mut self, name: &str, label: &str) { - self.vertex_name_label_map.insert(name.into(), label.into()); - let vertex = Vertex::new(name.into(), Some(label.into())); + pub fn insert_vertex(&mut self, name: usize, label: usize) { + self.key_opkey.insert(name, label); + let vertex = Vertex::new(name, label); self.vertices.push(vertex); } - pub fn build_edge(&mut self, data: Vec<(String, String, Option)>) { + pub fn build_edge(&mut self, data: Vec<(usize, usize, Option)>) { for (from, to, e_label) in data { if let Some(f_vertex) = self.vertices.iter_mut().find(|x| x.name == from) { - if let Some(to_label) = self.vertex_name_label_map.get(&to) { + if let Some(to_label) = self.key_opkey.get(&to) { let edge = Edge::new(from, to, f_vertex.label.clone(), to_label.clone(), e_label); f_vertex.push(edge); self.edge_size += 1; - } else { - println!("Error: build_edge => {} 不存在 to_label.", to); } - } else { - println!("Error: build_edge => {} 不存在 vertex.", from); } } } } impl Graph { - pub fn find_vertex(&self, name: &str) -> Option<&Vertex> { + pub fn get_vertex(&self, name: usize) -> Option<&Vertex> { self.vertices.iter().find(|x| x.name == name) } - fn push_node(&mut self, node: &Node) { - let vertex = Vertex::from(node); - self.vertex_name_label_map.insert(vertex.name.clone(), vertex.label.clone()); + fn get_vertex_mut(&mut self, name: usize) -> Option<&mut Vertex> { + self.vertices.iter_mut().find(|x| x.name == name) + } + + fn add_vertex(&mut self, name: usize, optype: usize) { + let vertex = Vertex::new(name, optype); self.vertices.push(vertex); } - fn build_edges_for_nodes(&mut self, edges: Vec<(String, String)>) { + fn build_edges(&mut self, edges: &Vec<(String, String)>, ctx: &Context) { for (from, to) in edges { - if let Some(f_vertex) = self.vertices.iter_mut().find(|x| x.name == from) { - if let Some(to_label) = self.vertex_name_label_map.get(&to) { + if let Some(f_vertex) = self.get_vertex_mut(ctx.name_key[from.as_str()]) { + if let Some(&to_label) = ctx.name_opkey.get(to.as_str()) { let edge = Edge::new( - from, - to, - f_vertex.label.clone(), - to_label.clone(), + ctx.name_key[from.as_str()], + ctx.name_key[to.as_str()], + f_vertex.label, + to_label, Some(Edge::NIL_E_LABEL.into()), ); f_vertex.push(edge); @@ -87,14 +84,17 @@ impl Graph { } impl Graph { - pub fn from_model_graph(model_graph: ModelGraph, directed: bool) -> Graph { - let node_map = model_graph.nodes; + pub fn from_model_graph(raw: &Model, ctx: &mut Context, directed: bool) -> Graph { let mut graph = Graph::new(0, directed); - graph.name = model_graph.name; - for (_, val) in &node_map { - graph.push_node(val); + graph.name = raw.name.clone(); + for (id, val) in &raw.nodes { + let key = ctx.name_key[id.as_str()]; + let opkey = ctx.op_opkey[val.opType.as_str()]; + graph.key_opkey.insert(key, opkey); + graph.add_vertex(key, opkey); } - graph.build_edges_for_nodes(model_graph.edges); - return graph; + graph.build_edges(&raw.edges, ctx); + + graph } } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/history.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/history.rs index c022dbaebdfdc04ee473947a93c6bb2bd0a18c50..2c577d91246511dcab8577ed8ba36f6e15ee84cf 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/history.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/history.rs @@ -2,7 +2,6 @@ * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ use rustc_hash::FxHashSet; -use smartstring::alias::String; use crate::gspan::models::{edge::Edge, prev_dfs::PrevDFS}; @@ -14,7 +13,7 @@ use crate::gspan::models::{edge::Edge, prev_dfs::PrevDFS}; pub struct History<'a> { pub histories: Vec<&'a Edge>, pub edges: FxHashSet, - pub vertices: FxHashSet, + pub vertices: FxHashSet, } impl<'a> History<'a> { @@ -28,8 +27,8 @@ impl<'a> History<'a> { loop { history.histories.push(e.edge); history.edges.insert(e.edge.id); - history.vertices.insert(e.edge.from.clone()); - history.vertices.insert(e.edge.to.clone()); + history.vertices.insert(e.edge.from); + history.vertices.insert(e.edge.to); if e.prev.is_none() { break; } @@ -43,7 +42,7 @@ impl<'a> History<'a> { self.edges.contains(&id) } - pub fn has_vertex(&self, name: &str) -> bool { + pub fn has_vertex(&self, name: &usize) -> bool { self.vertices.contains(name) } } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/prev_dfs.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/prev_dfs.rs index 3f0d1031c644f31db9ae6fb2505f8fe3a1e5e263..bec4c64efef7bc287c8c000e709ee91e04ded802 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/prev_dfs.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/prev_dfs.rs @@ -2,7 +2,6 @@ * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ use rustc_hash::FxHashSet; -use smartstring::alias::String; use crate::gspan::models::edge::Edge; @@ -34,14 +33,14 @@ impl<'a> PrevDFS<'a> { } } - pub fn get_vertex_names(&self) -> FxHashSet<(usize, String)> { - let mut names: FxHashSet<(usize, String)> = FxHashSet::default(); + pub fn get_vertex_names(&self) -> FxHashSet<(usize, usize)> { + let mut names: FxHashSet<(usize, usize)> = FxHashSet::default(); let mut cur = self; loop { - names.insert((cur.gid.clone(), cur.edge.from.clone())); - names.insert((cur.gid.clone(), cur.edge.to.clone())); + names.insert((cur.gid.clone(), cur.edge.from)); + names.insert((cur.gid.clone(), cur.edge.to)); if let Some(prev) = &cur.prev { cur = **prev; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/projected.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/projected.rs index 76674aa72c3399cbfa5b56c413be449293edb58b..7a5116a7ca649e68d60ca4660b0452d889f68417 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/projected.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/projected.rs @@ -2,7 +2,6 @@ * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ use rustc_hash::FxHashSet; -use smartstring::alias::String; use crate::gspan::models::{edge::Edge, prev_dfs::PrevDFS}; // PrevDFS 链表节点的集合 @@ -27,7 +26,7 @@ impl<'a> Projected<'a> { self.projections.push(Box::new(new_pdfs)); } - pub fn to_vertex_names_list(&self) -> Vec> { + pub fn to_vertex_names_list(&self) -> Vec> { self.projections.iter().map(|p| p.get_vertex_names()).collect() } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/vertex.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/vertex.rs index e633757ce04ab8f7a9cc889ac4729c10c4a22c47..6b732f7fd5469bbbf00470a9593c4aa1dd07318f 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/vertex.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/models/vertex.rs @@ -1,26 +1,23 @@ /* * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ -use crate::{gspan::models::edge::Edge, io::node::Node}; -use smartstring::alias::String; +use std::marker::PointeeSized; +use crate::gspan::models::edge::Edge; #[derive(Debug, Clone)] pub struct Vertex { - pub name: String, - pub label: String, + pub name: usize, + pub label: usize, pub edges: Vec, } impl Vertex { - pub const NIL_V_LABEL: &str = ""; + pub const NIL_V_LABEL: usize = usize::MAX; - pub fn new(name: String, label: Option) -> Vertex { + pub fn new(name: usize, label: usize) -> Vertex { Vertex { name, - label: match label { - None => String::new(), - Some(label) => label, - }, + label, edges: Vec::with_capacity(8), } } @@ -28,12 +25,10 @@ impl Vertex { pub fn push(&mut self, edge: Edge) { self.edges.push(edge); } - - pub fn from(node: &Node) -> Vertex { - Vertex::new(node.name.clone(), Some(node.opType.clone())) - } } +impl PointeeSized for Vertex {} + impl PartialEq for Vertex { fn eq(&self, other: &Self) -> bool { self.label == other.label diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/result.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/result.rs index d2c33e7d62ea1e242280f7cd9aa661b2113d912c..b02a5dc0264c1c24bcd1a9aa1cdc576d410ac195 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/result.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/gspan/result.rs @@ -30,7 +30,7 @@ pub struct MaxDFSCodeGraphResult { inner_min_sup: usize, max_pat_min: usize, // Minimum number of vertices max_pat_max: usize, // Maximum number of vertices - value: Vec<(DFSCode, Vec>)>, + value: Vec<(DFSCode, Vec>)>, } impl MaxDFSCodeGraphResult { @@ -66,7 +66,6 @@ impl MaxDFSCodeGraphResult { return false; } let item = (dfs_code.clone(), projected.to_vertex_names_list()); - let edges_list = projected.to_edges_list(); self.value.push(item); true @@ -81,7 +80,7 @@ impl MaxDFSCodeGraphResult { if let Some(graph_id) = first.iter().next().map(|(gid, _)| *gid) { let g = v.0.to_graph(graph_id, false); optype_struct = g.vertices.iter() - .map(|v| v.label.clone()).collect::>().join(",").into(); + .map(|v| v.label.clone()).collect::>().join(",").into(); } } let instances = diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/mod.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/mod.rs index 07c96e70a866a16b7a2470d530018d7c28b45d12..7c23ea701416f627df3c256ac8b36e24ce895a3b 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/mod.rs +++ b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/mod.rs @@ -1,6 +1,4 @@ /* * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. */ -pub mod model_graph; -pub mod node; pub mod output; diff --git a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/node.rs b/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/node.rs deleted file mode 100644 index 9cce1cc429f0183bcda2043a36341ff05db6fad5..0000000000000000000000000000000000000000 --- a/plugins/mindstudio-insight-plugins/ModelVis/rust/subgraph/src/io/node.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c), Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. - */ -use serde::{Deserialize, Serialize}; -use smartstring::alias::String; - -#[derive(Serialize, Deserialize, Debug, Clone)] -#[allow(non_snake_case)] -pub struct Node { - pub name: String, - pub opType: String, - pub input: Vec, -}