simulation-alg 0.1.5__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/Cargo.lock +17 -19
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/Cargo.toml +3 -3
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/PKG-INFO +1 -1
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/pyproject.toml +1 -1
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/simulation.pyi +20 -1
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/src/graph/hypergraph.rs +134 -6
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/src/graph/networkx_graph.rs +18 -18
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/.github/workflows/ci.yml +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/.gitignore +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/.gitmodules +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/LICENSE +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/README.md +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/hyper-simulation.log +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/python/__init__.py +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/requirements.txt +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/src/graph/mod.rs +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/src/lib.rs +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/src/utils.rs +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/tests/test_hyper_simulation.py +0 -0
- {simulation_alg-0.1.5 → simulation_alg-0.1.6}/tests/test_simulation.py +0 -0
|
@@ -188,9 +188,9 @@ dependencies = [
|
|
|
188
188
|
|
|
189
189
|
[[package]]
|
|
190
190
|
name = "graph-simulation"
|
|
191
|
-
version = "0.1.
|
|
191
|
+
version = "0.1.20"
|
|
192
192
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
193
|
-
checksum = "
|
|
193
|
+
checksum = "3c2137012fbaf6b3428ea73e32e93e1f7c90c13d6edbfd687f6cc1021b8ab83a"
|
|
194
194
|
dependencies = [
|
|
195
195
|
"bincode",
|
|
196
196
|
"env_logger",
|
|
@@ -297,9 +297,9 @@ dependencies = [
|
|
|
297
297
|
|
|
298
298
|
[[package]]
|
|
299
299
|
name = "once_cell"
|
|
300
|
-
version = "1.
|
|
300
|
+
version = "1.21.3"
|
|
301
301
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
302
|
-
checksum = "
|
|
302
|
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
303
303
|
|
|
304
304
|
[[package]]
|
|
305
305
|
name = "once_cell_polyfill"
|
|
@@ -342,11 +342,10 @@ dependencies = [
|
|
|
342
342
|
|
|
343
343
|
[[package]]
|
|
344
344
|
name = "pyo3"
|
|
345
|
-
version = "0.
|
|
345
|
+
version = "0.27.1"
|
|
346
346
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
347
|
-
checksum = "
|
|
347
|
+
checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf"
|
|
348
348
|
dependencies = [
|
|
349
|
-
"cfg-if",
|
|
350
349
|
"indoc",
|
|
351
350
|
"libc",
|
|
352
351
|
"memoffset",
|
|
@@ -360,19 +359,18 @@ dependencies = [
|
|
|
360
359
|
|
|
361
360
|
[[package]]
|
|
362
361
|
name = "pyo3-build-config"
|
|
363
|
-
version = "0.
|
|
362
|
+
version = "0.27.1"
|
|
364
363
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
365
|
-
checksum = "
|
|
364
|
+
checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb"
|
|
366
365
|
dependencies = [
|
|
367
|
-
"once_cell",
|
|
368
366
|
"target-lexicon",
|
|
369
367
|
]
|
|
370
368
|
|
|
371
369
|
[[package]]
|
|
372
370
|
name = "pyo3-ffi"
|
|
373
|
-
version = "0.
|
|
371
|
+
version = "0.27.1"
|
|
374
372
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
375
|
-
checksum = "
|
|
373
|
+
checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be"
|
|
376
374
|
dependencies = [
|
|
377
375
|
"libc",
|
|
378
376
|
"pyo3-build-config",
|
|
@@ -380,9 +378,9 @@ dependencies = [
|
|
|
380
378
|
|
|
381
379
|
[[package]]
|
|
382
380
|
name = "pyo3-macros"
|
|
383
|
-
version = "0.
|
|
381
|
+
version = "0.27.1"
|
|
384
382
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
385
|
-
checksum = "
|
|
383
|
+
checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71"
|
|
386
384
|
dependencies = [
|
|
387
385
|
"proc-macro2",
|
|
388
386
|
"pyo3-macros-backend",
|
|
@@ -392,9 +390,9 @@ dependencies = [
|
|
|
392
390
|
|
|
393
391
|
[[package]]
|
|
394
392
|
name = "pyo3-macros-backend"
|
|
395
|
-
version = "0.
|
|
393
|
+
version = "0.27.1"
|
|
396
394
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
397
|
-
checksum = "
|
|
395
|
+
checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b"
|
|
398
396
|
dependencies = [
|
|
399
397
|
"heck",
|
|
400
398
|
"proc-macro2",
|
|
@@ -545,7 +543,7 @@ dependencies = [
|
|
|
545
543
|
|
|
546
544
|
[[package]]
|
|
547
545
|
name = "simulation"
|
|
548
|
-
version = "0.1.
|
|
546
|
+
version = "0.1.6"
|
|
549
547
|
dependencies = [
|
|
550
548
|
"graph-base",
|
|
551
549
|
"graph-simulation",
|
|
@@ -566,9 +564,9 @@ dependencies = [
|
|
|
566
564
|
|
|
567
565
|
[[package]]
|
|
568
566
|
name = "target-lexicon"
|
|
569
|
-
version = "0.
|
|
567
|
+
version = "0.13.3"
|
|
570
568
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
571
|
-
checksum = "
|
|
569
|
+
checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
|
|
572
570
|
|
|
573
571
|
[[package]]
|
|
574
572
|
name = "unicode-ident"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "simulation"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.6"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
description = "A Python package for simulation algorithms"
|
|
@@ -13,8 +13,8 @@ name = "simulation"
|
|
|
13
13
|
crate-type = ["cdylib"]
|
|
14
14
|
|
|
15
15
|
[dependencies]
|
|
16
|
-
pyo3 = { version = "0.
|
|
17
|
-
graph-simulation = "0.1.
|
|
16
|
+
pyo3 = { version = "0.27.1", features = ["extension-module"] }
|
|
17
|
+
graph-simulation = "0.1.20"
|
|
18
18
|
graph-base = "0.1.13"
|
|
19
19
|
rayon = "1.10.0"
|
|
20
20
|
# [tool.maturin]
|
|
@@ -48,6 +48,19 @@ class Hyperedge:
|
|
|
48
48
|
def __init__(self, id_set: set[int], desc: str, id: int): ...
|
|
49
49
|
def id_set(self) -> set[int]: ...
|
|
50
50
|
def desc(self) -> str: ...
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DMatch:
|
|
54
|
+
"""
|
|
55
|
+
D-Match for hyper simulation
|
|
56
|
+
"""
|
|
57
|
+
def __init__(self) -> None: ...
|
|
58
|
+
@staticmethod
|
|
59
|
+
def from_dict(d_match_by_sc_id: dict[tuple[int, int], set[tuple[int, int]]]) -> 'DMatch': ...
|
|
60
|
+
|
|
61
|
+
class Delta:
|
|
62
|
+
def __init__(self) -> None: ...
|
|
63
|
+
def add_sematic_cluster_pair(self, u: Node, v: Node, cluster_u: list[Hyperedge], cluster_v: list[Hyperedge]) -> int: ...
|
|
51
64
|
|
|
52
65
|
class Hypergraph:
|
|
53
66
|
"""
|
|
@@ -74,5 +87,11 @@ class Hypergraph:
|
|
|
74
87
|
@staticmethod
|
|
75
88
|
def soft_hyper_simulation(query: 'Hypergraph', data: 'Hypergraph', l_match_fn: Callable[[Hyperedge, Hyperedge], dict[int, set[int]]]) -> dict[int, set[int]]:
|
|
76
89
|
"""
|
|
77
|
-
|
|
90
|
+
Soft hyper simulation.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def get_hyper_simulation(query: 'Hypergraph', data: 'Hypergraph', delta: Delta, d_match: DMatch) -> dict[int, set[int]]:
|
|
95
|
+
"""
|
|
96
|
+
Hyper Simulation
|
|
78
97
|
"""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use std::{collections::{HashMap, HashSet}, fmt::Display, hash::Hash};
|
|
2
2
|
|
|
3
|
-
use pyo3::prelude
|
|
4
|
-
use graph_simulation::algorithm::hyper_simulation::{HyperSimulation, LMatch, LPredicate};
|
|
3
|
+
use pyo3::{prelude::*, types::PyList};
|
|
4
|
+
use graph_simulation::algorithm::hyper_simulation::{DMatch, Delta, HyperSimulation, LMatch, LPredicate, SematicCluster};
|
|
5
5
|
use graph_base::interfaces::{edge, graph::{self, SingleId}, hypergraph::{self, ContainedHyperedge}, typed, vertex};
|
|
6
6
|
// use graph_base::interfaces::hypergraph;
|
|
7
7
|
|
|
@@ -144,6 +144,15 @@ impl Hypergraph {
|
|
|
144
144
|
.map(|(k, v)| (k.id(), v.into_iter().map(|n| n.id()).collect()))
|
|
145
145
|
.collect()
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
#[staticmethod]
|
|
149
|
+
pub fn get_hyper_simulation(query: PyRef<Hypergraph>, data: PyRef<Hypergraph>, delta: PyRef<DeltaPy>, d_match: PyRef<DMatchImpl>) -> HashMap<usize, HashSet<usize>> {
|
|
150
|
+
let delta_inner = DeltaImpl::from(delta.clone(), &*query, &*data);
|
|
151
|
+
let sim = HyperSimulation::get_hyper_simulation_naive(&*query, &*data, &delta_inner, &*d_match);
|
|
152
|
+
sim.into_iter()
|
|
153
|
+
.map(|(k, v)| (k.id(), v.into_iter().map(|n| n.id()).collect()))
|
|
154
|
+
.collect()
|
|
155
|
+
}
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
impl Display for Node {
|
|
@@ -216,8 +225,8 @@ impl<'a> typed::Typed<'a> for Hypergraph {
|
|
|
216
225
|
// You cannot pass Rust trait objects directly to Python.
|
|
217
226
|
// Instead, you need to define what data you want to compare and pass only that.
|
|
218
227
|
// For example, if your Type trait has an id() method, you could do:
|
|
219
|
-
Python::
|
|
220
|
-
type_same_fn.call1(py, (&x.
|
|
228
|
+
Python::attach(|py| {
|
|
229
|
+
type_same_fn.call1(py, (&x.id, &y.id)).map_or(false, |result| {
|
|
221
230
|
result.extract::<bool>(py).unwrap_or(false)
|
|
222
231
|
})
|
|
223
232
|
})
|
|
@@ -229,6 +238,8 @@ impl<'a> typed::Typed<'a> for Hypergraph {
|
|
|
229
238
|
|
|
230
239
|
impl<'a> ContainedHyperedge<'a> for Hypergraph {}
|
|
231
240
|
|
|
241
|
+
|
|
242
|
+
|
|
232
243
|
#[pyclass(name = "LMatch")]
|
|
233
244
|
pub struct LMatchImpl {
|
|
234
245
|
l_match_fn: Option<Py<PyAny>>, // (Hyperedge, Hyperedge) -> dict[int, set[int]]
|
|
@@ -275,7 +286,7 @@ impl LMatch for LMatchImpl {
|
|
|
275
286
|
fn l_match_with_node_mut(&mut self, e: &Self::Edge, e_prime: &Self::Edge, u: usize) -> &HashSet<usize> {
|
|
276
287
|
let l_match = self.l_match_cache.entry((e.id, e_prime.id)).or_insert_with(|| {
|
|
277
288
|
if let Some(l_match_fn) = self.l_match_fn.as_ref() {
|
|
278
|
-
Python::
|
|
289
|
+
Python::attach(|py| {
|
|
279
290
|
l_match_fn.call1(py, (e.clone(), e_prime.clone())).map_or_else(
|
|
280
291
|
|_| HashMap::new(),
|
|
281
292
|
|result| result.extract::<HashMap<usize, HashSet<usize>>>(py).unwrap_or_default(),
|
|
@@ -315,7 +326,7 @@ impl<'a> LPredicate<'a> for Hypergraph {
|
|
|
315
326
|
|
|
316
327
|
fn l_predicate_edge(&'a self, e: &'a Self::Edge, e_prime: &'a Self::Edge) -> bool {
|
|
317
328
|
if let Some(l_predicate_fn) = self.l_predicate_fn.as_ref() {
|
|
318
|
-
Python::
|
|
329
|
+
Python::attach(|py| {
|
|
319
330
|
l_predicate_fn.call1(py, (e.clone(), e_prime.clone())).map_or(false, |result| {
|
|
320
331
|
result.extract::<bool>(py).unwrap_or(false)
|
|
321
332
|
})
|
|
@@ -331,6 +342,123 @@ impl<'a> LPredicate<'a> for Hypergraph {
|
|
|
331
342
|
}
|
|
332
343
|
}
|
|
333
344
|
|
|
345
|
+
#[pyclass(name = "DMatch")]
|
|
346
|
+
pub struct DMatchImpl {
|
|
347
|
+
d_match_cache: HashMap<(usize, usize), HashSet<(usize, usize)>>,
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
impl<'a> DMatch<'a> for DMatchImpl {
|
|
351
|
+
type Edge = Hyperedge;
|
|
352
|
+
|
|
353
|
+
fn d_match(&self, e: &SematicCluster<'a, Self::Edge>, e_prime: &SematicCluster<'a, Self::Edge>) -> &HashSet<(usize, usize)> {
|
|
354
|
+
if let Some(set) = self.d_match_cache.get(&(e.id(), e_prime.id())) {
|
|
355
|
+
return set;
|
|
356
|
+
}
|
|
357
|
+
unreachable!("D-Match not exist at ({}, {})", e.id(), e_prime.id())
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// fn d_match_mut(&mut self, e: &SematicCluster<'a, Self::Edge>, e_prime: &SematicCluster<'a, Self::Edge>) -> &HashSet<(usize, usize)> {
|
|
361
|
+
// todo!()
|
|
362
|
+
// }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
#[pymethods]
|
|
368
|
+
impl DMatchImpl {
|
|
369
|
+
#[new]
|
|
370
|
+
fn new() -> Self {
|
|
371
|
+
DMatchImpl {
|
|
372
|
+
d_match_cache: HashMap::new()
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
#[staticmethod]
|
|
377
|
+
fn from_dict(d_match_by_sc_id: HashMap<(usize, usize), HashSet<(usize, usize)>>) -> Self {
|
|
378
|
+
DMatchImpl {
|
|
379
|
+
d_match_cache: d_match_by_sc_id
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// #[pyclass(name = "Delta")]
|
|
385
|
+
struct DeltaImpl<'a> {
|
|
386
|
+
sematic_cluster: HashMap<(&'a Node, &'a Node), Vec<(SematicCluster<'a, Hyperedge>, SematicCluster<'a, Hyperedge>)>>
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
impl<'a> Delta<'a> for DeltaImpl<'a> {
|
|
390
|
+
type Edge = Hyperedge;
|
|
391
|
+
type Node = Node;
|
|
392
|
+
|
|
393
|
+
fn get_sematic_clusters(&'a self, u: &'a Self::Node, v: &'a Self::Node) -> &'a Vec<(SematicCluster<'a, Self::Edge>, SematicCluster<'a, Self::Edge>)> {
|
|
394
|
+
if let Some(pairs) = self.sematic_cluster.get(&(u, v)) {
|
|
395
|
+
return pairs;
|
|
396
|
+
}
|
|
397
|
+
unreachable!("Have no sematic_clusters between {} to {}", u, v)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
impl<'a> DeltaImpl<'a> {
|
|
402
|
+
fn from(delta: DeltaPy, query: &'a Hypergraph, data: &'a Hypergraph) -> Self {
|
|
403
|
+
let mut pair_map: HashMap<(&Node, &Node), Vec<(SematicCluster<'a, Hyperedge>, SematicCluster<'a, Hyperedge>)>> = HashMap::new();
|
|
404
|
+
for ((u_id, v_id), pairs) in delta.sematic_cluster_cache {
|
|
405
|
+
let u = query.nodes.get(u_id).unwrap();
|
|
406
|
+
let v = data.nodes.get(v_id).unwrap();
|
|
407
|
+
for ((q_edges_ids, q_id), (d_edges_ids, d_id)) in pairs {
|
|
408
|
+
let q_edges: Vec<&Hyperedge> = q_edges_ids.iter().map(|id| {
|
|
409
|
+
query.hyperedges.get(*id).unwrap()
|
|
410
|
+
}).collect();
|
|
411
|
+
let d_edges: Vec<&Hyperedge> = d_edges_ids.iter().map(|id| {
|
|
412
|
+
data.hyperedges.get(*id).unwrap()
|
|
413
|
+
}).collect();
|
|
414
|
+
|
|
415
|
+
let sc_q = SematicCluster::new(q_id, q_edges);
|
|
416
|
+
let sc_d = SematicCluster::new(d_id, d_edges);
|
|
417
|
+
|
|
418
|
+
pair_map.entry((u, v)).or_default().push((sc_q, sc_d));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let res = DeltaImpl {
|
|
422
|
+
sematic_cluster: pair_map
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
return res;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
#[derive(Clone)]
|
|
432
|
+
#[pyclass(name = "Delta")]
|
|
433
|
+
pub struct DeltaPy {
|
|
434
|
+
sematic_cluster_cache: HashMap<(usize, usize), Vec<((Vec<usize>, usize), (Vec<usize>, usize))>>,
|
|
435
|
+
global_cnt: usize
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
#[pymethods]
|
|
439
|
+
impl DeltaPy {
|
|
440
|
+
#[new]
|
|
441
|
+
fn new() -> Self {
|
|
442
|
+
DeltaPy {
|
|
443
|
+
sematic_cluster_cache: HashMap::new(),
|
|
444
|
+
global_cnt: 0
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
fn add_sematic_cluster_pair(&mut self, u: PyRef<Node>, v: PyRef<Node>, cluster_u: Vec<PyRef<Hyperedge>>, cluster_v: Vec<PyRef<Hyperedge>>) -> usize {
|
|
449
|
+
let id = self.global_cnt;
|
|
450
|
+
self.global_cnt += 1;
|
|
451
|
+
let u_ids: Vec<_> = cluster_u.iter().map(|e_ref| {
|
|
452
|
+
e_ref.id
|
|
453
|
+
}).collect();
|
|
454
|
+
let v_ids: Vec<_> = cluster_v.iter().map(|e_ref| {
|
|
455
|
+
e_ref.id
|
|
456
|
+
}).collect();
|
|
457
|
+
self.sematic_cluster_cache.entry((u.id, v.id)).or_default().push(((u_ids, id), (v_ids, id)));
|
|
458
|
+
return id;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
334
462
|
// fn test_sim(g: &Hypergraph, h: &Hypergraph) {
|
|
335
463
|
// let mut l_match = LMatchImpl::new();
|
|
336
464
|
// let sim = HyperSimulation::get_simulation_naive(g, h, &mut l_match);
|
|
@@ -8,10 +8,10 @@ use graph_base::interfaces::graph::{Graph, Directed, Adjacency, AdjacencyInv, Si
|
|
|
8
8
|
use pyo3::prelude::*;
|
|
9
9
|
use std::collections::{HashMap, HashSet};
|
|
10
10
|
use std::hash::{Hash, Hasher};
|
|
11
|
-
use std::path::Display;
|
|
12
|
-
use std::sync::Arc;
|
|
11
|
+
// use std::path::Display;
|
|
12
|
+
// use std::sync::Arc;
|
|
13
13
|
|
|
14
|
-
type SharedRustFn = Arc<dyn Fn(&Attributes, &Attributes) -> bool + Send + Sync>;
|
|
14
|
+
// type SharedRustFn = Arc<dyn Fn(&Attributes, &Attributes) -> bool + Send + Sync>;
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
// 自定义图结构
|
|
@@ -21,7 +21,7 @@ struct Attributes(HashMap<String, Py<PyAny>>);
|
|
|
21
21
|
|
|
22
22
|
impl Clone for Attributes {
|
|
23
23
|
fn clone(&self) -> Self {
|
|
24
|
-
Python::
|
|
24
|
+
Python::attach(|py| {
|
|
25
25
|
let cloned_map = self.0.iter()
|
|
26
26
|
.map(|(k, v)| (k.clone(), v.clone_ref(py)))
|
|
27
27
|
.collect();
|
|
@@ -50,7 +50,7 @@ impl PartialEq for Attributes {
|
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
Python::
|
|
53
|
+
Python::attach(|py| {
|
|
54
54
|
self.0.iter().all(|(key, value)| {
|
|
55
55
|
match other.0.get(key) {
|
|
56
56
|
Some(other_value) => {
|
|
@@ -74,7 +74,7 @@ impl Hash for Attributes {
|
|
|
74
74
|
let mut entries: Vec<_> = self.0.iter().collect();
|
|
75
75
|
entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
|
76
76
|
|
|
77
|
-
Python::
|
|
77
|
+
Python::attach(|py| {
|
|
78
78
|
for (key, value) in entries {
|
|
79
79
|
key.hash(state);
|
|
80
80
|
// 使用 Python 对象的 __hash__ 方法
|
|
@@ -144,7 +144,7 @@ pub struct NetworkXGraph {
|
|
|
144
144
|
|
|
145
145
|
impl Clone for NetworkXGraph {
|
|
146
146
|
fn clone(&self) -> Self {
|
|
147
|
-
Python::
|
|
147
|
+
Python::attach(|py| {
|
|
148
148
|
NetworkXGraph {
|
|
149
149
|
nodes: self.nodes.clone(),
|
|
150
150
|
edges: self.edges.clone(),
|
|
@@ -158,8 +158,8 @@ impl Clone for NetworkXGraph {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
fn convert_to_string(obj: &
|
|
162
|
-
Python::
|
|
161
|
+
fn convert_to_string(obj: &Py<PyAny>) -> PyResult<String> {
|
|
162
|
+
Python::attach(|py| {
|
|
163
163
|
// Try direct conversion first
|
|
164
164
|
obj.call_method0(py, "__str__")?.extract::<String>(py)
|
|
165
165
|
.or_else(|_| {
|
|
@@ -391,7 +391,7 @@ impl<'a> Graph<'a> for NetworkXGraph {
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
fn test_eq(a: &Py<PyAny>, b: &Py<PyAny>) -> bool {
|
|
394
|
-
Python::
|
|
394
|
+
Python::attach(|py| {
|
|
395
395
|
match a.call_method1(py, "__eq__", (b,)) {
|
|
396
396
|
Ok(result) => result.extract::<bool>(py).unwrap_or(false),
|
|
397
397
|
Err(_) => false
|
|
@@ -420,7 +420,7 @@ impl<'a> Labeled<'a> for NetworkXGraph {
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
if let Some(compare_fn) = self.same_label_fn.as_ref() {
|
|
423
|
-
Python::
|
|
423
|
+
Python::attach(|py| {
|
|
424
424
|
let attr1 = node.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
425
425
|
let attr2 = label.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
426
426
|
compare_fn.call1(py,(attr1, attr2)).unwrap().extract::<bool>(py).unwrap()
|
|
@@ -441,7 +441,7 @@ impl<'a> Labeled<'a> for NetworkXGraph {
|
|
|
441
441
|
|
|
442
442
|
fn edge_label_same(&self, edge1: &Self::Edge, edge2: &Self::Edge) -> bool {
|
|
443
443
|
if let Some(compare_fn) = self.same_edge_fn.as_ref() {
|
|
444
|
-
Python::
|
|
444
|
+
Python::attach(|py| {
|
|
445
445
|
let attr1 = edge1.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
446
446
|
let attr2 = edge2.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
447
447
|
compare_fn.call1(py,(attr1, attr2)).unwrap().extract::<bool>(py).unwrap()
|
|
@@ -453,7 +453,7 @@ impl<'a> Labeled<'a> for NetworkXGraph {
|
|
|
453
453
|
|
|
454
454
|
fn edge_node_label_same(&self, src1: &Self::Node, edge1: &Self::Edge, dst1: &Self::Node, src2: &Self::Node, edge2: &Self::Edge, dst2: &Self::Node) -> bool {
|
|
455
455
|
if let Some(compare_fn) = self.same_node_edge_fn.as_ref() {
|
|
456
|
-
Python::
|
|
456
|
+
Python::attach(|py| {
|
|
457
457
|
let src1_attr = src1.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
458
458
|
let dst1_attr = dst1.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
459
459
|
let edge1_attr = edge1.attributes.0.iter().map(|(k, v)| (k.clone(), v.clone_ref(py))).collect::<HashMap<_, _>>();
|
|
@@ -489,7 +489,7 @@ impl std::fmt::Display for NetworkXGraph {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
-
fn to_nx_node(py: Python, node: &Node) -> PyResult<
|
|
492
|
+
fn to_nx_node(py: Python, node: &Node) -> PyResult<Py<PyAny>> {
|
|
493
493
|
let attrs_dict = PyDict::new(py);
|
|
494
494
|
for (k, v) in &node.attributes.0 {
|
|
495
495
|
attrs_dict.set_item(k, v.clone_ref(py))?;
|
|
@@ -501,7 +501,7 @@ fn to_nx_node(py: Python, node: &Node) -> PyResult<PyObject> {
|
|
|
501
501
|
|
|
502
502
|
#[pyfunction]
|
|
503
503
|
#[pyo3(signature = (nx_graph1, nx_graph2, is_label_cached = false))]
|
|
504
|
-
pub fn get_simulation_inter(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'_, PyAny>, is_label_cached: bool) -> PyResult<
|
|
504
|
+
pub fn get_simulation_inter(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'_, PyAny>, is_label_cached: bool) -> PyResult<Py<PyAny>> {
|
|
505
505
|
let mut graph1 = NetworkXGraph::from_networkx(nx_graph1)?;
|
|
506
506
|
let graph2 = NetworkXGraph::from_networkx(nx_graph2)?;
|
|
507
507
|
|
|
@@ -513,7 +513,7 @@ pub fn get_simulation_inter(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'_,
|
|
|
513
513
|
|
|
514
514
|
|
|
515
515
|
// Convert simulation to a list of pairs (i, j) where i is a node in graph1, j is a node in graph2
|
|
516
|
-
Python::
|
|
516
|
+
Python::attach(|py| {
|
|
517
517
|
let map = PyDict::new(py);
|
|
518
518
|
|
|
519
519
|
for (node, set) in sim.iter() {
|
|
@@ -540,7 +540,7 @@ pub fn is_simulation_isomorphic(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<
|
|
|
540
540
|
|
|
541
541
|
#[pyfunction]
|
|
542
542
|
#[pyo3(signature = (nx_graph1, nx_graph2, compare, is_label_cached = false))]
|
|
543
|
-
pub fn get_simulation_inter_fn(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'_, PyAny>, compare: Py<PyAny>, is_label_cached: bool) -> PyResult<
|
|
543
|
+
pub fn get_simulation_inter_fn(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'_, PyAny>, compare: Py<PyAny>, is_label_cached: bool) -> PyResult<Py<PyAny>> {
|
|
544
544
|
let mut graph1 = NetworkXGraph::from_networkx(nx_graph1)?;
|
|
545
545
|
let graph2 = NetworkXGraph::from_networkx(nx_graph2)?;
|
|
546
546
|
|
|
@@ -552,7 +552,7 @@ pub fn get_simulation_inter_fn(nx_graph1: &Bound<'_, PyAny>, nx_graph2: &Bound<'
|
|
|
552
552
|
|
|
553
553
|
let sim = graph1.get_simulation_inter(&graph2);
|
|
554
554
|
|
|
555
|
-
Python::
|
|
555
|
+
Python::attach(|py| {
|
|
556
556
|
let map = PyDict::new(py);
|
|
557
557
|
|
|
558
558
|
for (node, set) in sim.iter() {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|