topologicpy 0.8.57__py3-none-any.whl → 0.8.58__py3-none-any.whl

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.
topologicpy/Graph.py CHANGED
@@ -6929,44 +6929,146 @@ class Graph:
6929
6929
  return graph.Edge(vertexA, vertexB, tolerance) # Hook to Core
6930
6930
 
6931
6931
  @staticmethod
6932
- def Edges(graph, vertices=None, tolerance=0.0001):
6932
+ def Edges(
6933
+ graph,
6934
+ vertices: list = None,
6935
+ strict: bool = False,
6936
+ sortBy: str = None,
6937
+ reverse: bool = False,
6938
+ silent: bool = False,
6939
+ tolerance: float = 0.0001
6940
+ ) -> list: # list[topologic_core.Edge]
6933
6941
  """
6934
- Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges.
6942
+ Returns the list of edges from `graph` whose endpoints match the given `vertices`
6943
+ according to the `strict` rule.
6944
+
6945
+ If `strict` is True, both endpoints of an edge must be in `vertices`.
6946
+ If `strict` is False, at least one endpoint must be in `vertices`.
6935
6947
 
6936
6948
  Parameters
6937
6949
  ----------
6938
- graph : topologic_core.Graph
6950
+ graph : topologicpy.Graph
6939
6951
  The input graph.
6940
- vertices : list , optional
6941
- An optional list of vertices to restrict the returned list of edges only to those connected to this list.
6952
+ vertices : list[topologicpy.Vertex]
6953
+ The list of vertices to test membership against.
6954
+ strict : bool, optional
6955
+ If set to True, require both endpoints to be in `vertices`. Otherwise,
6956
+ require at least one endpoint to be in `vertices`. Default is False.
6957
+ sortBy : str , optional
6958
+ The dictionary key to use for sorting the returned edges. Special strings include "length" and "distance" to sort by the length of the edge. Default is None.
6959
+ reverse : bool , optional
6960
+ If set to True, the sorted list is reversed. This has no effect if the sortBy parameter is not set. Default is False.
6961
+ silent : bool, optional
6962
+ Isilent : bool, optional
6963
+ If set to True, all errors and warnings are suppressed. Default is False.
6942
6964
  tolerance : float , optional
6943
6965
  The desired tolerance. Default is 0.0001.
6944
6966
 
6945
6967
  Returns
6946
6968
  -------
6947
- list
6948
- The list of edges in the graph.
6969
+ list[topologic_core.Edge]
6970
+ The list of matching edges from the original graph (not recreated).
6949
6971
 
6950
6972
  """
6973
+ from topologicpy.Vertex import Vertex
6951
6974
  from topologicpy.Topology import Topology
6975
+ from topologicpy.Edge import Edge
6976
+ from topologicpy.Helper import Helper
6977
+ from topologicpy.Dictionary import Dictionary
6978
+
6979
+ def sort_edges(edges, sortBy, reverse):
6980
+ if not sortBy is None:
6981
+ if "length" in sortBy.lower() or "dist" in sortBy.lower():
6982
+ edge_values = [Edge.Length(e) for e in edges]
6983
+ else:
6984
+ edge_values = [Dictionary.ValueAtKey(Topology.Dictionary(e), sortBy, "0") for e in edges]
6985
+ edges = Helper.Sort(edges, edge_values)
6986
+ if reverse:
6987
+ edges.reverse()
6988
+ return edges
6952
6989
 
6953
6990
  if not Topology.IsInstance(graph, "Graph"):
6954
- print("Graph.Edges - Error: The input graph is not a valid graph. Returning None.")
6955
- return None
6991
+ if not silent:
6992
+ print("Graph.InducedEdges - Error: The input 'graph' is not a valid Graph. Returning [].")
6993
+ return []
6994
+
6995
+ graph_edges = []
6996
+ _ = graph.Edges(graph_edges, tolerance) # Hook to Core
6997
+ graph_edges = list(dict.fromkeys(graph_edges)) # remove duplicates
6998
+
6999
+ if not graph_edges:
7000
+ return []
6956
7001
  if not vertices:
6957
- edges = []
6958
- _ = graph.Edges(edges, tolerance) # Hook to Core
6959
- if not edges:
6960
- return []
6961
- return list(dict.fromkeys(edges)) # remove duplicates
6962
- else:
6963
- vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
6964
- if len(vertices) < 1:
6965
- print("Graph.Edges - Error: The input list of vertices does not contain any valid vertices. Returning None.")
6966
- return None
6967
- edges = []
6968
- _ = graph.Edges(vertices, tolerance, edges) # Hook to Core
6969
- return list(dict.fromkeys(edges)) # remove duplicates
7002
+ graph_edges = sort_edges(graph_edges, sortBy, reverse)
7003
+ return graph_edges
7004
+
7005
+ if not isinstance(vertices, list):
7006
+ if not silent:
7007
+ print("Graph.Edges - Error: The input 'vertices' is not a list. Returning [].")
7008
+ return []
7009
+
7010
+ valid_vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
7011
+ if not valid_vertices:
7012
+ if not silent:
7013
+ print("Graph.Edges - Warning: No valid vertices provided. Returning [].")
7014
+ return []
7015
+
7016
+ return_edges = []
7017
+ for e in graph_edges:
7018
+ sv = Edge.StartVertex(e)
7019
+ ev = Edge.EndVertex(e)
7020
+
7021
+ in_start = not Vertex.Index(sv, valid_vertices) is None
7022
+ in_end = not Vertex.Index(ev, valid_vertices) is None
7023
+ if strict:
7024
+ if in_start and in_end:
7025
+ return_edges.append(e)
7026
+ else:
7027
+ if in_start or in_end:
7028
+ return_edges.append(e)
7029
+
7030
+ return_edges = sort_edges(return_edges, sortBy, reverse)
7031
+ return return_edges
7032
+
7033
+ # @staticmethod
7034
+ # def Edges(graph, vertices=None, tolerance=0.0001):
7035
+ # """
7036
+ # Returns the edges found in the input graph. If the input list of vertices is specified, this method returns the edges connected to this list of vertices. Otherwise, it returns all graph edges.
7037
+
7038
+ # Parameters
7039
+ # ----------
7040
+ # graph : topologic_core.Graph
7041
+ # The input graph.
7042
+ # vertices : list , optional
7043
+ # An optional list of vertices to restrict the returned list of edges only to those connected to this list.
7044
+ # tolerance : float , optional
7045
+ # The desired tolerance. Default is 0.0001.
7046
+
7047
+ # Returns
7048
+ # -------
7049
+ # list
7050
+ # The list of edges in the graph.
7051
+
7052
+ # """
7053
+ # from topologicpy.Topology import Topology
7054
+
7055
+ # if not Topology.IsInstance(graph, "Graph"):
7056
+ # print("Graph.Edges - Error: The input graph is not a valid graph. Returning None.")
7057
+ # return None
7058
+ # if not vertices:
7059
+ # edges = []
7060
+ # _ = graph.Edges(edges, tolerance) # Hook to Core
7061
+ # if not edges:
7062
+ # return []
7063
+ # return list(dict.fromkeys(edges)) # remove duplicates
7064
+ # else:
7065
+ # vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
7066
+ # if len(vertices) < 1:
7067
+ # print("Graph.Edges - Error: The input list of vertices does not contain any valid vertices. Returning None.")
7068
+ # return None
7069
+ # edges = []
7070
+ # _ = graph.Edges(vertices, tolerance, edges) # Hook to Core
7071
+ # return list(dict.fromkeys(edges)) # remove duplicates
6970
7072
 
6971
7073
  @staticmethod
6972
7074
  def EigenVectorCentrality(graph, normalize: bool = False, key: str = "eigen_vector_centrality", colorKey: str = "evc_color", colorScale: str = "viridis", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
@@ -8412,6 +8514,57 @@ class Graph:
8412
8514
  edge = Topology.SetDictionary(edge, d)
8413
8515
  return graph
8414
8516
 
8517
+
8518
+ @staticmethod
8519
+ def InducedSubgraph(graph, vertices: list = None, strict: bool = False, silent: bool = False, tolerance: float = 0.0001):
8520
+ """
8521
+ Returns the subgraph whose edges are connected to the given `vertices`
8522
+ according to the `strict` rule. Isolated vertices are included as-is.
8523
+
8524
+ If `strict` is True, both endpoints of an edge must be in `vertices`.
8525
+ If `strict` is False, at least one endpoint must be in `vertices`.
8526
+
8527
+ Parameters
8528
+ ----------
8529
+ graph : topologicpy.Graph
8530
+ The input graph.
8531
+ vertices : list[topologicpy.Vertex]
8532
+ The list of vertices to test membership against.
8533
+ strict : bool, optional
8534
+ If set to True, require both endpoints to be in `vertices`. Otherwise,
8535
+ require at least one endpoint to be in `vertices`. Default is False.
8536
+ silent : bool, optional
8537
+ Isilent : bool, optional
8538
+ If set to True, all errors and warnings are suppressed. Default is False
8539
+ tolerance : float , optional
8540
+ The desired tolerance. Default is 0.0001.
8541
+
8542
+ Returns
8543
+ -------
8544
+ list[topologic_core.Edge]
8545
+ The list of matching edges from the original graph (not recreated).
8546
+
8547
+ """
8548
+ from topologicpy.Topology import Topology
8549
+
8550
+ if not Topology.IsInstance(graph, "Graph"):
8551
+ if not silent:
8552
+ print("Graph.InducedSubgraph - Error: The input graph parameter is not a valid graph. Returning None.")
8553
+
8554
+ if not isinstance(vertices, list):
8555
+ if not silent:
8556
+ print("Graph.InducedSubgraph - Error: The input 'vertices' is not a list. Returning None.")
8557
+ return None
8558
+
8559
+ valid_vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
8560
+ if not valid_vertices:
8561
+ if not silent:
8562
+ print("Graph.InducedSubgraph - Warning: No valid vertices provided. Returning None.")
8563
+ return None
8564
+ connected_vertices = [v for v in valid_vertices if Graph.VertexDegree(graph, v) > 0]
8565
+ edges = Graph.Edges(graph, connected_vertices, strict=strict, tolerance=tolerance)
8566
+ return Graph.ByVerticesEdges(valid_vertices, edges)
8567
+
8415
8568
  @staticmethod
8416
8569
  def IsEmpty(graph, silent: bool = False):
8417
8570
  """
@@ -11215,7 +11368,166 @@ class Graph:
11215
11368
  print(f'Graph.Kernel - Error: Unsupported method "{method}". '
11216
11369
  f'Supported methods are "WL" and "Hopper". Returning None.')
11217
11370
  return None
11218
-
11371
+
11372
+ @staticmethod
11373
+ def KHopsSubgraph(
11374
+ graph,
11375
+ vertices: list,
11376
+ k: int = 1,
11377
+ direction: str = "both",
11378
+ silent: bool = False,
11379
+ ):
11380
+ """
11381
+ Returns a subgraph consisting of the k-hop neighborhood around the input list of seed vertices.
11382
+
11383
+ Parameters
11384
+ ----------
11385
+ graph : topologicpy.Graph
11386
+ The input graph.
11387
+ vertices : list
11388
+ The input list of seed vertices.
11389
+ k : int, optional
11390
+ Number of hops. Default is 1.
11391
+ direction : str, optional
11392
+ 'both', 'out', or 'in'. Default 'both'.
11393
+ silent : bool, optional
11394
+ Suppress warnings/errors. Default False.
11395
+
11396
+ Returns
11397
+ -------
11398
+ topologicpy.Graph or None
11399
+ The resulting subgraph, or None on error.
11400
+ """
11401
+ from topologicpy.Vertex import Vertex
11402
+ from topologicpy.Edge import Edge
11403
+ from topologicpy.Graph import Graph
11404
+ from topologicpy.Topology import Topology
11405
+ from topologicpy.Dictionary import Dictionary
11406
+
11407
+ # ---- validate inputs ----
11408
+ if not Topology.IsInstance(graph, "graph"):
11409
+ if not silent:
11410
+ print("Graph.KHopsSubgraph - Error: The input graph parameter is not a valid graph. Returning None.")
11411
+ return None
11412
+
11413
+ if not isinstance(vertices, list):
11414
+ if not silent:
11415
+ print("Graph.KHopsSubgraph - Error: The input vertices parameter is not a valid list. Returning None.")
11416
+ return None
11417
+
11418
+ graph_vertices = Graph.Vertices(graph)
11419
+ if not graph_vertices:
11420
+ if not silent:
11421
+ print("Graph.KHopsSubgraph - Error: The input graph does not contain any vertices. Returning None.")
11422
+ return None
11423
+
11424
+ # Keep only valid vertex objects
11425
+ seed_vertices = [v for v in vertices if Topology.IsInstance(v, "vertex")]
11426
+ if not seed_vertices:
11427
+ if not silent:
11428
+ print("Graph.KHopsSubgraph - Error: The input vertices list does not contain any valid vertices. Returning None.")
11429
+ return None
11430
+
11431
+ # ---- map seeds to vertex indices (prefer identity; fallback to list.index) ----
11432
+ id_to_index = {Topology.UUID(v): i for i, v in enumerate(graph_vertices)}
11433
+ seed_indices = []
11434
+ for sv in seed_vertices:
11435
+ idx = id_to_index.get(Topology.UUID(sv))
11436
+ if idx is None:
11437
+ try:
11438
+ idx = graph_vertices.index(sv) # fallback if same object not used
11439
+ except ValueError:
11440
+ idx = None
11441
+ if idx is not None:
11442
+ seed_indices.append(idx)
11443
+
11444
+ if not seed_indices:
11445
+ if not silent:
11446
+ print("Graph.KHopsSubgraph - Error: None of the seed vertices are found in the graph. Returning None.")
11447
+ return None
11448
+
11449
+ # ---- get mesh data (index-based edge list) ----
11450
+ # Expect: mesh_data["vertices"] (list), mesh_data["edges"] (list of [a, b] indices)
11451
+ mesh_data = Graph.MeshData(graph)
11452
+ edges_idx = mesh_data.get("edges") or []
11453
+ # Compute number of vertices robustly
11454
+ n_verts = len(mesh_data.get("vertices") or graph_vertices)
11455
+
11456
+ # ---- build adjacency (directed; BFS respects 'direction') ----
11457
+ adj_out = {i: set() for i in range(n_verts)}
11458
+ adj_in = {i: set() for i in range(n_verts)}
11459
+ for (a, b) in edges_idx:
11460
+ if 0 <= a < n_verts and 0 <= b < n_verts:
11461
+ adj_out[a].add(b)
11462
+ adj_in[b].add(a)
11463
+
11464
+ # ---- BFS up to k hops ----
11465
+ dir_norm = (direction or "both").lower()
11466
+ if dir_norm not in ("both", "out", "in"):
11467
+ dir_norm = "both"
11468
+
11469
+ visited = set(seed_indices)
11470
+ frontier = set(seed_indices)
11471
+ for _ in range(max(0, int(k))):
11472
+ nxt = set()
11473
+ for v in frontier:
11474
+ if dir_norm in ("both", "out"):
11475
+ nxt |= adj_out.get(v, set())
11476
+ if dir_norm in ("both", "in"):
11477
+ nxt |= adj_in.get(v, set())
11478
+ nxt -= visited
11479
+ if not nxt:
11480
+ break
11481
+ visited |= nxt
11482
+ frontier = nxt
11483
+
11484
+ if not visited:
11485
+ if not silent:
11486
+ print("Graph.KHopsSubgraph - Warning: No vertices found within the specified k hops. Returning None.")
11487
+ return None
11488
+
11489
+ # ---- assemble subgraph ----
11490
+ # Vertices: actual TopologicPy Vertex objects
11491
+ sub_vertex_indices = sorted(visited)
11492
+ sub_vertices = [graph_vertices[i] for i in sub_vertex_indices]
11493
+
11494
+ # Edges: include only those whose endpoints are both in the subgraph
11495
+ sub_index_set = set(sub_vertex_indices)
11496
+ # Map from global index -> actual Vertex object for edge reconstruction
11497
+ idx_to_vertex = {i: graph_vertices[i] for i in sub_vertex_indices}
11498
+
11499
+ sub_edges = []
11500
+ for (a, b) in edges_idx:
11501
+ if a in sub_index_set and b in sub_index_set:
11502
+ # Recreate edge to ensure it references the subgraph vertices
11503
+ ea = idx_to_vertex[a]
11504
+ eb = idx_to_vertex[b]
11505
+ try:
11506
+ e = Edge.ByStartVertexEndVertex(ea, eb)
11507
+ except Exception:
11508
+ # If creation fails, skip this edge
11509
+ continue
11510
+ # Preserve edge label if present
11511
+ try:
11512
+ # Find original edge and copy its dictionary if possible
11513
+ # (best-effort; safe if Graph.Edges aligns with edges_idx order)
11514
+ # Otherwise, leave edge as-is.
11515
+ pass
11516
+ except Exception:
11517
+ pass
11518
+ sub_edges.append(e)
11519
+
11520
+ try:
11521
+ return Graph.ByVerticesEdges(sub_vertices, sub_edges)
11522
+ except Exception:
11523
+ # As a fallback, some environments accept edges alone
11524
+ try:
11525
+ return Graph.ByEdges(sub_edges)
11526
+ except Exception:
11527
+ if not silent:
11528
+ print("Graph.KHopsSubgraph - Error: Failed to construct the subgraph. Returning None.")
11529
+ return None
11530
+
11219
11531
  @staticmethod
11220
11532
  def Laplacian(graph, silent: bool = False, normalized: bool = False):
11221
11533
  """
@@ -14267,7 +14579,7 @@ class Graph:
14267
14579
  return round(degree, mantissa)
14268
14580
 
14269
14581
  @staticmethod
14270
- def Vertices(graph, vertexKey=None, reverse=False):
14582
+ def Vertices(graph, sortBy=None, reverse=False):
14271
14583
  """
14272
14584
  Returns the list of vertices in the input graph.
14273
14585
 
@@ -14275,11 +14587,14 @@ class Graph:
14275
14587
  ----------
14276
14588
  graph : topologic_core.Graph
14277
14589
  The input graph.
14278
- vertexKey : str , optional
14279
- If set, the returned list of vertices is sorted according to the dicitonary values stored under this key. Default is None.
14590
+ sortBy : str , optional
14591
+ The dictionary key to use for sorting the returned edges. Special strings include "length" and "distance" to sort by the length of the edge. Default is None.
14280
14592
  reverse : bool , optional
14281
- If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. Default is False.
14282
-
14593
+ If set to True, the sorted list is reversed. This has no effect if the sortBy parameter is not set. Default is False.
14594
+ silent : bool, optional
14595
+ Isilent : bool, optional
14596
+ If set to True, all errors and warnings are suppressed. Default is False.
14597
+
14283
14598
  Returns
14284
14599
  -------
14285
14600
  list
@@ -14299,13 +14614,13 @@ class Graph:
14299
14614
  _ = graph.Vertices(vertices) # Hook to Core
14300
14615
  except:
14301
14616
  vertices = []
14302
- if not vertexKey == None:
14303
- sorting_values = []
14617
+ if not sortBy == None:
14618
+ vertex_values = []
14304
14619
  for v in vertices:
14305
14620
  d = Topology.Dictionary(v)
14306
- value = Dictionary.ValueAtKey(d, vertexKey)
14307
- sorting_values.append(value)
14308
- vertices = Helper.Sort(vertices, sorting_values)
14621
+ value = str(Dictionary.ValueAtKey(d, sortBy, "0"))
14622
+ vertex_values.append(value)
14623
+ vertices = Helper.Sort(vertices, vertex_values)
14309
14624
  if reverse == True:
14310
14625
  vertices.reverse()
14311
14626
  return vertices