aGrUM 3.0.0
a C++ library for (probabilistic) graphical models
gum::CausalModel< GUM_SCALAR > Class Template Reference

A causal model pairing an observational BayesNet with a causal DAG. More...

#include <causalModel.h>

Collaboration diagram for gum::CausalModel< GUM_SCALAR >:

Public Member Functions

 CausalModel ()=delete
 Default constructor disabled: a causal model must be built from a BN.
 CausalModel (const BayesNet< GUM_SCALAR > &observationalBN)
 Construct a causal model with no latent variables.
 CausalModel (const BayesNet< GUM_SCALAR > &observationalBN, const LatentDescriptorVector &latentVarsDescriptor, bool assumeNonSpurious=false)
 Construct a causal model and add a list of latent confounders.
 CausalModel (const CausalModel &other)
 Copy constructor.
 CausalModel (CausalModel &&other) noexcept
 Move constructor.
 ~CausalModel ()
 Destructor.
CausalModeloperator= (const CausalModel &other)=default
 Copy assignment.
CausalModeloperator= (CausalModel &&other) noexcept=default
 Move assignment.
void addLatentVariable (std::string_view latentName, const std::vector< std::string > &childrenOfLatent, bool assumeNonSpurious=false)
 Add a latent variable by names of its observed children.
void addLatentVariable (std::string_view latentName, const std::vector< NodeId > &childrenOfLatent, bool assumeNonSpurious=false)
 Add a latent variable by **NodeId**s of its observed children.
bool existsArc (NodeId x, NodeId y) const
 Whether a causal arc x → y exists (by ids) in the causal DAG.
bool existsArc (std::string_view x, std::string_view y) const
 Whether a causal arc x → y exists (by names) in the causal DAG.
void assumeSpurious (NodeId x, NodeId y)
 Mark an arc as spurious: present in observationalBN, removed from causalDAG.
void assumeSpurious (std::string_view x, std::string_view y)
void assumeNonSpurious (NodeId x, NodeId y)
 Mark an arc as non-spurious: present in observationalBN, added to causalDAG.
void assumeNonSpurious (std::string_view x, std::string_view y)
bool isAssumedSpurious (NodeId x, NodeId y) const
 Returns true if arc is present in observationalBN and absent in causalDAG.
bool isAssumedSpurious (std::string_view x, std::string_view y) const
std::optional< NodeSetbackDoor (NodeId cause, NodeId effect) const
 Find a backdoor adjustment set (Z) between cause and effect (IDs only).
std::optional< NodeSetbackDoor (std::string_view cause, std::string_view effect) const
std::optional< NodeSetfrontDoor (NodeId cause, NodeId effect) const
 Find a frontdoor adjustment set (Z) between cause and effect (IDs only).
std::optional< NodeSetfrontDoor (std::string_view cause, std::string_view effect) const
 Find a frontdoor adjustment set (Z) between cause and effect (IDs only).
CausalModel< GUM_SCALAR > inducedCausalSubModel (const CausalModel< GUM_SCALAR > &cm, NodeSet subset) const
 Induced causal submodel on a subset of nodes.
std::string toDot (const bool SHOW_LATENT_NAMES=false, const char *NODE_BG="#404040", const char *NODE_FG="white", const char *EDGE_COL="#4A4A4A") const
 DOT representation of the causal DAG (observed + latent).
const BayesNet< GUM_SCALAR > & observationalBN () const
 Observational BN (observed variables only).
DAG causalDAG () const
 Causal DAG (observed + latent variables), with node names set.
Set< std::string > names () const
 All variable names appearing in the causal model (observed + latent).
NodeId idFromName (std::string_view name) const
 Node id from variable name (observed or latent).
std::string nameFromId (NodeId id) const
 Variable name from node id (observed or latent).
Bijection< NodeId, std::string > id2name (bool includeLatentVariable=false) const
 Bidirectional mapping between node ids and variable names.
NodeSet latentVariablesIds () const
 Node ids of all latent variables.
Set< std::string > latentVariablesNames () const
 Names of all latent variables.
NodeSet parents (NodeId x) const
 Parents of a node (by id) in the causal DAG (including latents).
NodeSet parents (std::string_view name) const
 Parents of a node (by name) in the causal DAG (including latents).
NodeSet children (NodeId x) const
 Children of a node (by id) in the causal DAG (including latents).
NodeSet children (std::string_view name) const
 Children of a node (by name) in the causal DAG (including latents).
HashTable< NodeId, NodeSetconnectedComponents () const
 Weakly connected components of the causal DAG.
const DiscreteVariablevariable (NodeId id) const
 From id to variable (observed only, by id).
const DiscreteVariablevariable (std::string_view name) const
 From name to variable (observed only, by id).

Private Attributes

BayesNet< GUM_SCALAR > _observationalBN_
 The underlying BayesNet representing the observed part of the model.
DAG _causalDAG_
 The underlying DAG representing the causal structure (observed + latent).
Bijection< NodeId, std::string > _ids_names_
 Bidirectional mapping between node ids and variable names (observed + latent).

Detailed Description

template<GUM_Numeric GUM_SCALAR>
class gum::CausalModel< GUM_SCALAR >

A causal model pairing an observational BayesNet with a causal DAG.

The class is DAG-centric for causality: all causal-structure queries and edits happen on _causalDAG_, which may include latent variables that do not appear in _observationalBN_. Name/Id bookkeeping is handled so you can work with either.

Invariants / expectations

  • _observationalBN_ corresponds to the observed part (no latents).
  • _causalDAG_ contains all observed variables and any added latent variables.
  • When adding a latent with children, the latent becomes a parent of all listed children in the causal DAG. The assumeNonSpurious flag controls whether existing arcs between those children are preserved or may be adjusted (implementation dependent).

Definition at line 84 of file causalModel.h.

Constructor & Destructor Documentation

◆ CausalModel() [1/5]

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::CausalModel ( )
delete

Default constructor disabled: a causal model must be built from a BN.

References CausalModel(), assumeNonSpurious(), and observationalBN().

Referenced by CausalModel(), CausalModel(), CausalModel(), CausalModel(), CausalModel(), ~CausalModel(), operator=(), and operator=().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CausalModel() [2/5]

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::CausalModel ( const BayesNet< GUM_SCALAR > & observationalBN)
explicit

Construct a causal model with no latent variables.

Parameters
observationalBNthe observed Bayesian network

Initializes the causal DAG as the BN's DAG (observed-only).

Definition at line 61 of file causalModel_tpl.h.

61 :
64 }
A causal model pairing an observational BayesNet with a causal DAG.
Definition causalModel.h:84
DAG _causalDAG_
The underlying DAG representing the causal structure (observed + latent).
Definition causalModel.h:90
BayesNet< GUM_SCALAR > _observationalBN_
The underlying BayesNet representing the observed part of the model.
Definition causalModel.h:87
CausalModel()=delete
Default constructor disabled: a causal model must be built from a BN.
const BayesNet< GUM_SCALAR > & observationalBN() const
Observational BN (observed variables only).

References CausalModel(), _causalDAG_, _observationalBN_, and observationalBN().

Here is the call graph for this function:

◆ CausalModel() [3/5]

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::CausalModel ( const BayesNet< GUM_SCALAR > & observationalBN,
const LatentDescriptorVector & latentVarsDescriptor,
bool assumeNonSpurious = false )
explicit

Construct a causal model and add a list of latent confounders.

Parameters
observationalBNthe observed Bayesian network
latentVarsDescriptorlist of (latentName, childrenIds) descriptors
assumeNonSpuriouswhether to preserve existing arcs among the latent's children

Each latent is created and added as a parent of the provided children in the causal DAG. The observed BN remains unchanged.

Definition at line 72 of file causalModel_tpl.h.

74 :
76 // add each latent (children given as names)
77 for (const auto& [latent, children]: latentVarsDescriptor) {
79 }
81 }
void addLatentVariable(std::string_view latentName, const std::vector< std::string > &childrenOfLatent, bool assumeNonSpurious=false)
Add a latent variable by names of its observed children.
NodeSet children(NodeId x) const
Children of a node (by id) in the causal DAG (including latents).
void assumeNonSpurious(NodeId x, NodeId y)
Mark an arc as non-spurious: present in observationalBN, added to causalDAG.

References CausalModel(), _causalDAG_, _observationalBN_, addLatentVariable(), assumeNonSpurious(), children(), and observationalBN().

Here is the call graph for this function:

◆ CausalModel() [4/5]

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::CausalModel ( const CausalModel< GUM_SCALAR > & other)

Copy constructor.

Definition at line 85 of file causalModel_tpl.h.

85 :
89 };
Bijection< NodeId, std::string > _ids_names_
Bidirectional mapping between node ids and variable names (observed + latent).
Definition causalModel.h:93

References CausalModel(), _causalDAG_, _ids_names_, and _observationalBN_.

Here is the call graph for this function:

◆ CausalModel() [5/5]

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::CausalModel ( CausalModel< GUM_SCALAR > && other)
noexcept

Move constructor.

Definition at line 93 of file causalModel_tpl.h.

References CausalModel(), _causalDAG_, _ids_names_, and _observationalBN_.

Here is the call graph for this function:

◆ ~CausalModel()

template<GUM_Numeric GUM_SCALAR>
gum::CausalModel< GUM_SCALAR >::~CausalModel ( )

Destructor.

Definition at line 67 of file causalModel_tpl.h.

67 {
69 }

References CausalModel().

Here is the call graph for this function:

Member Function Documentation

◆ addLatentVariable() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::addLatentVariable ( std::string_view latentName,
const std::vector< NodeId > & childrenOfLatent,
bool assumeNonSpurious = false )

Add a latent variable by **NodeId**s of its observed children.

Parameters
latentNamenew latent variable name
childrenOfLatentnode ids of observed children
assumeNonSpuriouspreserve existing arcs among the children if true.

A new latent node is inserted into the causal DAG and connected as a parent of each listed child. The observed BN is not modified.

Warning
When assumeNonSpurious == false and the latent has children \(\{X_1,\ldots,X_n\}\), any existing arcs \(X_i \rightarrow X_j\) among those children may be removed from the causal DAG. To preserve some child→child arcs while dropping others, consider creating separate latents: one with assumeNonSpurious = true for the subset to preserve, and another with assumeNonSpurious = false for the subset to drop. A finer-grained edit API may be added later.

Definition at line 117 of file causalModel_tpl.h.

119 {
120 // must have at least 2 children
121 if (childrenOfLatent.size() < 2) {
122 GUM_ERROR(InvalidArgument, "A latent variable must affect at least 2 children.");
123 }
124
125 // reject names that collide with observed or existing latent variables
126 if (_observationalBN_.variableNodeMap().exists(latentName)) {
128 "Latent variable name conflicts with an observed variable: "
130 }
131 if (_ids_names_.existsSecond(std::string(latentName))) {
133 "Latent variable name already used by another latent: " + std::string(latentName));
134 }
135
136 // create a fresh node id for the latent in the causal DAG
137 const NodeId id_latent = _causalDAG_.addNode();
138
139 // record bookkeeping
141
142 // add arcs latent -> each child
143 for (const auto& cid: childrenOfLatent) {
144 // children are observed nodes: they must already exist in causal DAG (copied from observed
145 // BN)
146 _causalDAG_.addArc(id_latent, cid);
147 }
148
149 // remove any direct causal arc among children unless assumeNonSpurious=true
150 if (!assumeNonSpurious) {
151 for (auto i = childrenOfLatent.begin(); i != childrenOfLatent.end(); ++i) {
152 auto j = i;
153 ++j;
154 for (auto jj = j; jj != childrenOfLatent.end(); ++jj) {
155 const NodeId a = *i;
156 const NodeId b = *jj;
157 // remove either direction if present
158 Arc ab(a, b), ba(b, a);
159 if (_causalDAG_.existsArc(ab)) _causalDAG_.eraseArc(ab);
160 else if (_causalDAG_.existsArc(ba)) _causalDAG_.eraseArc(ba);
161 }
162 }
163 }
164 }
#define GUM_ERROR(type, msg)
Definition exceptions.h:76

References _observationalBN_, assumeNonSpurious(), and GUM_ERROR.

Here is the call graph for this function:

◆ addLatentVariable() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::addLatentVariable ( std::string_view latentName,
const std::vector< std::string > & childrenOfLatent,
bool assumeNonSpurious = false )

Add a latent variable by names of its observed children.

Parameters
latentNamenew latent variable name
childrenOfLatentnames of observed children
assumeNonSpuriouspreserve existing arcs among the children if true

A new latent node is inserted into the causal DAG and connected as a parent of each listed child. The observed BN is not modified.

Warning
When assumeNonSpurious == false and the latent has children \(\{X_1,\ldots,X_n\}\), any existing arcs \(X_i \rightarrow X_j\) among those children may be removed from the causal DAG. To preserve some child→child arcs while dropping others, consider creating separate latents: one with assumeNonSpurious = true for the subset to preserve, and another with assumeNonSpurious = false for the subset to drop. A finer-grained edit API may be added later.

Definition at line 104 of file causalModel_tpl.h.

107 {
109 childIds.reserve(childrenOfLatent.size());
110 for (const auto& nm: childrenOfLatent) {
111 childIds.push_back(idFromName(nm));
112 }
114 }
NodeId idFromName(std::string_view name) const
Node id from variable name (observed or latent).

References addLatentVariable(), assumeNonSpurious(), and idFromName().

Referenced by CausalModel(), and addLatentVariable().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ assumeNonSpurious() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::assumeNonSpurious ( NodeId x,
NodeId y )

Mark an arc as non-spurious: present in observationalBN, added to causalDAG.

Throws if arc is not present in observationalBN.

Definition at line 222 of file causalModel_tpl.h.

222 {
223 // Validate arc in observationalBN
224 if (!_observationalBN_.internalDag().existsArc(Arc(x, y))) {
226 "Arc(" + std::to_string(x) + "," + std::to_string(y)
227 + ") not present in observationalBN");
228 }
229 // Add arc to causalDAG if absent
230 if (!_causalDAG_.existsArc(Arc(x, y))) { _causalDAG_.addArc(x, y); }
231 }

Referenced by CausalModel(), CausalModel(), addLatentVariable(), addLatentVariable(), assumeNonSpurious(), and operator=().

Here is the caller graph for this function:

◆ assumeNonSpurious() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::assumeNonSpurious ( std::string_view x,
std::string_view y )

Definition at line 234 of file causalModel_tpl.h.

234 {
236 }

References assumeNonSpurious(), and idFromName().

Here is the call graph for this function:

◆ assumeSpurious() [1/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::assumeSpurious ( NodeId x,
NodeId y )

Mark an arc as spurious: present in observationalBN, removed from causalDAG.

Throws if arc is not present in observationalBN.

Definition at line 205 of file causalModel_tpl.h.

205 {
206 // Validate arc in observationalBN
207 if (!_observationalBN_.internalDag().existsArc(Arc(x, y))) {
209 "Arc(" + std::to_string(x) + "," + std::to_string(y)
210 + ") not present in observationalBN");
211 }
212 // Remove arc from causalDAG if present
213 if (_causalDAG_.existsArc(Arc(x, y))) { _causalDAG_.eraseArc(Arc(x, y)); }
214 }

References _causalDAG_, _observationalBN_, and GUM_ERROR.

Referenced by assumeSpurious().

Here is the caller graph for this function:

◆ assumeSpurious() [2/2]

template<GUM_Numeric GUM_SCALAR>
void gum::CausalModel< GUM_SCALAR >::assumeSpurious ( std::string_view x,
std::string_view y )

Definition at line 217 of file causalModel_tpl.h.

217 {
219 }
void assumeSpurious(NodeId x, NodeId y)
Mark an arc as spurious: present in observationalBN, removed from causalDAG.

References assumeSpurious(), and idFromName().

Here is the call graph for this function:

◆ backDoor() [1/2]

template<GUM_Numeric GUM_SCALAR>
std::optional< NodeSet > gum::CausalModel< GUM_SCALAR >::backDoor ( NodeId cause,
NodeId effect ) const

Find a backdoor adjustment set (Z) between cause and effect (IDs only).

Enumerates admissible backdoor sets in the current causal DAG (excluding latent variables) and returns the first valid one found. Returns std::nullopt if no backdoor set exists at all. Note: an empty set is a valid backdoor when X has no back-door paths.

Preconditions:

  • cause and effect must be observed variables of the model.
Parameters
causeNodeId of the cause (X).
effectNodeId of the effect (Y).
Returns
std::optional<NodeSet> The backdoor adjustment set (Z) as NodeIds, or std::nullopt if no backdoor set exists.

Definition at line 389 of file causalModel_tpl.h.

389 {
390 // Preconditions: cause/effect must be observed (non-latent).
392 if (lat.contains(cause) || lat.contains(effect)) {
394 "CausalModel::backDoor: 'cause' and 'effect' must be observed (non-latent).");
395 }
396
398 if (candidates.empty()) return std::nullopt;
399 return candidates.front(); // first valid set (may itself be empty)
400 }
NodeSet latentVariablesIds() const
Node ids of all latent variables.
static NodeSetVec enumerateBackdoorSets(const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
Enumerate valid backdoor adjustment sets.

References _causalDAG_, gum::Set< Key >::contains(), gum::DoorCriteria::enumerateBackdoorSets(), GUM_ERROR, and latentVariablesIds().

Referenced by backDoor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ backDoor() [2/2]

template<GUM_Numeric GUM_SCALAR>
std::optional< NodeSet > gum::CausalModel< GUM_SCALAR >::backDoor ( std::string_view cause,
std::string_view effect ) const

Definition at line 562 of file causalModel_tpl.h.

563 {
565 }
std::optional< NodeSet > backDoor(NodeId cause, NodeId effect) const
Find a backdoor adjustment set (Z) between cause and effect (IDs only).

References backDoor(), and idFromName().

Here is the call graph for this function:

◆ causalDAG()

template<GUM_Numeric GUM_SCALAR>
DAG gum::CausalModel< GUM_SCALAR >::causalDAG ( ) const

Causal DAG (observed + latent variables), with node names set.

Definition at line 554 of file causalModel_tpl.h.

554 {
555 DAG g = _causalDAG_;
556 for (auto id: g)
557 g.setName(id, nameFromId(id));
558 return g;
559 }
std::string nameFromId(NodeId id) const
Variable name from node id (observed or latent).

References _causalDAG_, nameFromId(), and gum::NodeGraphPart::setName().

Referenced by gum::DoCalculus< GUM_SCALAR >::_ID_().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ children() [1/2]

template<GUM_Numeric GUM_SCALAR>
NodeSet gum::CausalModel< GUM_SCALAR >::children ( NodeId x) const

Children of a node (by id) in the causal DAG (including latents).

Definition at line 375 of file causalModel_tpl.h.

375 {
376 return _causalDAG_.children(x);
377 }

References _causalDAG_.

Referenced by CausalModel(), gum::DoCalculus< GUM_SCALAR >::_cDecomposition_(), and children().

Here is the caller graph for this function:

◆ children() [2/2]

template<GUM_Numeric GUM_SCALAR>
NodeSet gum::CausalModel< GUM_SCALAR >::children ( std::string_view name) const

Children of a node (by name) in the causal DAG (including latents).

Definition at line 380 of file causalModel_tpl.h.

380 {
381 return children(idFromName(name));
382 }

References children(), and idFromName().

Here is the call graph for this function:

◆ connectedComponents()

template<GUM_Numeric GUM_SCALAR>
HashTable< NodeId, NodeSet > gum::CausalModel< GUM_SCALAR >::connectedComponents ( ) const

Weakly connected components of the causal DAG.

Returns
A table mapping a representative NodeId to the NodeSet of nodes in its component.

Definition at line 261 of file causalModel_tpl.h.

261 {
263
264 // Build a mutable "remaining" set from the DAG's node view
266 for (auto n: _causalDAG_.nodes())
267 remaining.insert(n);
268
269 while (!remaining.empty()) {
270 const NodeId root = *remaining.begin();
271
272 NodeSet cc;
274 remaining.erase(root);
275
276 while (!stack.empty()) {
277 const NodeId u = stack.back();
278 stack.pop_back();
279 cc.insert(u);
280
281 // weak neighbours = parents ∪ children
282 for (auto v: _causalDAG_.parents(u)) {
283 if (remaining.contains(v)) {
284 remaining.erase(v);
285 stack.push_back(v);
286 }
287 }
288 for (auto v: _causalDAG_.children(u)) {
289 if (remaining.contains(v)) {
290 remaining.erase(v);
291 stack.push_back(v);
292 }
293 }
294 }
295
296 comps.insert(root, std::move(cc));
297 }
298
299 return comps;
300 }

References _causalDAG_, gum::Set< Key >::begin(), gum::Set< Key >::empty(), and gum::Set< Key >::insert().

Here is the call graph for this function:

◆ existsArc() [1/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::CausalModel< GUM_SCALAR >::existsArc ( NodeId x,
NodeId y ) const

Whether a causal arc x → y exists (by ids) in the causal DAG.

Definition at line 167 of file causalModel_tpl.h.

167 {
168 return _causalDAG_.existsArc(Arc(x, y));
169 }

References _causalDAG_.

Referenced by existsArc().

Here is the caller graph for this function:

◆ existsArc() [2/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::CausalModel< GUM_SCALAR >::existsArc ( std::string_view x,
std::string_view y ) const

Whether a causal arc x → y exists (by names) in the causal DAG.

Definition at line 172 of file causalModel_tpl.h.

172 {
173 const NodeId ix = idFromName(x);
174 const NodeId iy = idFromName(y);
175 return existsArc(ix, iy);
176 }
bool existsArc(NodeId x, NodeId y) const
Whether a causal arc x → y exists (by ids) in the causal DAG.

References existsArc(), and idFromName().

Here is the call graph for this function:

◆ frontDoor() [1/2]

template<GUM_Numeric GUM_SCALAR>
std::optional< NodeSet > gum::CausalModel< GUM_SCALAR >::frontDoor ( NodeId cause,
NodeId effect ) const

Find a frontdoor adjustment set (Z) between cause and effect (IDs only).

Enumerates admissible frontdoor sets in the current causal DAG (excluding latent variables) and returns the first valid one found. Returns std::nullopt if no frontdoor set exists at all. Note: an empty set is a valid frontdoor in degenerate cases.

Preconditions:

  • cause and effect must be observed variables of the model.
Parameters
causeNodeId of the cause (X).
effectNodeId of the effect (Y).
Returns
std::optional<NodeSet> The frontdoor adjustment set (Z) as NodeIds, or std::nullopt if no frontdoor set exists.

Definition at line 403 of file causalModel_tpl.h.

403 {
404 // Preconditions: cause/effect must be observed (non-latent).
406 if (lat.contains(cause) || lat.contains(effect)) {
408 "CausalModel::frontDoor: 'cause' and 'effect' must be observed (non-latent).");
409 }
410
412 if (candidates.empty()) return std::nullopt;
413 return candidates.front(); // first valid set (may itself be empty)
414 }
static NodeSetVec enumerateFrontdoorSets(const DAG &dag, NodeId X, NodeId Y, const NodeSet &excluded_nodes=NodeSet(), std::size_t max_cardinality=0, bool only_minimal=true, bool stopAtFirst=false)
Enumerate valid frontdoor adjustment sets.

References _causalDAG_, gum::Set< Key >::contains(), gum::DoorCriteria::enumerateFrontdoorSets(), GUM_ERROR, and latentVariablesIds().

Referenced by frontDoor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ frontDoor() [2/2]

template<GUM_Numeric GUM_SCALAR>
std::optional< NodeSet > gum::CausalModel< GUM_SCALAR >::frontDoor ( std::string_view cause,
std::string_view effect ) const

Find a frontdoor adjustment set (Z) between cause and effect (IDs only).

Enumerates admissible frontdoor sets in the current causal DAG (excluding latent variables) and returns the first valid one found. Returns std::nullopt if no frontdoor set exists at all. Note: an empty set is a valid frontdoor in degenerate cases.

Preconditions:

  • cause and effect must be observed variables of the model.
Parameters
causename (string_view) of the cause (X).
effectname (string_view) of the effect (Y).
Returns
std::optional<NodeSet> The frontdoor adjustment set (Z) as NodeIds, or std::nullopt if no frontdoor set exists.

Definition at line 568 of file causalModel_tpl.h.

569 {
571 }
std::optional< NodeSet > frontDoor(NodeId cause, NodeId effect) const
Find a frontdoor adjustment set (Z) between cause and effect (IDs only).

References frontDoor(), and idFromName().

Here is the call graph for this function:

◆ id2name()

template<GUM_Numeric GUM_SCALAR>
Bijection< NodeId, std::string > gum::CausalModel< GUM_SCALAR >::id2name ( bool includeLatentVariable = false) const

Bidirectional mapping between node ids and variable names.

Definition at line 526 of file causalModel_tpl.h.

526 {
528
529 // Add observed variables
530 for (const auto n: _observationalBN_.nodes()) {
531 result.insert(n, _observationalBN_.variable(n).name());
532 }
533
534 // Add latent variables if requested
536 for (auto iter = _ids_names_.beginSafe(); iter != _ids_names_.endSafe(); ++iter) {
537 result.insert(iter.first(), iter.second());
538 }
539 }
540
541 return result;
542 }

References _ids_names_, _observationalBN_, and gum::BijectionImplementation< T1, T2, std::is_scalar< T1 >::value &&std::is_scalar< T2 >::value >::insert().

Here is the call graph for this function:

◆ idFromName()

template<GUM_Numeric GUM_SCALAR>
NodeId gum::CausalModel< GUM_SCALAR >::idFromName ( std::string_view name) const

Node id from variable name (observed or latent).

Definition at line 507 of file causalModel_tpl.h.

507 {
508 if (_ids_names_.existsSecond(std::string(name))) {
509 return _ids_names_.first(std::string(name));
510 } else {
511 return _observationalBN_.idFromName(name);
512 }
513 }

References _ids_names_, and _observationalBN_.

Referenced by addLatentVariable(), assumeNonSpurious(), assumeSpurious(), backDoor(), children(), gum::Counterfactual< GUM_ELEMENT >::counterFactualModel(), gum::counterfactualModel(), existsArc(), frontDoor(), parents(), and variable().

Here is the caller graph for this function:

◆ inducedCausalSubModel()

template<GUM_Numeric GUM_SCALAR>
CausalModel< GUM_SCALAR > gum::CausalModel< GUM_SCALAR >::inducedCausalSubModel ( const CausalModel< GUM_SCALAR > & cm,
NodeSet subset ) const

Induced causal submodel on a subset of nodes.

Parameters
cma source causal model
subsetnode set to keep (observed + latent)
Returns
A new CausalModel induced by the subset (both BN/DAG restricted appropriately).

Definition at line 305 of file causalModel_tpl.h.

306 {
307 // If caller gave an empty subset, use "all observed" nodes
308 if (subset.empty()) {
309 for (auto n: cm.observationalBN().nodes())
310 subset.insert(n);
311 }
312
313 // Keep only OBSERVED nodes (drop latents explicitly)
315 for (auto L: cm.latentVariablesIds())
316 nodes.erase(L);
317
318 // --- Build an observational BN restricted to `nodes`
320 HashTable< NodeId, NodeId > idmap; // oldId -> newId
321
322 // add variables
323 for (auto n: nodes) {
324 const auto& var = cm.observationalBN().variable(n);
325 const NodeId nn = bn.add(var, n); // API accepts "add(var, id)"
326 idmap.insert(n, nn);
327 }
328
329 // add arcs among the kept observed nodes (from the *causal* DAG)
330 const auto causalDag = cm.causalDAG();
331 for (const auto& a: causalDag.arcs()) {
332 const NodeId x = a.tail();
333 const NodeId y = a.head();
334 if (nodes.contains(x) && nodes.contains(y)) { bn.addArc(idmap[x], idmap[y]); }
335 }
336
337 // --- Rebuild latent descriptor restricted to kept nodes
339 for (auto L: cm.latentVariablesIds()) {
341 mapped_children.reserve(4);
342
343 // intersect latent children with kept observed nodes
344 for (auto c: cm.children(L)) {
345 if (nodes.contains(c)) mapped_children.push_back(idmap[c]);
346 }
347
348 // keep the latent ONLY if it still has at least 2 children
349 if (mapped_children.size() >= 2) {
351 }
352 }
353
354
355 // assumeNonSpurious=true : pyagrum uses True here (do not remove arcs among children)
356 return CausalModel< GUM_SCALAR >(bn, latentDesc, /*assumeNonSpurious=*/true);
357 }
DAG causalDAG() const
Causal DAG (observed + latent variables), with node names set.

Referenced by gum::DoCalculus< GUM_SCALAR >::_ID_().

Here is the caller graph for this function:

◆ isAssumedSpurious() [1/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::CausalModel< GUM_SCALAR >::isAssumedSpurious ( NodeId x,
NodeId y ) const

Returns true if arc is present in observationalBN and absent in causalDAG.

Definition at line 239 of file causalModel_tpl.h.

239 {
240 return _observationalBN_.internalDag().existsArc(Arc(x, y))
241 && !_causalDAG_.existsArc(Arc(x, y));
242 }

References _causalDAG_, and _observationalBN_.

◆ isAssumedSpurious() [2/2]

template<GUM_Numeric GUM_SCALAR>
bool gum::CausalModel< GUM_SCALAR >::isAssumedSpurious ( std::string_view x,
std::string_view y ) const

Definition at line 245 of file causalModel_tpl.h.

245 {
247 }
bool isAssumedSpurious(NodeId x, NodeId y) const
Returns true if arc is present in observationalBN and absent in causalDAG.

◆ latentVariablesIds()

template<GUM_Numeric GUM_SCALAR>
NodeSet gum::CausalModel< GUM_SCALAR >::latentVariablesIds ( ) const

Node ids of all latent variables.

Definition at line 183 of file causalModel_tpl.h.

183 {
185 for (auto it = _ids_names_.begin(); it != _ids_names_.end(); ++it) {
186 latentIds.insert(it.first());
187 }
188 return latentIds;
189 }

References _ids_names_, and gum::Set< Key >::insert().

Referenced by gum::DoCalculus< GUM_SCALAR >::_cDecomposition_(), gum::DoCalculus< GUM_SCALAR >::_ID_(), backDoor(), gum::Counterfactual< GUM_ELEMENT >::counterFactualModel(), gum::Counterfactual< GUM_ELEMENT >::counterFactualModel(), gum::counterfactualModel(), frontDoor(), and toDot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ latentVariablesNames()

template<GUM_Numeric GUM_SCALAR>
Set< std::string > gum::CausalModel< GUM_SCALAR >::latentVariablesNames ( ) const

Names of all latent variables.

Definition at line 192 of file causalModel_tpl.h.

192 {
194 for (auto it = _ids_names_.begin(); it != _ids_names_.end(); ++it) {
195 latentNames.insert(it.second());
196 }
197 return latentNames;
198 }

◆ nameFromId()

template<GUM_Numeric GUM_SCALAR>
std::string gum::CausalModel< GUM_SCALAR >::nameFromId ( NodeId id) const

Variable name from node id (observed or latent).

Definition at line 516 of file causalModel_tpl.h.

516 {
517 if (_ids_names_.existsFirst(id)) {
518 return _ids_names_.second(id);
519 } else {
520 return _observationalBN_.variable(id).name();
521 }
522 }

References _ids_names_, and _observationalBN_.

Referenced by gum::DoCalculus< GUM_SCALAR >::_hedgeExceptionMsg(), gum::DoCalculus< GUM_SCALAR >::_ID_(), causalDAG(), and toDot().

Here is the caller graph for this function:

◆ names()

template<GUM_Numeric GUM_SCALAR>
Set< std::string > gum::CausalModel< GUM_SCALAR >::names ( ) const

All variable names appearing in the causal model (observed + latent).

Definition at line 494 of file causalModel_tpl.h.

494 {
496 for (const auto n: _observationalBN_.nodes()) {
497 names.insert(_observationalBN_.variable(n).name());
498 }
499 // latent names (from bijection)
500 for (auto it = _ids_names_.begin(); it != _ids_names_.end(); ++it) {
501 names.insert(it.second());
502 }
503 return names;
504 }
Set< std::string > names() const
All variable names appearing in the causal model (observed + latent).

References _ids_names_, _observationalBN_, and names().

Referenced by names().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ observationalBN()

◆ operator=() [1/2]

template<GUM_Numeric GUM_SCALAR>
CausalModel & gum::CausalModel< GUM_SCALAR >::operator= ( CausalModel< GUM_SCALAR > && other)
defaultnoexcept

Move assignment.

References CausalModel(), and assumeNonSpurious().

Here is the call graph for this function:

◆ operator=() [2/2]

template<GUM_Numeric GUM_SCALAR>
CausalModel & gum::CausalModel< GUM_SCALAR >::operator= ( const CausalModel< GUM_SCALAR > & other)
default

Copy assignment.

References CausalModel().

Here is the call graph for this function:

◆ parents() [1/2]

template<GUM_Numeric GUM_SCALAR>
NodeSet gum::CausalModel< GUM_SCALAR >::parents ( NodeId x) const

Parents of a node (by id) in the causal DAG (including latents).

Definition at line 364 of file causalModel_tpl.h.

364 {
365 // delegate to the causal DAG (includes latent->observed arcs)
366 return _causalDAG_.parents(x);
367 }

References _causalDAG_.

Referenced by parents().

Here is the caller graph for this function:

◆ parents() [2/2]

template<GUM_Numeric GUM_SCALAR>
NodeSet gum::CausalModel< GUM_SCALAR >::parents ( std::string_view name) const

Parents of a node (by name) in the causal DAG (including latents).

Definition at line 370 of file causalModel_tpl.h.

370 {
371 return parents(idFromName(name));
372 }
NodeSet parents(NodeId x) const
Parents of a node (by id) in the causal DAG (including latents).

References idFromName(), and parents().

Here is the call graph for this function:

◆ toDot()

template<GUM_Numeric GUM_SCALAR>
std::string gum::CausalModel< GUM_SCALAR >::toDot ( const bool SHOW_LATENT_NAMES = false,
const char * NODE_BG = "#404040",
const char * NODE_FG = "white",
const char * EDGE_COL = "#4A4A4A" ) const

DOT representation of the causal DAG (observed + latent).

Parameters
SHOW_LATENT_NAMESIf true, display latent names explicitly.
NODE_BGNode background color (hex code like "#404040" or Graphviz color name like "lightgray").
NODE_FGNode label/text color (hex code or Graphviz color name).
EDGE_COLEdge color (hex code or Graphviz color name).
Returns
A string containing a Graphviz/DOT graph.

Definition at line 420 of file causalModel_tpl.h.

423 {
424 // SHOW_LATENT_NAMES => causal.show_latent_names
425 // NODE_BG => causal.default_node_bgcolor
426 // NODE_FG => causal.default_node_fgcolor
427 // EDGE_COL => notebook.default_arc_color
428
430 out << "digraph {\n";
431
432 // First, latent nodes
433 for (const auto n: _causalDAG_.nodes()) {
436 try {
437 label = nameFromId(n);
438 } catch (gum::NotFound&) { label = "L" + std::to_string(n); }
439 out << " \"" << label << "\" "
440 << "[fillcolor=\"" << NODE_BG << "\", "
441 << "fontcolor=\"" << NODE_FG << "\", "
442 << "style=filled,shape=" << (SHOW_LATENT_NAMES ? "ellipse" : "point") << "];\n";
443 }
444 }
445
446 // Then, observed nodes
447 for (const auto n: _causalDAG_.nodes()) {
448 if (!latentVariablesIds().contains(n)) {
450 try {
451 label = nameFromId(n);
452 } catch (gum::NotFound&) { label = "L" + std::to_string(n); }
453 out << " \"" << label << "\" "
454 << "[fillcolor=\"" << NODE_BG << "\", "
455 << "fontcolor=\"" << NODE_FG << "\", "
456 << "style=filled,shape=ellipse];\n";
457 }
458 }
459
460 // Edges
461 for (const auto tail: _causalDAG_.nodes()) {
462 for (const auto head: _causalDAG_.children(tail)) {
464 try {
465 lt = nameFromId(tail);
466 } catch (gum::NotFound&) { lt = "L" + std::to_string(tail); }
467 try {
468 lh = nameFromId(head);
469 } catch (gum::NotFound&) { lh = "L" + std::to_string(head); }
470
471 const bool dashed
473
474 out << " \"" << lt << "\"->\"" << lh << "\" ";
475 if (dashed) {
476 out << "[style=dashed]";
477 } else {
478 // match Python: color="#4A4A4A:#4A4A4A"
479 out << "[color=\"" << EDGE_COL << ":" << EDGE_COL << "\"]";
480 }
481 out << ";\n";
482 }
483 }
484
485 out << "}\n";
486 return out.str();
487 }
bool contains(const Key &k) const
Indicates whether a given elements belong to the set.
Definition set_tpl.h:468
bool contains(std::string_view s, std::string_view needle)
true if needle in s

References _causalDAG_, gum::contains(), latentVariablesIds(), and nameFromId().

Here is the call graph for this function:

◆ variable() [1/2]

template<GUM_Numeric GUM_SCALAR>
const DiscreteVariable & gum::CausalModel< GUM_SCALAR >::variable ( NodeId id) const

From id to variable (observed only, by id).

Throws if id does not correspond to an observed variable.

Parameters
idthe node id
Exceptions
throwsgum::NotFound if id does not correspond to an observed variable in the model
Returns
the variable if exists

Definition at line 574 of file causalModel_tpl.h.

574 {
575 return _observationalBN_.variable(id);
576 }

References _observationalBN_.

◆ variable() [2/2]

template<GUM_Numeric GUM_SCALAR>
const DiscreteVariable & gum::CausalModel< GUM_SCALAR >::variable ( std::string_view name) const

From name to variable (observed only, by id).

Throws if id does not correspond to an observed variable.

Parameters
name
Exceptions
throwsgum::NotFound if id does not correspond to an observed variable in the model
Returns
the variable if exists

Definition at line 579 of file causalModel_tpl.h.

579 {
580 return _observationalBN_.variable(idFromName(name));
581 }

References _observationalBN_, and idFromName().

Here is the call graph for this function:

Member Data Documentation

◆ _causalDAG_

template<GUM_Numeric GUM_SCALAR>
DAG gum::CausalModel< GUM_SCALAR >::_causalDAG_
private

The underlying DAG representing the causal structure (observed + latent).

Definition at line 90 of file causalModel.h.

Referenced by CausalModel(), CausalModel(), CausalModel(), CausalModel(), assumeSpurious(), backDoor(), causalDAG(), children(), connectedComponents(), existsArc(), frontDoor(), isAssumedSpurious(), parents(), and toDot().

◆ _ids_names_

template<GUM_Numeric GUM_SCALAR>
Bijection< NodeId, std::string > gum::CausalModel< GUM_SCALAR >::_ids_names_
private

Bidirectional mapping between node ids and variable names (observed + latent).

Definition at line 93 of file causalModel.h.

Referenced by CausalModel(), CausalModel(), id2name(), idFromName(), latentVariablesIds(), nameFromId(), and names().

◆ _observationalBN_

template<GUM_Numeric GUM_SCALAR>
BayesNet< GUM_SCALAR > gum::CausalModel< GUM_SCALAR >::_observationalBN_
private

The underlying BayesNet representing the observed part of the model.

Definition at line 87 of file causalModel.h.

Referenced by CausalModel(), CausalModel(), CausalModel(), CausalModel(), addLatentVariable(), assumeSpurious(), id2name(), idFromName(), isAssumedSpurious(), nameFromId(), names(), observationalBN(), variable(), and variable().


The documentation for this class was generated from the following files: