Skip to main content

oxgraph_topology/
lib.rs

1//! Storage-agnostic traits for discrete topology views.
2//!
3//! `oxgraph-topology` defines the minimal vocabulary shared by graph,
4//! hypergraph, snapshot, and layout crates. It does not define concrete node,
5//! edge, vertex, hyperedge, incidence, storage, or role types. Implementations
6//! provide those through associated types.
7//!
8//! This crate defines read-view capabilities. Mutation belongs in explicit
9//! capability traits that define identity stability, deletion, compaction, and
10//! stale-handle semantics.
11//!
12//! A topology view is any value that exposes topology through these traits. The
13//! view decides its own boundary: an entire snapshot, one layout section, a
14//! generated projection, a page-sized window, or an overlay can all be views if
15//! they provide the requested capabilities.
16#![no_std]
17
18#[cfg(kani)]
19extern crate kani;
20
21/// Marker trait for compact topology identity handles.
22///
23/// IDs are handles into topology storage, not the storage itself. They are
24/// passed by value so traversal APIs can remain simple and static-dispatch
25/// friendly. A logical element, relation, or incidence can have different ID
26/// representations at different layers. Implementations should document which
27/// identity layer each ID type represents and how it maps to other exposed
28/// identity layers.
29///
30/// # Performance
31///
32/// Implementations are expected to be small `Copy` handles. Copying,
33/// comparing, ordering, hashing, and formatting for debug should be `O(1)`.
34pub trait TopologyId: Copy + Eq + Ord + core::fmt::Debug + core::hash::Hash {}
35
36/// Blanket implementation for compact ID handle types.
37///
38/// Any type satisfying the `TopologyId` bounds is a valid topology ID. This
39/// keeps the crate trait-based without requiring implementations to write empty
40/// marker impls for every local handle type.
41///
42/// # Performance
43///
44/// `perf: unspecified`; performance is inherited from the concrete ID type.
45impl<T> TopologyId for T where T: Copy + Eq + Ord + core::fmt::Debug + core::hash::Hash {}
46
47/// Common element and relation identity vocabulary for a topology view.
48///
49/// The associated ID types deliberately avoid graph- or hypergraph-specific
50/// names. Graph crates can map elements to nodes and relations to edges;
51/// hypergraph crates can map elements to vertices and relations to hyperedges.
52///
53/// # Performance
54///
55/// Associated ID values should be compact `O(1)` handles. Arbitrary relation or
56/// element payloads should be exposed by separate traits keyed by the associated
57/// ID types.
58pub trait TopologyBase {
59    /// Identity of a topology element.
60    ///
61    /// # Performance
62    ///
63    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
64    type ElementId: TopologyId;
65
66    /// Identity of a topology relation.
67    ///
68    /// # Performance
69    ///
70    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
71    type RelationId: TopologyId;
72}
73
74/// Substrate-neutral alias for a topology view's element ID type.
75///
76/// # Performance
77///
78/// `perf: unspecified`; performance is inherited from the underlying
79/// [`TopologyBase::ElementId`] type.
80pub type ElementId<T> = <T as TopologyBase>::ElementId;
81
82/// Substrate-neutral alias for a topology view's relation ID type.
83///
84/// # Performance
85///
86/// `perf: unspecified`; performance is inherited from the underlying
87/// [`TopologyBase::RelationId`] type.
88pub type RelationId<T> = <T as TopologyBase>::RelationId;
89
90/// Optional total weight capability for topology elements.
91///
92/// A view implements this trait only when every visible element has a weight
93/// representation. The topology layer does not interpret the value: it is not a
94/// probability, cost, distance, count, or property name. Algorithms state their
95/// own numeric contracts when they consume a selected weight capability.
96///
97/// # Performance
98///
99/// Lookup should be `O(1)` unless an implementation documents a weaker
100/// contract.
101pub trait ElementWeight: TopologyBase {
102    /// Copyable weight representation attached to each visible element.
103    ///
104    /// # Performance
105    ///
106    /// Values should be `O(1)` to copy.
107    type Weight: Copy;
108
109    /// Returns the weight attached to `element`.
110    ///
111    /// # Performance
112    ///
113    /// Expected `O(1)` unless the implementation documents otherwise.
114    fn element_weight(&self, element: Self::ElementId) -> Self::Weight;
115}
116
117/// Optional total weight capability for topology relations.
118///
119/// A view implements this trait only when every visible relation has a weight
120/// representation. The topology layer does not interpret the value; algorithms
121/// define any finite, non-negative, additive, ordered, or normalization
122/// requirements separately.
123///
124/// # Performance
125///
126/// Lookup should be `O(1)` unless an implementation documents a weaker
127/// contract.
128pub trait RelationWeight: TopologyBase {
129    /// Copyable weight representation attached to each visible relation.
130    ///
131    /// # Performance
132    ///
133    /// Values should be `O(1)` to copy.
134    type Weight: Copy;
135
136    /// Returns the weight attached to `relation`.
137    ///
138    /// # Performance
139    ///
140    /// Expected `O(1)` unless the implementation documents otherwise.
141    fn relation_weight(&self, relation: Self::RelationId) -> Self::Weight;
142}
143
144/// Optional local-to-canonical element identity capability.
145///
146/// Views implement this trait only when they guarantee a stable canonical ID for
147/// every visible element in the view's documented identity scope. The canonical
148/// ID is a substrate identity, not a Python label or domain identifier.
149///
150/// # Performance
151///
152/// Lookup should be `O(1)` unless an implementation documents a weaker
153/// contract.
154pub trait CanonicalElementIdentity: TopologyBase {
155    /// Canonical element ID guaranteed by this view.
156    ///
157    /// # Performance
158    ///
159    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
160    type CanonicalElementId: TopologyId;
161
162    /// Returns the canonical ID for a visible local `element`.
163    ///
164    /// # Performance
165    ///
166    /// Expected `O(1)` unless the implementation documents otherwise.
167    fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId;
168}
169
170/// Optional canonical-to-local element identity capability.
171///
172/// This reverse lookup is separate from [`CanonicalElementIdentity`] because it
173/// may require extra memory or may be partial for filtered and projected views.
174///
175/// # Performance
176///
177/// Lookup should be `O(1)` unless an implementation documents a weaker
178/// contract.
179pub trait LocalElementIdentity: CanonicalElementIdentity {
180    /// Returns the visible local element for `canonical`, if present.
181    ///
182    /// # Performance
183    ///
184    /// Expected `O(1)` unless the implementation documents otherwise.
185    fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId>;
186}
187
188/// Optional local-to-canonical relation identity capability.
189///
190/// Views implement this trait only when they guarantee a stable canonical ID for
191/// every visible relation in the view's documented identity scope.
192///
193/// # Performance
194///
195/// Lookup should be `O(1)` unless an implementation documents a weaker
196/// contract.
197pub trait CanonicalRelationIdentity: TopologyBase {
198    /// Canonical relation ID guaranteed by this view.
199    ///
200    /// # Performance
201    ///
202    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
203    type CanonicalRelationId: TopologyId;
204
205    /// Returns the canonical ID for a visible local `relation`.
206    ///
207    /// # Performance
208    ///
209    /// Expected `O(1)` unless the implementation documents otherwise.
210    fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId;
211}
212
213/// Optional canonical-to-local relation identity capability.
214///
215/// This reverse lookup is separate from [`CanonicalRelationIdentity`] because it
216/// may require extra memory or may be partial for filtered and projected views.
217///
218/// # Performance
219///
220/// Lookup should be `O(1)` unless an implementation documents a weaker
221/// contract.
222pub trait LocalRelationIdentity: CanonicalRelationIdentity {
223    /// Returns the visible local relation for `canonical`, if present.
224    ///
225    /// # Performance
226    ///
227    /// Expected `O(1)` unless the implementation documents otherwise.
228    fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId>;
229}
230
231/// Incidence identity and role vocabulary for topology views with incidences.
232///
233/// Incidence support is separate from [`TopologyBase`] so graph-only views can
234/// expose nodes and edges without inventing endpoint identities or roles. Views
235/// that support relation-to-element participation records implement this trait.
236///
237/// # Performance
238///
239/// Associated ID values should be compact `O(1)` handles. `Role` describes how
240/// an element participates in a relation. Arbitrary incidence payloads should be
241/// exposed by separate traits keyed by [`Self::IncidenceId`].
242pub trait IncidenceBase: TopologyBase {
243    /// Identity of one element's participation in one relation.
244    ///
245    /// # Performance
246    ///
247    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
248    type IncidenceId: TopologyId;
249
250    /// Implementation-defined participation role.
251    ///
252    /// A role is a topology-level label for an incidence. Rich metadata can be
253    /// attached by separate payload traits or storage layers keyed by
254    /// [`Self::IncidenceId`], [`Self::RelationId`], or [`Self::ElementId`].
255    ///
256    /// The `Copy + Eq + Debug` bound lets role-aware generic algorithms compare,
257    /// copy, and debug-format roles without each consumer restating the bound;
258    /// the role's *meaning* stays implementation-defined.
259    ///
260    /// # Performance
261    ///
262    /// `perf: unspecified`. Implementations should prefer structural role
263    /// values or compact role handles; rich metadata should be reached through
264    /// separate payload access traits.
265    type Role: Copy + Eq + core::fmt::Debug;
266}
267
268/// Optional total weight capability for topology incidences.
269///
270/// A view implements this trait only when every visible incidence has a weight
271/// representation. The topology layer does not interpret the value; algorithms
272/// define their own numeric contracts separately.
273///
274/// # Performance
275///
276/// Lookup should be `O(1)` unless an implementation documents a weaker
277/// contract.
278pub trait IncidenceWeight: IncidenceBase {
279    /// Copyable weight representation attached to each visible incidence.
280    ///
281    /// # Performance
282    ///
283    /// Values should be `O(1)` to copy.
284    type Weight: Copy;
285
286    /// Returns the weight attached to `incidence`.
287    ///
288    /// # Performance
289    ///
290    /// Expected `O(1)` unless the implementation documents otherwise.
291    fn incidence_weight(&self, incidence: Self::IncidenceId) -> Self::Weight;
292}
293
294/// Optional local-to-canonical incidence identity capability.
295///
296/// Views implement this trait only when they guarantee a stable canonical ID for
297/// every visible incidence in the view's documented identity scope.
298///
299/// # Performance
300///
301/// Lookup should be `O(1)` unless an implementation documents a weaker
302/// contract.
303pub trait CanonicalIncidenceIdentity: IncidenceBase {
304    /// Canonical incidence ID guaranteed by this view.
305    ///
306    /// # Performance
307    ///
308    /// Values should be `O(1)` to copy, compare, order, hash, and debug-format.
309    type CanonicalIncidenceId: TopologyId;
310
311    /// Returns the canonical ID for a visible local `incidence`.
312    ///
313    /// # Performance
314    ///
315    /// Expected `O(1)` unless the implementation documents otherwise.
316    fn canonical_incidence_id(&self, incidence: Self::IncidenceId) -> Self::CanonicalIncidenceId;
317}
318
319/// Optional canonical-to-local incidence identity capability.
320///
321/// This reverse lookup is separate from [`CanonicalIncidenceIdentity`] because
322/// it may require extra memory or may be partial for filtered and projected
323/// views.
324///
325/// # Performance
326///
327/// Lookup should be `O(1)` unless an implementation documents a weaker
328/// contract.
329pub trait LocalIncidenceIdentity: CanonicalIncidenceIdentity {
330    /// Returns the visible local incidence for `canonical`, if present.
331    ///
332    /// # Performance
333    ///
334    /// Expected `O(1)` unless the implementation documents otherwise.
335    fn local_incidence_id(
336        &self,
337        canonical: Self::CanonicalIncidenceId,
338    ) -> Option<Self::IncidenceId>;
339}
340
341/// Count capability for a topology view.
342///
343/// This trait is separated from [`TopologyBase`] because counts are a capability.
344/// Some views can report global counts cheaply; others expose only local,
345/// paged, generated, or filtered topology and should define counts only when the
346/// result is meaningful for that view.
347///
348/// # Performance
349///
350/// Methods should be `O(1)` unless an implementation documents a weaker
351/// contract.
352pub trait TopologyCounts: TopologyBase {
353    /// Returns the number of elements visible in this topology view.
354    ///
355    /// # Performance
356    ///
357    /// Expected `O(1)` unless the implementation documents otherwise.
358    fn element_count(&self) -> usize;
359
360    /// Returns the number of relations visible in this topology view.
361    ///
362    /// # Performance
363    ///
364    /// Expected `O(1)` unless the implementation documents otherwise.
365    fn relation_count(&self) -> usize;
366}
367
368/// Count capability for incidence-capable topology views.
369///
370/// This trait is separated from [`TopologyCounts`] because not every topology
371/// view exposes incidence records. Graph-only views can count nodes and edges
372/// without defining endpoint-incidence identities.
373///
374/// # Performance
375///
376/// Methods should be `O(1)` unless an implementation documents a weaker
377/// contract.
378pub trait IncidenceCounts: IncidenceBase {
379    /// Returns the number of incidences visible in this topology view.
380    ///
381    /// # Performance
382    ///
383    /// Expected `O(1)` unless the implementation documents otherwise.
384    fn incidence_count(&self) -> usize;
385}
386
387/// Dense element-index capability for topology views.
388///
389/// This capability maps visible element IDs to compact array indexes usable by
390/// algorithms that need visited sets, distance arrays, parent arrays, or other
391/// per-element scratch storage. The index is a property of the view, not the ID
392/// type itself: a view may expose opaque IDs while also providing a dense index
393/// mapping.
394///
395/// `element_bound` is an allocation bound, not necessarily the exact visible
396/// element count. Immutable compact layouts usually have `element_bound() ==
397/// element_count()`. Mutable layouts with tombstones, overlays, or stable slots
398/// may have `element_bound() >= element_count()`.
399///
400/// Implementations must ensure every valid visible element maps to an index less
401/// than `element_bound`, distinct visible elements map to distinct indexes, and
402/// indexes remain stable for the lifetime of the view operation using them.
403///
404/// # Performance
405///
406/// Methods should be `O(1)` unless an implementation documents a weaker
407/// contract.
408pub trait ElementIndex: TopologyBase {
409    /// Returns the exclusive upper bound for element indexes in this view.
410    ///
411    /// # Performance
412    ///
413    /// Expected `O(1)` unless the implementation documents otherwise.
414    fn element_bound(&self) -> usize;
415
416    /// Returns the dense index for `element` in this view.
417    ///
418    /// # Performance
419    ///
420    /// Expected `O(1)` unless the implementation documents otherwise.
421    fn element_index(&self, element: Self::ElementId) -> usize;
422}
423
424/// Dense relation-index capability for topology views.
425///
426/// This capability maps visible relation IDs to compact array indexes usable by
427/// algorithms that need per-relation scratch storage. The index is a property of
428/// the view, not the ID type itself.
429///
430/// `relation_bound` is an allocation bound, not necessarily the exact visible
431/// relation count. Immutable compact layouts usually have `relation_bound() ==
432/// relation_count()`. Mutable layouts may expose a larger bound.
433///
434/// Implementations must ensure every valid visible relation maps to an index
435/// less than `relation_bound`, distinct visible relations map to distinct
436/// indexes, and indexes remain stable for the lifetime of the view operation
437/// using them.
438///
439/// # Performance
440///
441/// Methods should be `O(1)` unless an implementation documents a weaker
442/// contract.
443pub trait RelationIndex: TopologyBase {
444    /// Returns the exclusive upper bound for relation indexes in this view.
445    ///
446    /// # Performance
447    ///
448    /// Expected `O(1)` unless the implementation documents otherwise.
449    fn relation_bound(&self) -> usize;
450
451    /// Returns the dense index for `relation` in this view.
452    ///
453    /// # Performance
454    ///
455    /// Expected `O(1)` unless the implementation documents otherwise.
456    fn relation_index(&self, relation: Self::RelationId) -> usize;
457}
458
459/// Dense incidence-index capability for topology views.
460///
461/// This capability maps visible incidence IDs to compact array indexes usable by
462/// algorithms that need per-incidence scratch storage. Views that do not expose
463/// incidences do not implement this trait.
464///
465/// `incidence_bound` is an allocation bound, not necessarily the exact visible
466/// incidence count. Immutable compact layouts usually have `incidence_bound() ==
467/// incidence_count()`. Mutable layouts may expose a larger bound.
468///
469/// Implementations must ensure every valid visible incidence maps to an index
470/// less than `incidence_bound`, distinct visible incidences map to distinct
471/// indexes, and indexes remain stable for the lifetime of the view operation
472/// using them.
473///
474/// # Performance
475///
476/// Methods should be `O(1)` unless an implementation documents a weaker
477/// contract.
478pub trait IncidenceIndex: IncidenceBase {
479    /// Returns the exclusive upper bound for incidence indexes in this view.
480    ///
481    /// # Performance
482    ///
483    /// Expected `O(1)` unless the implementation documents otherwise.
484    fn incidence_bound(&self) -> usize;
485
486    /// Returns the dense index for `incidence` in this view.
487    ///
488    /// # Performance
489    ///
490    /// Expected `O(1)` unless the implementation documents otherwise.
491    fn incidence_index(&self, incidence: Self::IncidenceId) -> usize;
492}
493
494/// Element-ID containment capability for a topology view.
495///
496/// This capability answers whether an element ID is valid and visible in this
497/// view at the time of the call. It is intentionally separate from
498/// [`ElementIndex`]: an index bound is an allocation bound, while containment is
499/// an ID-validity predicate. Mutable, filtered, tombstoned, or overlay views may
500/// have indexes below the bound that are not visible elements.
501///
502/// # Performance
503///
504/// Expected `O(1)` unless the implementation documents otherwise.
505pub trait ContainsElement: TopologyBase {
506    /// Returns whether `element` is valid and visible in this view.
507    ///
508    /// # Performance
509    ///
510    /// Expected `O(1)` unless the implementation documents otherwise.
511    fn contains_element(&self, element: Self::ElementId) -> bool;
512}
513
514/// Relation-ID containment capability for a topology view.
515///
516/// This capability answers whether a relation ID is valid and visible in this
517/// view at the time of the call. It does not answer graph-specific questions
518/// such as whether an edge between two nodes exists, nor hypergraph-specific
519/// questions such as whether a vertex participates in a hyperedge.
520///
521/// # Performance
522///
523/// Expected `O(1)` unless the implementation documents otherwise.
524pub trait ContainsRelation: TopologyBase {
525    /// Returns whether `relation` is valid and visible in this view.
526    ///
527    /// # Performance
528    ///
529    /// Expected `O(1)` unless the implementation documents otherwise.
530    fn contains_relation(&self, relation: Self::RelationId) -> bool;
531}
532
533/// Incidence-ID containment capability for an incidence-capable topology view.
534///
535/// This capability answers whether an incidence ID is valid and visible in this
536/// view at the time of the call.
537///
538/// # Performance
539///
540/// Expected `O(1)` unless the implementation documents otherwise.
541pub trait ContainsIncidence: IncidenceBase {
542    /// Returns whether `incidence` is valid and visible in this view.
543    ///
544    /// # Performance
545    ///
546    /// Expected `O(1)` unless the implementation documents otherwise.
547    fn contains_incidence(&self, incidence: Self::IncidenceId) -> bool;
548}
549
550/// Capability for traversing incidences attached to a relation.
551///
552/// The generic associated iterator type lets each backend return its own
553/// concrete iterator without allocating or using dynamic dispatch.
554///
555/// # Performance
556///
557/// Creating the iterator should be `O(1)` unless an implementation documents a
558/// weaker contract. Yielding `k` incidences should be `O(k)` because the output
559/// itself has length `k`.
560pub trait RelationIncidences: IncidenceBase {
561    /// Iterator over incidence IDs for one relation.
562    ///
563    /// # Performance
564    ///
565    /// Advancing the iterator should be amortized `O(1)` unless an
566    /// implementation documents otherwise.
567    type Incidences<'view>: Iterator<Item = Self::IncidenceId>
568    where
569        Self: 'view;
570
571    /// Returns incidences attached to `relation`.
572    ///
573    /// # Performance
574    ///
575    /// Expected `O(1)` to create the iterator; yielding `k` incidences is
576    /// expected `O(k)`.
577    fn relation_incidences(&self, relation: Self::RelationId) -> Self::Incidences<'_>;
578}
579
580/// Capability for traversing incidences attached to an element.
581///
582/// This is the topology-general form of asking which relations mention an
583/// element.
584///
585/// # Performance
586///
587/// Creating the iterator should be `O(1)` unless an implementation documents a
588/// weaker contract. Yielding `k` incidences should be `O(k)` because the output
589/// itself has length `k`.
590pub trait ElementIncidences: IncidenceBase {
591    /// Iterator over incidence IDs for one element.
592    ///
593    /// # Performance
594    ///
595    /// Advancing the iterator should be amortized `O(1)` unless an
596    /// implementation documents otherwise.
597    type Incidences<'view>: Iterator<Item = Self::IncidenceId>
598    where
599        Self: 'view;
600
601    /// Returns incidences attached to `element`.
602    ///
603    /// # Performance
604    ///
605    /// Expected `O(1)` to create the iterator; yielding `k` incidences is
606    /// expected `O(k)`.
607    fn element_incidences(&self, element: Self::ElementId) -> Self::Incidences<'_>;
608}
609
610/// Capability for resolving the element side of an incidence.
611///
612/// # Performance
613///
614/// Lookup should be `O(1)` unless an implementation documents a weaker
615/// contract.
616pub trait IncidenceElement: IncidenceBase {
617    /// Returns the element participating through `incidence`.
618    ///
619    /// # Performance
620    ///
621    /// Expected `O(1)` unless the implementation documents otherwise.
622    fn incidence_element(&self, incidence: Self::IncidenceId) -> Self::ElementId;
623}
624
625/// Capability for resolving the relation side of an incidence.
626///
627/// # Performance
628///
629/// Lookup should be `O(1)` unless an implementation documents a weaker
630/// contract.
631pub trait IncidenceRelation: IncidenceBase {
632    /// Returns the relation containing `incidence`.
633    ///
634    /// # Performance
635    ///
636    /// Expected `O(1)` unless the implementation documents otherwise.
637    fn incidence_relation(&self, incidence: Self::IncidenceId) -> Self::RelationId;
638}
639
640/// Capability for resolving a role attached to an incidence.
641///
642/// Roles are implementation-defined topology labels. The core never
643/// interprets them.
644///
645/// # Performance
646///
647/// Lookup should be `O(1)` unless an implementation documents a weaker
648/// contract.
649pub trait IncidenceRole: IncidenceBase {
650    /// Returns the role for `incidence`.
651    ///
652    /// # Performance
653    ///
654    /// Expected `O(1)` unless the implementation documents otherwise.
655    fn incidence_role(&self, incidence: Self::IncidenceId) -> Self::Role;
656}
657
658/// Exact relation-incidence count capability.
659///
660/// This pairs with [`RelationIncidences`] for backends that can report a
661/// relation's incidence count without traversal. The trait does not require
662/// traversal support by itself.
663///
664/// # Performance
665///
666/// Expected `O(1)` unless the implementation documents otherwise.
667pub trait RelationIncidenceCount: IncidenceBase {
668    /// Returns the number of incidences attached to `relation`.
669    ///
670    /// # Performance
671    ///
672    /// Expected `O(1)` unless the implementation documents otherwise.
673    fn relation_incidence_count(&self, relation: Self::RelationId) -> usize;
674}
675
676/// Exact element-incidence count capability.
677///
678/// This pairs with [`ElementIncidences`] for backends that can report an
679/// element's incidence count without traversal. The trait does not require
680/// traversal support by itself.
681///
682/// # Performance
683///
684/// Expected `O(1)` unless the implementation documents otherwise.
685pub trait ElementIncidenceCount: IncidenceBase {
686    /// Returns the number of incidences attached to `element`.
687    ///
688    /// # Performance
689    ///
690    /// Expected `O(1)` unless the implementation documents otherwise.
691    fn element_incidence_count(&self, element: Self::ElementId) -> usize;
692}
693
694/// Convenience trait for views that can resolve complete incidence records.
695///
696/// This trait has no methods of its own. It names the common capability bundle
697/// needed by generic code that wants to traverse relations and inspect each
698/// incidence's element, relation, and role.
699///
700/// # Performance
701///
702/// `perf: unspecified`; performance is inherited from the component traits.
703pub trait IncidenceView:
704    RelationIncidences + IncidenceElement + IncidenceRelation + IncidenceRole
705{
706}
707
708/// Blanket implementation for complete incidence views.
709///
710/// Any type that can traverse relation incidences and resolve each incidence's
711/// element, relation, and role automatically implements [`IncidenceView`].
712///
713/// # Performance
714///
715/// `perf: unspecified`; performance is inherited from the component traits.
716impl<T> IncidenceView for T where
717    T: RelationIncidences + IncidenceElement + IncidenceRelation + IncidenceRole
718{
719}
720
721/// Capability for expanding an element to its directed successor elements.
722///
723/// This is the substrate-neutral form of "follow outgoing connections from
724/// this element." For binary graphs the successors are the targets of
725/// outgoing edges; for hypergraphs they are the vertices reachable through
726/// outgoing hyperedges. Substrate-agnostic algorithms (forward BFS, forward
727/// reachability) bind on this trait so the same code drives any topology
728/// view that can answer the question.
729///
730/// Implementations define whether parallel connections produce repeated
731/// successor elements. Implementations that preserve multiplicity should
732/// document that behavior; consumers that need set semantics should
733/// deduplicate at their own level.
734///
735/// # Performance
736///
737/// Creating the iterator should be `O(1)` unless an implementation documents a
738/// weaker contract. Yielding `k` successors should be `O(k)` and should not
739/// allocate unless the implementation documents otherwise.
740pub trait ElementSuccessors: TopologyBase {
741    /// Iterator over successor element IDs reached from one element.
742    ///
743    /// # Performance
744    ///
745    /// Advancing the iterator should be amortized `O(1)` unless an
746    /// implementation documents otherwise.
747    type Successors<'view>: Iterator<Item = Self::ElementId>
748    where
749        Self: 'view;
750
751    /// Returns elements reachable through outgoing connections from `element`.
752    ///
753    /// # Performance
754    ///
755    /// Expected `O(1)` to create the iterator; yielding `k` successors is
756    /// expected `O(k)`.
757    fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_>;
758}
759
760/// Capability for expanding an element to its directed predecessor elements.
761///
762/// This is the substrate-neutral form of "follow incoming connections to this
763/// element." For binary graphs the predecessors are the sources of incoming
764/// edges; for hypergraphs they are the vertices that reach this vertex
765/// through outgoing hyperedges. Substrate-agnostic algorithms (reverse BFS,
766/// reverse reachability) bind on this trait so the same code drives any
767/// topology view that can answer the question.
768///
769/// Implementations define whether parallel connections produce repeated
770/// predecessor elements. Implementations that preserve multiplicity should
771/// document that behavior; consumers that need set semantics should
772/// deduplicate at their own level.
773///
774/// # Performance
775///
776/// Creating the iterator should be `O(1)` unless an implementation documents a
777/// weaker contract. Yielding `k` predecessors should be `O(k)` and should not
778/// allocate unless the implementation documents otherwise.
779pub trait ElementPredecessors: TopologyBase {
780    /// Iterator over predecessor element IDs reaching one element.
781    ///
782    /// # Performance
783    ///
784    /// Advancing the iterator should be amortized `O(1)` unless an
785    /// implementation documents otherwise.
786    type Predecessors<'view>: Iterator<Item = Self::ElementId>
787    where
788        Self: 'view;
789
790    /// Returns elements that reach `element` through outgoing connections.
791    ///
792    /// # Performance
793    ///
794    /// Expected `O(1)` to create the iterator; yielding `k` predecessors is
795    /// expected `O(k)`.
796    fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_>;
797}