Graph Operations
Names also exported by DAGMakie (find_backdoor_paths, find_directed_paths, is_dag) are written fully qualified so the docs build resolves them unambiguously when the plotting extension is loaded.
Paths and ancestry
CausalDynamics.d_separated — Function
d_separated(g, X, Y, Z)Check if nodes X and Y are d-separated by set Z in directed acyclic graph g.
Delegates to CausalInference.dsep (SimonAB fork under CDCS packages/CausalInference.jl).
Arguments
g: A directed acyclic graph (DiGraph)X: Source node or set of nodesY: Target node or set of nodesZ: Conditioning set (vector, set, or single node)
Returns
trueif X and Y are d-separated by Z,falseotherwise
CausalDynamics.get_ancestors — Function
get_ancestors(g, nodes)Get the set of all ancestors of the given nodes.
An ancestor of node X is any node that has a directed path to X.
Arguments
g: Directed acyclic graphnodes: Node or set of nodes
Returns
- Set of ancestor nodes
Examples
using CausalDynamics, Graphs
g = DiGraph(4)
add_edge!(g, 1, 3) # X → Z
add_edge!(g, 2, 3) # Y → Z
add_edge!(g, 3, 4) # Z → W
ancestors = get_ancestors(g, 4) # {1, 2, 3}CausalDynamics.get_descendants — Function
get_descendants(g, nodes)Get the set of all descendants of the given nodes.
A descendant of node X is any node that has a directed path from X.
Arguments
g: Directed acyclic graphnodes: Node or set of nodes
Returns
- Set of descendant nodes
Examples
using CausalDynamics, Graphs
g = DiGraph(4)
add_edge!(g, 1, 2) # X → Y
add_edge!(g, 1, 3) # X → Z
add_edge!(g, 2, 4) # Y → W
descendants = get_descendants(g, 1) # {2, 3, 4}CausalDynamics.get_parents — Function
get_parents(g, nodes)Get the set of parents of the given nodes.
A parent of node X is a node with a direct edge pointing into X.
Arguments
g::AbstractGraph: Directed acyclic graphnodes: Node (Int) or collection of nodes (Vector/Set)
Returns
Set{Int}: Set of parent nodes
Examples
using CausalDynamics, Graphs
g = DiGraph(4)
add_edge!(g, 1, 3) # X → Z
add_edge!(g, 2, 3) # Y → Z
add_edge!(g, 3, 4) # Z → W
parents = get_parents(g, 3) # {1, 2}CausalDynamics.get_children — Function
get_children(g, nodes)Get the set of children of the given nodes.
A child of node X is a node with a direct edge pointing from X.
Arguments
g::AbstractGraph: Directed acyclic graphnodes: Node (Int) or collection of nodes (Vector/Set)
Returns
Set{Int}: Set of child nodes
Examples
using CausalDynamics, Graphs
g = DiGraph(4)
add_edge!(g, 1, 2) # X → Y
add_edge!(g, 1, 3) # X → Z
add_edge!(g, 2, 4) # Y → W
children = get_children(g, 1) # {2, 3}CausalDynamics.markov_boundary — Function
markov_boundary(g, Y)Compute the Markov boundary of node Y.
The Markov boundary of Y is the minimal set that d-separates Y from all other nodes. It consists of: parents of Y, children of Y, and parents of children of Y.
Arguments
g: Directed acyclic graphY: Target node
Returns
- Set of nodes in the Markov boundary
Examples
using CausalDynamics, Graphs
g = DiGraph(4)
add_edge!(g, 1, 2) # X → Y
add_edge!(g, 3, 2) # Z → Y
add_edge!(g, 2, 4) # Y → W
add_edge!(g, 5, 4) # V → W
mb = markov_boundary(g, 2) # {1, 3, 4, 5}
# Parents: {1, 3}
# Children: {4}
# Parents of children: {5}CausalDynamics.find_backdoor_paths — Function
find_backdoor_paths(g, X, Y; max_paths=10_000)Find all backdoor paths from X to Y.
A backdoor path is a path that starts with an edge pointing into X.
Arguments
g: Directed acyclic graphX: Source nodeY: Target nodemax_paths: Cap on enumerated paths (raises if exceeded)
Returns
- Vector of backdoor paths (each path is a vector of nodes)
CausalDynamics.find_directed_paths — Function
find_directed_paths(g, X, Y; max_paths=10_000)Find all directed paths from X to Y (forward edges only).
Prefer nodes_on_directed_paths or has_path when only membership / existence is needed — full enumeration is exponential on dense DAGs.
CausalDynamics.nodes_on_directed_paths — Function
nodes_on_directed_paths(g, X, Y) -> Set{Int}Nodes that lie on at least one directed path from X to Y (including endpoints).
On a DAG this is ({X} ∪ descendants(X)) ∩ ({Y} ∪ ancestors(Y)) restricted to nodes reachable from X toward Y — computed via BFS, not path enumeration.
CausalDynamics.has_path — Function
has_path(g, source, target)Check if there exists a directed path from source to target.
A directed path follows edges in their forward direction only.
Arguments
g::AbstractGraph: Directed acyclic graphsource::Int: Source nodetarget::Int: Target node
Returns
Bool:trueif a directed path exists,falseotherwise
Examples
using CausalDynamics, Graphs
g = DiGraph(3)
add_edge!(g, 1, 2) # X → Y
add_edge!(g, 2, 3) # Y → Z
has_path(g, 1, 3) # true (path: 1 → 2 → 3)
has_path(g, 3, 1) # false (no reverse path)Notes
- Returns
falseif source == target (no self-loops) - Uses BFS reachability (not path enumeration)
CausalDynamics.is_dag — Function
is_dag(g)Check if graph g is a directed acyclic graph (DAG).
A DAG is a directed graph with no cycles (no path from a node back to itself).
Arguments
g::AbstractGraph: Directed graph to test
Returns
Bool:trueif g is a DAG,falseotherwise
Examples
using CausalDynamics, Graphs
# Valid DAG
g1 = DiGraph(3)
add_edge!(g1, 1, 2)
add_edge!(g1, 2, 3)
is_dag(g1) # true
# Contains cycle
g2 = DiGraph(3)
add_edge!(g2, 1, 2)
add_edge!(g2, 2, 3)
add_edge!(g2, 3, 1) # Cycle: 1 → 2 → 3 → 1
is_dag(g2) # falseNotes
- Uses topological sort to detect cycles
- Returns
falseif topological sort fails (indicates cycle) - Causal graphs must be DAGs (no causal loops)
See Also
validate_causal_graph: Validate and throw error if not DAG
CausalDynamics.validate_causal_graph — Function
validate_causal_graph(g)Validate that graph g is a valid causal graph (DAG).
Causal graphs must be directed acyclic graphs (DAGs) to represent well-defined causal relationships without circular dependencies.
Arguments
g::AbstractGraph: Directed graph to validate
Returns
Bool:trueif valid (always returns true, throws on error)
Throws
ArgumentError: If graph is not a DAG (contains cycles)
Examples
using CausalDynamics, Graphs
# Valid DAG
g1 = DiGraph(3)
add_edge!(g1, 1, 2)
add_edge!(g1, 2, 3)
validate_causal_graph(g1) # true
# Invalid: contains cycle
g2 = DiGraph(3)
add_edge!(g2, 1, 2)
add_edge!(g2, 2, 3)
add_edge!(g2, 3, 1)
validate_causal_graph(g2) # throws ArgumentErrorNotes
- Used internally by identification functions to ensure graph validity
- Causal models require DAGs to avoid circular causal dependencies
- Throws error rather than returning false for clearer error messages
See Also
is_dag: Check if graph is DAG (returns boolean)
CausalDynamics.create_causal_graph — Function
create_causal_graph(edges)Create a causal graph from a list of edges.
Convenience function to create a validated DAG from edge specifications. Automatically determines graph size and validates that the result is a DAG.
Arguments
edges: Edge specification, either:Vector{Tuple{Int, Int}}: List of (source, target) tuplesDict{Int, Vector{Int}}: Dictionary mapping source nodes to vectors of target nodes
Returns
DiGraph: Validated directed acyclic graph
Examples
using CausalDynamics
# From edge list: Z → X, Z → Y, X → Y
edges = [(1, 2), (1, 3), (2, 3)]
g = create_causal_graph(edges)
# From dictionary (same graph)
edge_dict = Dict(
1 => [2, 3], # Z → X, Z → Y
2 => [3] # X → Y
)
g = create_causal_graph(edge_dict)Throws
ArgumentError: If the resulting graph is not a DAG (contains cycles)
Notes
- Automatically determines number of nodes from edge specifications
- Validates that graph is acyclic before returning
- Node indices start at 1
See Also
validate_causal_graph: Validate that a graph is a DAGis_dag: Check if a graph is acyclic
CausalGraph metadata
Optional property bag and attached data on a causal graph wrapper.
CausalDynamics.CausalGraph — Type
CausalGraphA causal graph with attached properties (node names, data, metadata).
Extends DiGraph with property storage (node names, attached data, metadata). Integrates with TMLE.jl, Turing.jl, and RxInfer.jl for estimation after identification.
Fields
graph::DiGraph: The underlying directed acyclic graph structurenode_props::Dict{Int, Dict{Symbol, Any}}: Properties attached to nodesedge_props::Dict{Tuple{Int, Int}, Dict{Symbol, Any}}: Properties attached to edgesgraph_props::Dict{Symbol, Any}: Graph-level properties (e.g., data, metadata)
Examples
using CausalDynamics
# Create a causal graph with properties
g = CausalGraph(3)
add_edge!(g, 1, 2) # Z → X
add_edge!(g, 1, 3) # Z → Y
add_edge!(g, 2, 3) # X → Y
# Attach node names
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 2, :name, :X)
set_node_prop!(g, 3, :name, :Y)
# Attach data (DataFrame, NamedTuple, or other table format)
# using DataFrames # Optional - only needed if using DataFrames
# data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
# set_prop!(g, :data, data)
# Or use attach_data! convenience function
# attach_data!(g, data)
# Access properties
get_node_prop(g, 1, :name) # :Z
get_prop(g, :data) # DataFrameNotes
- All
Graphs.jloperations work onCausalGraph(delegated to underlying graph) - Properties are stored efficiently using nested dictionaries
- Compatible with GraphMakie.jl for visualisation (subtype of
AbstractGraph) - Designed for integration with probabilistic programming languages (Turing, RxInfer)
See Also
set_prop!: Set graph-level propertiesget_prop: Get graph-level propertiesset_node_prop!: Set node propertiesget_node_prop: Get node propertiesattach_data!: Convenience function to attach data with node names
CausalDynamics.attach_data! — Function
attach_data!(g, data; node_names=nothing, check_columns=true)Attach data to a causal graph with optional column name checking.
Designed for integration with statistical estimation packages:
- TMLE.jl: Uses DataFrames with named columns
- Turing.jl: Uses DataFrames or NamedTuples
- RxInfer.jl: Uses various table formats
Arguments
g::CausalGraph: The causal graphdata: Data to attach (DataFrame, NamedTuple, or other table format)node_names::Union{Dict{Int, Symbol}, Nothing}: Optional mapping from node indices to column names. If not provided, usesget_node_names(g).check_columns::Bool: Iftrue, verify that data columns match node names (default:true)
Returns
CausalGraph: The graph with data attached (for method chaining)
Examples
using CausalDynamics, DataFrames
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 2, :name, :X)
set_node_prop!(g, 3, :name, :Y)
data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
attach_data!(g, data) # Automatically uses node names
# Or specify node names explicitly
attach_data!(g, data; node_names=Dict(1 => :Z, 2 => :X, 3 => :Y))Notes
- Data is stored in graph property
:data - Node names are used to map graph nodes to data columns
- Column checking ensures data columns match expected node names
- Compatible with probabilistic programming packages (Turing, RxInfer)
See Also
get_data: Get attached datahas_data: Check if data is attachedget_node_names: Get node names
CausalDynamics.get_data — Function
get_data(g)Get the attached data from a causal graph.
Arguments
g::CausalGraph: The causal graph
Returns
- Attached data (DataFrame, NamedTuple, or other), or
nothingif no data attached
Examples
g = CausalGraph(3)
data = DataFrame(Z=randn(100), X=rand([0,1], 100), Y=randn(100))
attach_data!(g, data)
retrieved_data = get_data(g) # Returns DataFrameSee Also
attach_data!: Attach data to graphhas_data: Check if data is attached
CausalDynamics.has_data — Function
has_data(g)Check if data is attached to a causal graph.
Arguments
g::CausalGraph: The causal graph
Returns
Bool:trueif data is attached,falseotherwise
See Also
get_data: Get attached dataattach_data!: Attach data to graph
CausalDynamics.get_node_names — Function
get_node_names(g)Get node names as a dictionary mapping node indices to symbols.
Arguments
g::CausalGraph: The causal graph
Returns
Dict{Int, Symbol}: Mapping from node indices to names, or empty dict if no names set
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 2, :name, :X)
set_node_prop!(g, 3, :name, :Y)
names = get_node_names(g) # Dict(1 => :Z, 2 => :X, 3 => :Y)See Also
set_node_prop!: Set node nameget_node_name: Get name for single node
CausalDynamics.get_node_name — Function
get_node_name(g, node, default=nothing)Get the name for a specific node.
Arguments
g::CausalGraph: The causal graphnode::Int: Node indexdefault: Default value if name not set (default:nothing)
Returns
Symbol: Node name, ordefaultif not set
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
name = get_node_name(g, 1) # Returns :ZSee Also
get_node_names: Get all node namesset_node_prop!: Set node name
CausalDynamics.get_prop — Function
get_prop(g, key, default=nothing)Get a graph-level property.
Arguments
g::CausalGraph: The causal graphkey::Symbol: Property keydefault: Default value if property doesn't exist (default:nothing)
Returns
- Property value, or
defaultif not found
Examples
g = CausalGraph(3)
set_prop!(g, :data, my_dataframe)
data = get_prop(g, :data) # Returns DataFrame
missing_prop = get_prop(g, :nonexistent, :default) # Returns :defaultSee Also
set_prop!: Set graph-level propertyhas_prop: Check if property exists
CausalDynamics.set_prop! — Function
set_prop!(g, key, value)Set a graph-level property.
Arguments
g::CausalGraph: The causal graphkey::Symbol: Property keyvalue: Property value (any type)
Examples
g = CausalGraph(3)
set_prop!(g, :data, my_dataframe)
set_prop!(g, :description, "Confounding example")See Also
get_prop: Get graph-level propertyhas_prop: Check if property exists
CausalDynamics.has_prop — Function
has_prop(g, key)Check if a graph-level property exists.
Arguments
g::CausalGraph: The causal graphkey::Symbol: Property key
Returns
Bool:trueif property exists,falseotherwise
Examples
g = CausalGraph(3)
set_prop!(g, :data, my_dataframe)
has_prop(g, :data) # true
has_prop(g, :nonexistent) # falseSee Also
set_prop!: Set graph-level propertyget_prop: Get graph-level property
CausalDynamics.delete_prop! — Function
delete_prop!(g, key)Delete a graph-level property.
Arguments
g::CausalGraph: The causal graphkey::Symbol: Property key to delete
Returns
CausalGraph: The graph (for method chaining)
Examples
g = CausalGraph(3)
set_prop!(g, :data, my_data)
delete_prop!(g, :data) # Remove data propertySee Also
set_prop!: Set graph-level propertyget_prop: Get graph-level property
CausalDynamics.get_node_prop — Function
get_node_prop(g, node, key, default=nothing)Get a property for a specific node.
Arguments
g::CausalGraph: The causal graphnode::Int: Node indexkey::Symbol: Property keydefault: Default value if property doesn't exist (default:nothing)
Returns
- Property value, or
defaultif not found
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
name = get_node_prop(g, 1, :name) # Returns :Z
type = get_node_prop(g, 1, :type, :unknown) # Returns :unknownSee Also
set_node_prop!: Set node propertyhas_node_prop: Check if node property exists
CausalDynamics.set_node_prop! — Function
set_node_prop!(g, node, key, value)Set a property for a specific node.
Arguments
g::CausalGraph: The causal graphnode::Int: Node indexkey::Symbol: Property keyvalue: Property value (any type)
Throws
ArgumentError: If node index is out of range
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
set_node_prop!(g, 1, :type, :confounder)See Also
get_node_prop: Get node propertyhas_node_prop: Check if node property existsdelete_node_prop!: Delete node property
CausalDynamics.has_node_prop — Function
has_node_prop(g, node, key)Check if a property exists for a specific node.
Arguments
g::CausalGraph: The causal graphnode::Int: Node indexkey::Symbol: Property key
Returns
Bool:trueif property exists,falseotherwise
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
has_node_prop(g, 1, :name) # true
has_node_prop(g, 1, :type) # falseSee Also
set_node_prop!: Set node propertyget_node_prop: Get node property
CausalDynamics.delete_node_prop! — Function
delete_node_prop!(g, node, key)Delete a property for a specific node.
Arguments
g::CausalGraph: The causal graphnode::Int: Node indexkey::Symbol: Property key to delete
Returns
CausalGraph: The graph (for method chaining)
Examples
g = CausalGraph(3)
set_node_prop!(g, 1, :name, :Z)
delete_node_prop!(g, 1, :name) # Remove name propertySee Also
set_node_prop!: Set node propertyget_node_prop: Get node property
CausalDynamics.get_edge_prop — Function
get_edge_prop(g, src, dst, key, default=nothing)Get a property for a specific edge.
Arguments
g::CausalGraph: The causal graphsrc::Int: Source node indexdst::Int: Destination node indexkey::Symbol: Property keydefault: Default value if property doesn't exist (default:nothing)
Returns
- Property value, or
defaultif not found
Examples
g = CausalGraph(3)
add_edge!(g, 1, 2)
set_edge_prop!(g, 1, 2, :weight, 0.5)
weight = get_edge_prop(g, 1, 2, :weight) # Returns 0.5See Also
set_edge_prop!: Set edge propertyhas_edge_prop: Check if edge property exists
CausalDynamics.set_edge_prop! — Function
set_edge_prop!(g, src, dst, key, value)Set a property for a specific edge.
Arguments
g::CausalGraph: The causal graphsrc::Int: Source node indexdst::Int: Destination node indexkey::Symbol: Property keyvalue: Property value (any type)
Throws
ArgumentError: If edge doesn't exist in the graph
Examples
g = CausalGraph(3)
add_edge!(g, 1, 2)
set_edge_prop!(g, 1, 2, :weight, 0.5)
set_edge_prop!(g, 1, 2, :label, "causes")See Also
get_edge_prop: Get edge propertyhas_edge_prop: Check if edge property existsdelete_edge_prop!: Delete edge property
CausalDynamics.has_edge_prop — Function
has_edge_prop(g, src, dst, key)Check if a property exists for a specific edge.
Arguments
g::CausalGraph: The causal graphsrc::Int: Source node indexdst::Int: Destination node indexkey::Symbol: Property key
Returns
Bool:trueif property exists,falseotherwise
See Also
set_edge_prop!: Set edge propertyget_edge_prop: Get edge property
CausalDynamics.delete_edge_prop! — Function
delete_edge_prop!(g, src, dst, key)Delete a property for a specific edge.
Arguments
g::CausalGraph: The causal graphsrc::Int: Source node indexdst::Int: Destination node indexkey::Symbol: Property key to delete
Returns
CausalGraph: The graph (for method chaining)
Examples
g = CausalGraph(3)
add_edge!(g, 1, 2)
set_edge_prop!(g, 1, 2, :weight, 0.5)
delete_edge_prop!(g, 1, 2, :weight) # Remove weight propertySee Also
set_edge_prop!: Set edge propertyget_edge_prop: Get edge property
Hypergraphs
Higher-order edges; not used by identify or CDM simulation (see Scope).
CausalDynamics.Hypergraph — Type
HypergraphExperimental. A hypergraph with vertices, edges, and hyperedges for modeling higher-order interactions. Not wired into identify / backdoor / CDM pipelines; prefer SimpleDiGraph for identification and simulation.
In causal modelling, hypergraphs can represent:
- Group interventions: Multiple variables set or affected simultaneously (
do(·)on a set) - Multi-way interactions: Effects involving more than two variables
- Collective behaviour: Several variables linked by a shared hyperedge
Fields
graph::SimpleDiGraph: The underlying directed graph for pairwise edgeshyperedges::Dict{Int, HyperedgeData}: Hyperedges (id => HyperedgeData)next_hyperedge_id::Int: Next available hyperedge IDvertex_to_hyperedges::Dict{Int, Set{Int}}: Index: vertex -> set of hyperedge IDs
Constructors
Hypergraph(n::Int=0)Create a hypergraph with n vertices and no edges or hyperedges.
Hypergraph(g::SimpleDiGraph)Create a hypergraph from an existing directed graph, preserving its edges.
Examples
using CausalDynamics
# Create a hypergraph with 5 vertices
hg = Hypergraph(5)
# Add pairwise edges (using Graphs.jl functions)
add_edge!(hg, 1, 2) # X → Y
add_edge!(hg, 2, 3) # Y → Z
# Add a hyperedge representing a group interaction
# e.g., a meeting where agents 1, 2, 3 all interact
meeting = add_hyperedge!(hg, [1, 2, 3])
# Query the structure
num_vertices(hg) # 5
num_hyperedges(hg) # 1
hyperedge_vertices(hg, meeting) # Set([1, 2, 3])
# Find all hyperedges containing vertex 2
incident_hyperedges(hg, 2) # Set([meeting])
# Remove a hyperedge (intervention: ban the meeting)
rem_hyperedge!(hg, meeting)See Also
add_hyperedge!: Add a hyperedgerem_hyperedge!: Remove a hyperedgehyperedge_vertices: Get vertices in a hyperedgeincident_hyperedges: Get hyperedges containing a vertex
CausalDynamics.HyperedgeData — Type
HyperedgeDataA hyperedge connecting multiple vertices simultaneously.
Unlike edges (which connect exactly 2 vertices), hyperedges can connect any number of vertices, representing group interactions where all members interact simultaneously.
Fields
id::Int: Unique identifier for this hyperedgevertices::Set{Int}: Set of vertex IDs in this hyperedge
CausalDynamics.add_hyperedge! — Function
add_hyperedge!(hg::Hypergraph, vertex_ids) -> IntAdd a hyperedge connecting multiple vertices. Returns the hyperedge ID.
A hyperedge represents a group interaction where all vertices interact simultaneously. Unlike edges (which are pairwise), hyperedges can connect any number of vertices.
Arguments
hg::Hypergraph: The hypergraph to modifyvertex_ids: Collection of vertex IDs (Vector, Set, or Tuple)
Returns
Int: The ID of the newly created hyperedge
Examples
hg = Hypergraph(5)
# Group meeting with agents 1, 2, 3
meeting_id = add_hyperedge!(hg, [1, 2, 3])
# Coalition of agents 2, 4, 5
coalition_id = add_hyperedge!(hg, Set([2, 4, 5]))CausalDynamics.rem_hyperedge! — Function
rem_hyperedge!(hg::Hypergraph, id::Int)Remove a hyperedge by its ID.
This represents an intervention on the hypergraph structure: $do(H \leftarrow H^*)$ where we modify the set of group interactions.
Examples
hg = Hypergraph(5)
meeting = add_hyperedge!(hg, [1, 2, 3])
# Intervention: ban the meeting
rem_hyperedge!(hg, meeting)CausalDynamics.hyperedges — Function
hyperedges(hg::Hypergraph)Return an iterator over all hyperedges in the hypergraph.
CausalDynamics.hyperedge_vertices — Function
hyperedge_vertices(hg::Hypergraph, id::Int) -> Set{Int}Return the set of vertices in the specified hyperedge.
Examples
hg = Hypergraph(5)
meeting = add_hyperedge!(hg, [1, 2, 3])
hyperedge_vertices(hg, meeting) # Set([1, 2, 3])CausalDynamics.incident_hyperedges — Function
incident_hyperedges(hg::Hypergraph, v::Int) -> Set{Int}Return the set of hyperedge IDs that contain vertex v.
This is useful for finding all group interactions that a particular vertex (e.g., an agent) participates in.
Examples
hg = Hypergraph(5)
meeting1 = add_hyperedge!(hg, [1, 2, 3]) # Agent 2 in meeting 1
meeting2 = add_hyperedge!(hg, [2, 4, 5]) # Agent 2 in meeting 2
incident_hyperedges(hg, 2) # Set([meeting1, meeting2])
incident_hyperedges(hg, 1) # Set([meeting1])CausalDynamics.num_vertices — Function
num_vertices(hg::Hypergraph) -> IntReturn the number of vertices in the hypergraph.
CausalDynamics.num_hyperedges — Function
num_hyperedges(hg::Hypergraph) -> IntReturn the number of hyperedges in the hypergraph.
CausalDynamics.to_simple_graph — Function
to_simple_graph(hg::Hypergraph) -> SimpleDiGraphReturn the underlying directed graph (pairwise edges only).
This is useful for applying standard graph algorithms that don't support hyperedges.