Files
wifi-densepose/docs/research/cognitive-frontier/temporal-hypergraphs.md
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

21 KiB

Temporal Hypergraphs: Time-Varying Hyperedges with Causal Constraints

Overview

Temporal Hypergraphs extend standard hyperedges with time-varying validity, causal ordering constraints, and spike-timing based temporal reasoning. This enables modeling of complex temporal relationships where multiple entities participate in events that have duration, ordering, and causal dependencies.

Core Concept

┌─────────────────────────────────────────────────────────────────────────────┐
│                        TEMPORAL HYPERGRAPH                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  TIME ──────────────────────────────────────────────────────────────────►   │
│        t₀        t₁        t₂        t₃        t₄        t₅                │
│        │         │         │         │         │         │                 │
│  ┌─────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────┐           │
│  │                    HYPEREDGE H₁                              │           │
│  │  ┌───────────────────────────────────────┐                   │           │
│  │  │ Nodes: {A, B, C}                      │                   │           │
│  │  │ Valid: [t₀, t₃)                       │                   │           │
│  │  │ Type: "MEETING"                       │                   │           │
│  │  └───────────────────────────────────────┘                   │           │
│  │        │                                                     │           │
│  │        │ CAUSES (Δt = t₂ - t₀)                              │           │
│  │        ▼                                                     │           │
│  │  ┌───────────────────────────────────────────────────┐       │           │
│  │  │ HYPEREDGE H₂                                      │       │           │
│  │  │ Nodes: {B, D, E}                                  │       │           │
│  │  │ Valid: [t₂, t₅)                                   │       │           │
│  │  │ Type: "PROJECT"                                   │       │           │
│  │  │ Constraint: AFTER(H₁)                             │       │           │
│  │  └───────────────────────────────────────────────────┘       │           │
│  └──────────────────────────────────────────────────────────────┘           │
│                                                                             │
│  CAUSAL GRAPH:  H₁ ───causes───► H₂ ───prevents───► H₃                     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Data Structures

1. Temporal Hyperedge

// crates/ruvector-graph/src/temporal/hyperedge.rs

use chrono::{DateTime, Utc, Duration};

/// Temporal validity interval
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalInterval {
    /// Start time (inclusive)
    pub start: DateTime<Utc>,
    /// End time (exclusive), None = ongoing
    pub end: Option<DateTime<Utc>>,
    /// Validity type
    pub validity: ValidityType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidityType {
    /// Hyperedge exists during interval
    Exists,
    /// Hyperedge is valid/active during interval
    Valid,
    /// Hyperedge is scheduled for interval
    Scheduled,
    /// Hyperedge was true during interval (historical)
    Historical,
}

impl TemporalInterval {
    /// Check if interval contains a point in time
    pub fn contains(&self, t: DateTime<Utc>) -> bool {
        t >= self.start && self.end.map(|e| t < e).unwrap_or(true)
    }

    /// Check if intervals overlap
    pub fn overlaps(&self, other: &TemporalInterval) -> bool {
        let self_end = self.end.unwrap_or(DateTime::<Utc>::MAX_UTC);
        let other_end = other.end.unwrap_or(DateTime::<Utc>::MAX_UTC);

        self.start < other_end && other.start < self_end
    }

    /// Allen's interval algebra relations
    pub fn relation(&self, other: &TemporalInterval) -> AllenRelation {
        // Implement all 13 Allen relations
        // before, meets, overlaps, starts, during, finishes, equals, etc.
        todo!()
    }
}

/// Hyperedge with temporal dimension
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalHyperedge {
    /// Base hyperedge
    pub hyperedge: Hyperedge,

    /// Temporal validity intervals (can have multiple)
    pub intervals: Vec<TemporalInterval>,

    /// Causal constraints
    pub causal_constraints: Vec<CausalConstraint>,

    /// Temporal properties (can vary over time)
    pub temporal_properties: HashMap<String, TimeSeries>,

    /// Version history
    pub versions: Vec<HyperedgeVersion>,

    /// Spike-timing metadata (for SNN integration)
    pub spike_metadata: Option<SpikeMetadata>,
}

/// Causal constraint between hyperedges
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CausalConstraint {
    /// Type of causal relationship
    pub constraint_type: CausalConstraintType,
    /// Target hyperedge ID
    pub target: HyperedgeId,
    /// Minimum time delay
    pub min_delay: Option<Duration>,
    /// Maximum time delay
    pub max_delay: Option<Duration>,
    /// Causal strength (learned from SNN)
    pub strength: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CausalConstraintType {
    /// This hyperedge must come AFTER target
    After,
    /// This hyperedge must come BEFORE target
    Before,
    /// This hyperedge CAUSES target
    Causes,
    /// This hyperedge PREVENTS target
    Prevents,
    /// This hyperedge must OVERLAP with target
    Overlaps,
    /// This hyperedge must be CONTAINED in target
    During,
    /// This hyperedge ENABLES target (necessary but not sufficient)
    Enables,
}

2. Time Series Properties

/// Time-varying property value
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeries {
    /// Property name
    pub name: String,
    /// Time-value pairs
    pub points: Vec<(DateTime<Utc>, PropertyValue)>,
    /// Interpolation method
    pub interpolation: Interpolation,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Interpolation {
    /// Step function (constant until next point)
    Step,
    /// Linear interpolation
    Linear,
    /// No interpolation (only exact points)
    None,
}

impl TimeSeries {
    /// Get value at specific time
    pub fn value_at(&self, t: DateTime<Utc>) -> Option<PropertyValue> {
        match self.interpolation {
            Interpolation::Step => {
                self.points.iter()
                    .rev()
                    .find(|(pt, _)| *pt <= t)
                    .map(|(_, v)| v.clone())
            }
            Interpolation::Linear => {
                // Find surrounding points and interpolate
                let before = self.points.iter().rev().find(|(pt, _)| *pt <= t);
                let after = self.points.iter().find(|(pt, _)| *pt > t);

                match (before, after) {
                    (Some((t1, v1)), Some((t2, v2))) => {
                        // Linear interpolation
                        self.interpolate_linear(*t1, v1, *t2, v2, t)
                    }
                    (Some((_, v)), None) => Some(v.clone()),
                    (None, Some((_, v))) => Some(v.clone()),
                    (None, None) => None,
                }
            }
            Interpolation::None => {
                self.points.iter()
                    .find(|(pt, _)| *pt == t)
                    .map(|(_, v)| v.clone())
            }
        }
    }
}

3. Spike-Timing Integration

/// Spike metadata for temporal hyperedges
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpikeMetadata {
    /// Neuron ID representing this hyperedge
    pub neuron_id: usize,
    /// Spike times when hyperedge was activated
    pub spike_times: Vec<f64>,
    /// Learned causal weights to other hyperedges
    pub causal_weights: HashMap<HyperedgeId, f64>,
}

/// SNN-based temporal hypergraph analyzer
pub struct TemporalHypergraphSNN {
    /// Causal discovery SNN
    causal_snn: CausalDiscoverySNN,
    /// Mapping: hyperedge → neuron
    hyperedge_neurons: HashMap<HyperedgeId, usize>,
    /// Reverse mapping: neuron → hyperedge
    neuron_hyperedges: HashMap<usize, HyperedgeId>,
}

impl TemporalHypergraphSNN {
    /// Convert hyperedge event to spike
    pub fn hyperedge_to_spike(&self, hyperedge: &TemporalHyperedge, event_time: f64) -> Spike {
        let neuron_id = self.hyperedge_neurons
            .get(&hyperedge.hyperedge.id)
            .copied()
            .unwrap_or(0);

        Spike {
            neuron_id,
            time: event_time,
        }
    }

    /// Learn causal relationships from spike timing
    pub fn learn_causality(&mut self, spikes: &[Spike]) {
        for spike in spikes {
            let event = GraphEvent {
                event_type: GraphEventType::HyperedgeActivation,
                vertex: None,
                edge: None,
                data: spike.neuron_id as f64,
            };
            self.causal_snn.observe_event(event, spike.time);
        }
    }

    /// Extract learned causal graph between hyperedges
    pub fn extract_hyperedge_causality(&self) -> HashMap<(HyperedgeId, HyperedgeId), f64> {
        let causal_graph = self.causal_snn.extract_causal_graph();
        let mut result = HashMap::new();

        for edge in causal_graph.edges() {
            if let (Some(src_he), Some(tgt_he)) = (
                self.neuron_hyperedges.get(&edge.source),
                self.neuron_hyperedges.get(&edge.target),
            ) {
                result.insert((src_he.clone(), tgt_he.clone()), edge.strength);
            }
        }

        result
    }
}

Temporal Query Language

4. Extended Cypher for Temporal Hypergraphs

/// Temporal Cypher query extensions
pub enum TemporalCypherExtension {
    /// Query at specific time point
    /// MATCH (n)-[r]->(m) AT TIME '2024-01-15T10:00:00Z'
    AtTime(DateTime<Utc>),

    /// Query during interval
    /// MATCH (n)-[r]->(m) DURING ['2024-01-01', '2024-02-01']
    During(TemporalInterval),

    /// Query with temporal ordering
    /// MATCH (h1:Hyperedge) BEFORE (h2:Hyperedge)
    Before(HyperedgePattern, HyperedgePattern),

    /// Query causal relationships
    /// MATCH (h1:Hyperedge) CAUSES (h2:Hyperedge)
    Causes(HyperedgePattern, HyperedgePattern),

    /// Query temporal evolution
    /// MATCH (n) EVOLVES FROM '2024-01-01' TO '2024-12-31'
    Evolves { start: DateTime<Utc>, end: DateTime<Utc> },

    /// Query with Allen relations
    /// MATCH (h1) OVERLAPS (h2)
    AllenRelation(AllenRelation, HyperedgePattern, HyperedgePattern),
}

/// Temporal query executor
pub struct TemporalCypherExecutor {
    graph: TemporalHypergraphDB,
    snn: TemporalHypergraphSNN,
}

impl TemporalCypherExecutor {
    /// Execute temporal Cypher query
    pub fn execute(&self, query: &str) -> Result<TemporalQueryResult> {
        let parsed = self.parse_temporal_cypher(query)?;

        match parsed.temporal_extension {
            Some(TemporalCypherExtension::AtTime(t)) => {
                self.execute_at_time(&parsed.base_query, t)
            }
            Some(TemporalCypherExtension::Causes(src, tgt)) => {
                self.execute_causal_query(&src, &tgt)
            }
            Some(TemporalCypherExtension::Evolves { start, end }) => {
                self.execute_evolution_query(&parsed.base_query, start, end)
            }
            _ => self.execute_base_query(&parsed.base_query),
        }
    }

    /// Execute causal query using SNN
    fn execute_causal_query(
        &self,
        source: &HyperedgePattern,
        target: &HyperedgePattern,
    ) -> Result<TemporalQueryResult> {
        // Find matching hyperedges
        let source_hyperedges = self.graph.match_pattern(source)?;
        let target_hyperedges = self.graph.match_pattern(target)?;

        // Query causal relationships from SNN
        let causality = self.snn.extract_hyperedge_causality();

        let mut results = Vec::new();
        for src in &source_hyperedges {
            for tgt in &target_hyperedges {
                if let Some(&strength) = causality.get(&(src.id.clone(), tgt.id.clone())) {
                    if strength > 0.1 {
                        results.push(CausalMatch {
                            source: src.clone(),
                            target: tgt.clone(),
                            strength,
                            delay: self.estimate_causal_delay(src, tgt),
                        });
                    }
                }
            }
        }

        Ok(TemporalQueryResult::Causal(results))
    }
}

5. Example Queries

-- Find all meetings that caused project formations
MATCH (meeting:Hyperedge {type: 'MEETING'})
      CAUSES
      (project:Hyperedge {type: 'PROJECT'})
WHERE meeting.duration > 60
RETURN meeting, project, causal_strength, causal_delay

-- Find hyperedges valid during Q1 2024
MATCH (h:Hyperedge)
DURING ['2024-01-01', '2024-03-31']
RETURN h.type, count(*) AS count

-- Trace temporal evolution of a relationship
MATCH (team:Hyperedge {id: 'team-alpha'})
EVOLVES FROM '2024-01-01' TO '2024-12-31'
RETURN timestamp, team.members, team.confidence

-- Find overlapping events
MATCH (h1:Hyperedge {type: 'MEETING'})
      OVERLAPS
      (h2:Hyperedge {type: 'MEETING'})
WHERE h1 <> h2
RETURN h1, h2, overlap_duration

-- Counterfactual query
MATCH (cause:Hyperedge)
      CAUSES
      (effect:Hyperedge {outcome: 'failure'})
COUNTERFACTUAL NOT cause
PREDICT effect.outcome

MinCut Integration

6. Temporal MinCut

/// MinCut on temporal hypergraphs
pub struct TemporalMinCut {
    /// Static MinCut analyzer
    static_analyzer: RuVectorGraphAnalyzer,
    /// Time window for snapshot
    window: Duration,
}

impl TemporalMinCut {
    /// Compute MinCut at specific time
    pub fn mincut_at(&self, t: DateTime<Utc>) -> u64 {
        // Build snapshot graph at time t
        let snapshot = self.build_snapshot(t);

        let mut analyzer = RuVectorGraphAnalyzer::new(snapshot);
        analyzer.min_cut()
    }

    /// Compute MinCut evolution over time
    pub fn mincut_evolution(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
        step: Duration,
    ) -> Vec<(DateTime<Utc>, u64)> {
        let mut results = Vec::new();
        let mut t = start;

        while t <= end {
            results.push((t, self.mincut_at(t)));
            t = t + step;
        }

        results
    }

    /// Find time of minimum connectivity
    pub fn find_vulnerability_window(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
    ) -> Option<TemporalInterval> {
        let evolution = self.mincut_evolution(start, end, Duration::hours(1));

        // Find minimum mincut value
        let min_idx = evolution.iter()
            .enumerate()
            .min_by_key(|(_, (_, v))| *v)?
            .0;

        // Expand window around minimum
        let window_start = evolution.get(min_idx.saturating_sub(1))
            .map(|(t, _)| *t)
            .unwrap_or(start);
        let window_end = evolution.get(min_idx + 1)
            .map(|(t, _)| *t);

        Some(TemporalInterval {
            start: window_start,
            end: window_end,
            validity: ValidityType::Valid,
        })
    }

    /// Build graph snapshot at time t
    fn build_snapshot(&self, t: DateTime<Utc>) -> Arc<DynamicGraph> {
        let graph = Arc::new(DynamicGraph::new());

        // Add only hyperedges valid at time t
        for hyperedge in self.get_valid_hyperedges(t) {
            // Convert hyperedge to clique in graph
            for i in 0..hyperedge.hyperedge.nodes.len() {
                for j in (i + 1)..hyperedge.hyperedge.nodes.len() {
                    let u = self.node_to_vertex(&hyperedge.hyperedge.nodes[i]);
                    let v = self.node_to_vertex(&hyperedge.hyperedge.nodes[j]);
                    let _ = graph.insert_edge(u, v, hyperedge.hyperedge.confidence as f64);
                }
            }
        }

        graph
    }
}

7. Causal MinCut

/// MinCut on the causal graph between hyperedges
pub struct CausalMinCut {
    snn: TemporalHypergraphSNN,
}

impl CausalMinCut {
    /// Find minimum intervention to prevent an outcome
    pub fn minimum_intervention(
        &self,
        source_hyperedges: &[HyperedgeId],
        target_outcome: &HyperedgeId,
    ) -> Vec<HyperedgeId> {
        // Build causal graph
        let causality = self.snn.extract_hyperedge_causality();
        let causal_graph = self.build_causal_graph(&causality);

        // Compute MinCut between sources and target
        let mut analyzer = RuVectorGraphAnalyzer::new(causal_graph);
        let (side_a, side_b) = analyzer.partition().unwrap();

        // Find hyperedges in the cut
        let cut_hyperedges = self.find_cut_hyperedges(&side_a, &side_b);

        cut_hyperedges
    }

    /// Find critical causal paths
    pub fn critical_causal_paths(
        &self,
        outcome: &HyperedgeId,
    ) -> Vec<CausalPath> {
        let causality = self.snn.extract_hyperedge_causality();

        // Trace back all causal paths to outcome
        let mut paths = Vec::new();
        self.trace_causal_paths(outcome, &causality, &mut Vec::new(), &mut paths);

        // Rank by causal strength
        paths.sort_by(|a, b| b.total_strength.partial_cmp(&a.total_strength).unwrap());

        paths
    }
}

Implementation Phases

Phase 1: Core Data Structures

  • Implement TemporalInterval with Allen's algebra
  • Create TemporalHyperedge structure
  • Add TimeSeries for temporal properties
  • Implement version history

Phase 2: Storage and Indexing

  • Create temporal index (B-tree on intervals)
  • Implement efficient time-range queries
  • Add versioned storage backend
  • Create snapshot generation

Phase 3: SNN Integration

  • Map hyperedges to neurons
  • Implement spike-timing causal learning
  • Create causal constraint inference
  • Add strength estimation

Phase 4: Query Language

  • Extend Cypher parser for temporal operators
  • Implement AT TIME queries
  • Add CAUSES/PREVENTS queries
  • Create evolution queries

Phase 5: MinCut Integration

  • Implement temporal MinCut snapshots
  • Add MinCut evolution tracking
  • Create causal MinCut for interventions
  • Build vulnerability detection

Key Metrics

Metric Target Description
Temporal Query Latency < 50ms Time-range query performance
Snapshot Generation < 10ms Build graph at point in time
Causal Inference < 100ms Learn causality from spikes
MinCut Evolution < 1s Compute MinCut over time range

Novel Research Contributions

  1. Spike-Timing Hyperedge Causality: Using STDP to learn causal relationships between N-ary events
  2. Temporal MinCut: Extending subpolynomial MinCut to time-varying graphs
  3. Causal Intervention Planning: Using MinCut to find minimum changes to prevent outcomes
  4. Allen Algebra + Causality: Combining temporal logic with causal constraints

Use Cases

1. Event Sequence Analysis

Model complex events (meetings, projects, decisions) as temporal hyperedges and discover causal chains.

2. Temporal Knowledge Graphs

Represent facts that change over time with automatic causality detection.

3. Process Mining

Analyze business processes as temporal hypergraphs with causal constraints.

4. Predictive Maintenance

Model system states as hyperedges and predict failures through causal analysis.

5. Social Network Dynamics

Track group formations/dissolutions over time with causal explanations.

References

  • Allen, J. (1983). Maintaining Knowledge about Temporal Intervals
  • Maier, D. (2010). Semantics of Temporal Queries
  • Pearl, J. (2009). Causality: Models, Reasoning, and Inference
  • Sporns, O. (2010). Networks of the Brain