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.
@@ -188,9 +188,9 @@ dependencies = [
188
188
 
189
189
  [[package]]
190
190
  name = "graph-simulation"
191
- version = "0.1.14"
191
+ version = "0.1.20"
192
192
  source = "registry+https://github.com/rust-lang/crates.io-index"
193
- checksum = "b87bfc6e277b4022afe3108e71a374642b0aaceb42ea3dbeedfdd2aeb5e6ea59"
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.20.2"
300
+ version = "1.21.3"
301
301
  source = "registry+https://github.com/rust-lang/crates.io-index"
302
- checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
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.23.5"
345
+ version = "0.27.1"
346
346
  source = "registry+https://github.com/rust-lang/crates.io-index"
347
- checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
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.23.5"
362
+ version = "0.27.1"
364
363
  source = "registry+https://github.com/rust-lang/crates.io-index"
365
- checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
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.23.5"
371
+ version = "0.27.1"
374
372
  source = "registry+https://github.com/rust-lang/crates.io-index"
375
- checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
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.23.5"
381
+ version = "0.27.1"
384
382
  source = "registry+https://github.com/rust-lang/crates.io-index"
385
- checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
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.23.5"
393
+ version = "0.27.1"
396
394
  source = "registry+https://github.com/rust-lang/crates.io-index"
397
- checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
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.4"
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.12.16"
567
+ version = "0.13.3"
570
568
  source = "registry+https://github.com/rust-lang/crates.io-index"
571
- checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
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.4"
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.23", features = ["extension-module"] }
17
- graph-simulation = "0.1.14"
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]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulation-alg
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "simulation-alg"
7
- version = "0.1.5"
7
+ version = "0.1.6"
8
8
  requires-python = ">=3.10"
9
9
  classifiers = [
10
10
  "Programming Language :: Rust",
@@ -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
- Hyper simulation.
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::with_gil(|py| {
220
- type_same_fn.call1(py, (&x.desc, &y.desc)).map_or(false, |result| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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: &PyObject) -> PyResult<String> {
162
- Python::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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::with_gil(|py| {
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<PyObject> {
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<PyObject> {
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::with_gil(|py| {
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<PyObject> {
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::with_gil(|py| {
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