Skip to main content

cpdag.same_markov_equivalence_class()

Return True if two graphs share a Markov equivalence class.

Usage

cpdag.same_markov_equivalence_class(
    graph1,
    graph2,
)

Each graph may be a DOT string, an object with a .source attribute (such as a graphviz.Digraph), or a networkx.DiGraph. Two graphs are Markov-equivalent when they have identical node sets, identical skeletons, and identical sets of unshielded colliders. A subgraph or anonymous { ... } block is layout only: the comparison uses the nodes and edges inside it, not the cluster id. Node identity is the DOT id (or networkx node); label attributes are not treated as identity. Attribute assignments such as rankdir=LR are not nodes. Unquoted digraph, subgraph, and strict are keywords, not node ids: quote them ("digraph" -> Y). A unicode space does not separate those keywords; write an ASCII space, as in strict digraph. node = box is rejected; write node [shape=box]. A quoted odd backslash run before a line feed is line continuation, so "foo\<newline>bar" is the node foobar. An even run is not continuation.

Parameters

graph1: str | object with .source | networkx.DiGraph

The two graphs to compare.

graph2: str | object with .source | networkx.DiGraph
The two graphs to compare.

Returns

bool
True if the graphs are Markov-equivalent, False otherwise.

Raises

TypeError

If either argument is not a DOT string, a .source object, or a networkx.DiGraph.

ValueError
If a DOT string cannot be parsed.

Examples

The chain A -> B -> C and the fork A <- B -> C are Markov-equivalent (same skeleton, no v-structure); the collider A -> B <- C is not::

from pathmc import same_markov_equivalence_class

same_markov_equivalence_class(
    "digraph { A -> B; B -> C; }",
    "digraph { B -> A; B -> C; }",
)  # True
same_markov_equivalence_class(
    "digraph { A -> B; B -> C; }",
    "digraph { A -> B; C -> B; }",
)  # False