67 sepSet_(std::move(from.sepSet_)) {
94 sepSet_ = std::move(from.sepSet_);
132 std::vector< Edge > edges(
graph.edges().begin(),
graph.edges().end());
134 bool anyTested =
false;
135 std::vector< Edge > toRemove;
137 for (
const Edge& edge: edges) {
138 if (!
graph.existsEdge(edge)) {
continue; }
140 const NodeId X = edge.first();
141 const NodeId Y = edge.second();
147 if (pval <=
alpha_) {
return; }
149 toRemove.push_back(edge);
151 graph.eraseEdge(edge);
153 sepSet_.insert({X, Y}, {{}, pval});
154 sepSet_.insert({Y, X}, {{}, pval});
158 bool exh_found =
false;
159 std::set< NodeId > exh_union;
160 double exh_pval = 0.0;
162 for (
int dir = 0; dir < 2; ++dir) {
163 const NodeId A = (dir == 0) ? X : Y;
164 const NodeId B = (dir == 0) ? Y : X;
166 std::vector< NodeId > adj;
168 if (nb != B) { adj.push_back(nb); }
171 if (
static_cast< Size >(adj.size()) < d) {
continue; }
174 std::vector< std::size_t > idx(d);
175 std::iota(idx.begin(), idx.end(), std::size_t(0));
178 std::vector< NodeId > cond(d);
179 for (
Size i = 0; i < d; ++i) {
180 cond[i] = adj[idx[i]];
183 const double pval =
test_->statistics(A, B, cond).second;
187 toRemove.push_back(edge);
189 graph.eraseEdge(edge);
191 sepSet_.insert({X, Y}, {cond, pval});
192 sepSet_.insert({Y, X}, {cond, pval});
196 if (exh_pval == 0.0) { exh_pval = pval; }
197 for (
const NodeId n: cond) {
202 int i =
static_cast< int >(d) - 1;
203 while (i >= 0 && idx[i] == adj.size() - d +
static_cast< std::size_t
>(i)) {
206 if (i < 0) {
break; }
208 for (
int j = i + 1; j < static_cast< int >(d); ++j) {
209 idx[j] = idx[j - 1] + 1;
216 toRemove.push_back(edge);
218 graph.eraseEdge(edge);
220 const std::vector< NodeId > union_vec(exh_union.begin(), exh_union.end());
221 sepSet_.insert({X, Y}, {union_vec, exh_pval});
222 sepSet_.insert({Y, X}, {union_vec, exh_pval});
228 for (
const Edge& e: toRemove) {
229 if (
graph.existsEdge(e)) {
graph.eraseEdge(e); }
233 if (!anyTested) {
break; }
245 if (
graph.existsEdge(src, dst)) {
248 graph.addArc(src, dst);
249 GUM_SL_EMIT(src, dst,
"Orient " << src <<
" -> " << dst,
"V-structure collider");
254 "Conflict " << src <<
" -> " << dst,
255 "V-structure blocked by constraint");
257 }
else if (
graph.existsArc(dst, src)) {
259 GUM_SL_EMIT(src, dst,
"Conflict " << src <<
" -> " << dst,
"V-structure conflict: latent?");
274 if (!
sepSet_.exists({X, Y})) {
return false; }
276 return std::find(entry.
cond.begin(), entry.
cond.end(), Z) == entry.
cond.end();
281 const NodeId X = std::get< 0 >(tp);
282 const NodeId Y = std::get< 1 >(tp);
283 const NodeId Z = std::get< 2 >(tp);
284 if (!isCollider(X, Y, Z)) {
continue; }
294 std::vector< Candidate > candidates;
295 candidates.reserve(triples.size());
298 const NodeId X = std::get< 0 >(tp);
299 const NodeId Y = std::get< 1 >(tp);
300 const NodeId Z = std::get< 2 >(tp);
301 if (!isCollider(X, Y, Z)) {
continue; }
302 candidates.push_back({X, Y, Z,
sepSet_[{X, Y}].pval});
305 std::sort(candidates.begin(), candidates.end(), [](
const Candidate& a,
const Candidate& b) {
306 return a.pval > b.pval;
309 for (
const auto& [X, Y, Z, pval]: candidates) {
Abstract base class for CI-test-based structure learning algorithms.
#define GUM_SL_EMIT(x, y, action, explain)
The base class for all directed edges.
The base class for all undirected edges.
Base class for mixed graphs.
Abstract base for CI-test-based causal structure learning (PC, FCI).
~CIBasedLearning() override
MixedGraph orientUnshieldedColliders_(MixedGraph graph)
orient unshielded colliders in graph and return the updated graph
void setAlpha(double alpha)
maximum conditioning set size; Size(-1) = unlimited (default)
UCPriority ucPriority() const
maximum conditioning set size; Size(-1) = unlimited (default)
void orientColliderArm_(MixedGraph &graph, NodeId src, NodeId dst)
orient one arm (src → dst) of a collider; calls resolveOrientConflict_ on conflict
bool exhaustiveSepSet() const
maximum conditioning set size; Size(-1) = unlimited (default)
void setIndependenceTest(IndependenceTest &test)
inject independence test (non-owning: caller manages lifetime)
void setUCPriority(UCPriority p)
collider candidate ordering (default: Standard)
UCPriority
Controls how collider candidates are ordered before orientation.
@ Standard
process triples in natural traversal order
CIBasedLearning & operator=(const CIBasedLearning &)
void setMaxCondSetSize(Size max_k)
maximum conditioning set size; Size(-1) = unlimited (default)
MixedGraph learnSkeleton(MixedGraph graph) override
Phase 1: skeleton discovery via conditional independence tests.
double alpha() const
maximum conditioning set size; Size(-1) = unlimited (default)
void setStable(bool stable)
stable mode: defer edge removals until end of each depth (default: true)
virtual void resolveOrientConflict_(NodeId src, NodeId dst)
conflict hook: called when orientation cannot proceed (arc blocked or reversed). Default: push Arc(sr...
HashTable< std::pair< NodeId, NodeId >, SepSetEntry_ > sepSet_
void setExhaustiveSepSet(bool exhaustive)
exhaustive sepset mode: accumulate union of all separating sets found at each depth,...
std::vector< ThreePoints > unshieldedTriples_(const MixedGraph &graph)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
const std::vector< NodeId > _emptySet_
bool isArcValid_(const MixedGraph &graph, NodeId x, NodeId y)
static bool _existsDirectedPath_(const MixedGraph &graph, NodeId n1, NodeId n2)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
ConstraintBasedLearning()
ConstraintBasedLearning & operator=(const ConstraintBasedLearning &)
MixedGraph initGraph_(const MixedGraph &template_graph)
Builds a complete MixedGraph on the nodes of template_graph, minus edges forbidden by structural cons...
std::vector< Arc > _latentCouples_
The base class for all the independence tests used for learning.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Size NodeId
Type for node ids.
Base classes for mixed directed/undirected graphs.
include the inlined functions if necessary
std::tuple< NodeId, NodeId, NodeId > ThreePoints
gum is the global namespace for all aGrUM entities
std::vector< NodeId > cond