A graph consists of multiple geometry nodes and corresponding edge information. Edges are stored as a Tensor with the same axes ad `geometry` plus their dual counterparts. Additional dimensions can be added to `edges` to store vector-valued connectivity weights.
| 13 | @sliceable(keepdims='vector') |
| 14 | @dataclass(frozen=True, eq=False) |
| 15 | class Graph(Geometry): |
| 16 | """ |
| 17 | A graph consists of multiple geometry nodes and corresponding edge information. |
| 18 | |
| 19 | Edges are stored as a Tensor with the same axes ad `geometry` plus their dual counterparts. |
| 20 | Additional dimensions can be added to `edges` to store vector-valued connectivity weights. |
| 21 | """ |
| 22 | nodes: Geometry |
| 23 | edges: Tensor |
| 24 | boundary: Dict[str, Dict[str, slice]] |
| 25 | |
| 26 | variable_attrs = ('nodes', 'edges') |
| 27 | |
| 28 | def __post_init__(self): |
| 29 | assert isinstance(self.nodes, Geometry), f"nodes must be a Geometry but got {self.nodes}" |
| 30 | node_dims = non_batch(self.nodes).non_channel |
| 31 | assert node_dims in self.edges.shape and self.edges.shape.dual.rank == node_dims.rank, f"edges must contain all node dims {node_dims} as primal and dual but got {self.edges.shape}" |
| 32 | |
| 33 | @cached_property |
| 34 | def connectivity(self) -> Tensor: |
| 35 | return math.tensor_like(self.edges, 1) if math.is_sparse(self.edges) else (self.edges != 0) & ~math.is_nan(self.edges) |
| 36 | |
| 37 | def as_points(self): |
| 38 | return replace(self, nodes=Point(self.nodes.center)) |
| 39 | |
| 40 | @cached_property |
| 41 | def deltas(self): |
| 42 | return math.pairwise_distances(self.nodes.center, format=self.edges) |
| 43 | |
| 44 | @cached_property |
| 45 | def unit_deltas(self): |
| 46 | return math.safe_div(self.deltas, self.distances) |
| 47 | |
| 48 | @cached_property |
| 49 | def distances(self): |
| 50 | return vec_length(self.deltas) |
| 51 | |
| 52 | @cached_property |
| 53 | def bounding_distance(self) -> Optional[Tensor]: |
| 54 | return math.max(self.distances) |
| 55 | |
| 56 | @property |
| 57 | def center(self) -> Tensor: |
| 58 | return self.nodes.center |
| 59 | |
| 60 | @property |
| 61 | def shape(self) -> Shape: |
| 62 | return self.nodes.shape |
| 63 | |
| 64 | @property |
| 65 | def volume(self) -> Tensor: |
| 66 | return self.nodes.volume |
| 67 | |
| 68 | @property |
| 69 | def faces(self) -> 'Geometry': |
| 70 | raise NotImplementedError |
| 71 | |
| 72 | @property |
no outgoing calls
no test coverage detected