Back to Blog

Changelog for aGrUM/pyAgrum 3.0.0

Changelog for 3.0.0

This major release brings three headline changes:

  • Causal module promoted to C++: the causal inference layer moves from a pure-Python package to a first-class C++ module (CM), with SWIG bindings, an AST for do-calculus, and a complete Python API.
  • Extended structure learning: PC, FCI (with PAG output), GreedyThickThinning, and triangle deletions in GreedyHillClimbing are all integrated into BNLearner.
  • New GUM serialization format (jgum / bgum): a first-class JSON and binary format for all graphical models (BN, ID, MRF), with round-trip string API (proceedFromString) and full Python bindings (saveGUM / loadGUM / saveGUMstring / loadGUMstring).

This major release brings more than 410 commits since 2.3.2.

This release also reflects a sustained optimization effort. On the C++ side, the test suite expanded from 1 487 to 1 890 tests — all previous ones included — while execution time dropped from 150 s to 130 s (measured on a Mac Studio, Apple M2 Ultra, 24 cores, 128 GB RAM). The pyAgrum library (_pyagrum.so) shrank from 13 MB down to 7.8 MB despite the addition of new structure-learning algorithms, the full causal module, and more.

Additionally, this release consolidates widespread API modernisation (C++20 std::format, string_view, optional_ref, std::optional, concepts) and several breaking renames detailed below.

1. pyAgrum

1.1 Breaking API

  • Causal module — renames and signature changes:

    • CausalModel.observedBN() renamed → observationalBN()
    • CausalModel.addLatentVariable(..., keepArcs=...) — parameter keepArcs renamed → assumeNonSpurious
    • CausalModel.backDoor(), frontDoor() — return None instead of raising when no valid set exists; set() when the empty set is valid.
    • DoorCriteria: EnumerationOptions removed; options (excluded_nodes, max_cardinality, only_minimal, stopAtFirst) are now direct keyword parameters; all methods are now static.
    • CausalImpact: direct access to result field removed — use impact().
    • Counterfactual: getResult()impact().
    • DSeparation renamed → Separation.
  • BNClassifier — API change:

    • BNClassifier now requires a DiscreteTypeProcessor as a constructor parameter.
    • Factory function createBNClassifier signature changed accordingly.
    • Binary prediction dispatch and predict_proba updated; model() method added (returns a copy of the underlying BN).
    • Notebooks and tests updated to the new API.
  • Graph — path-finding functions return None when no path exists:

    • undirectedPath, directedPath, directedUnorientedPath, mixedOrientedPath, mixedUnorientedPath now return None instead of raising NotFound (or returning []) when no path exists.
  • Type aliases removed (ctbn, clg):

    • NodeId and NameOrId type aliases in pyagrum.ctbn and pyagrum.clg have been removed. Use int for NodeId and int | str for NameOrId.
  • Pickle metadata:

    • _gum_add_properties_while_getstate_ removed; pickle metadata now handled by updateMetaData(). Pickled objects from earlier versions may not unpickle correctly.

1.2 New Functionalities

  • 100% docstring coverage:

    • Added %feature(docstring) entries for all 2174 methods; docstrings follow NumPy format.
  • Native GUM format (jgum / bgum):

    • Added saveGUM(), loadGUM(), saveGUMstring(), loadGUMstring() to BayesNet, InfluenceDiagram, and MarkovRandomField.
    • Added SWIG type mapping for optional<vector<NodeId>>.
    • Added GumFormatTestSuite covering jgum/bgum round-trips and string serialization.
    • Added Sphinx page jgum-bgum-format.rst with format reference and usage examples.
    • Added notebook 91-Tools-LoadAndSaveGraphicalModels illustrating all I/O formats.
  • Causal Integration:

    • Added SWIG Python bindings for the C++ causal module (CausalModel, CausalFormula, DoorCriteria, Counterfactual, …).
    • Refactored causalEffectEstimation as a top-level subpackage.
    • Fixed type references in docstrings (pyagrum.causal.CausalModelpyagrum.CausalModel).
    • Restructured causality section in Sphinx documentation.
    • Updated causality notebooks.
    • Added causal2graph utility for causal graph visualization.
    • Added AST printing for CausalImpact results.
  • New learning algorithms:

    • FCI and PAG: added SWIG bindings for PAG (Partial Ancestral Graph) and the FCI (Fast Causal Inference) algorithm; exposed BNLearner.useFCI(), fciExhaustiveSepSet() / setFCIExhaustiveSepSet(); added documentation and test suite for PAG/FCI.
    • PC algorithm: exposed BNLearner.usePC(), integrating constraint-based structure learning alongside Miic and FCI, with setPCAlpha(), setPCStable(), setPCMaxCondSetSize(), setPCUnshieldedColliderSorted().
    • GreedyThickThinning: exposed BNLearner.useGreedyThickThinning() for score-based structure learning, with setGreedyThickThinningReversals().
    • GreedyHillClimbing — triangle deletions: exposed BNLearner.useExtendedGreedyHillClimbing() and allowArcTriangleDeletions(), adding arc-triangle deletion moves to the existing add/reverse/delete arc operators.
  • qBNSampling (experimental, thanks to Tibor Dubois, Thierry Rioual, Mehmet Gunes):

    • New pyagrum.qBNSampling module: quantum circuit encoding of Bayesian Networks and rejection-sampling inference.
  • CLG (Conditional Linear Gaussian):

    • Added CLG.asDiscreteBN() method to convert a CLG model to a discretized BN.
    • Added CLG.__eq__, __getstate__, __setstate__ (pickle support).
    • New CLGModelTestSuite and extended CLGInference/SEM/Sampling/Random test suites.
    • randomCLG gains max_parents and ratio_arc parameters; GaussianVariable sigma guard added (thanks to Ima Bernada).
  • BNClassifier:

    • Added model() method returning a copy of the underlying BN.
    • Full coverage BNClassifierTestSuite.
  • StructuralMetrics:

    • Exposed StructuralMetrics (formerly StructuralComparator) with SHD, tp/fp/fn/tn accessors and SID (Structural Intervention Distance) for BN comparison.
    • Updated GraphicalBNComparator to delegate to StructuralMetrics; structuralFScore added.
  • Tensor:

    • Tensor methods random(), randomDistribution(), randomCPT(), noising() now return self for chaining (via CHANGE_THEN_RETURN_SELF).
    • Python bindings for mean(), variance(), stdDev(), isNumerical().
    • Numpy interop: new as_nparray(), toarray(), and fillWith(ndarray) methods.
  • Graph API extensions:

    • Exposed nameFromId(), idFromName(), setName(), hasName() on all graph types (DiGraph, DAG, MixedGraph, …).
    • Added connectedComponents() (returns dict[int, int]), connectedComponentsList(), and connectedComponentsCount().
    • Fixed missing descendants() / ancestors() on DiGraph; NodeSet, ArcSet, EdgeSet returns now use dedicated typemaps.
  • SWIG sentinel typedefs:

    • Added sentinel typedefs to replace -> object with precise Python return type annotations in generated bindings.

1.3 Improvements

  • Performance & Data Handling:

    • Optimized Tensor numpy access: zero-copy __getitem__, memcpy-based __setitem__ (numpy >= 1.7 required).
    • Introduced PYTHONIZED_MARGINALS macro and global numpy import for generated inference code.
    • Lazy-import of pandas via TYPE_CHECKING (~240 ms saved on import time).
  • Type annotations & static analysis:

    • Added full type annotations to all pyLibs (lib/, clg/, skbn/, bnmixture/, ctbn/, causalEffectEstimation/).
    • Added inference type aliases: BNInference, MRFInference, CNInference.
    • Replaced mypy with pyrefly in act guideline; fixed all type errors across pyLibs.
    • MatrixLike/ArrayLike type aliases added to __init__.in.py.
  • skbn / BNClassifier (thanks to Lou Toubiana):

    • Refactored for full sklearn API compliance: ClassifierMixin placed before BaseEstimator in MRO, fitted attributes renamed to trailing-underscore convention (bn_, threshold_, target_, …), validate_data() used for input validation in fit, predict, and predict_proba.
    • fit(X, y) signature is now positional and strictly sklearn-compliant; the old fit(X=None, y=None, data=None, targetName=None) form is removed.
    • Fixed pandas 2.x compatibility: X is cast to object dtype before processing in fit.
    • Suppressed spurious sklearn 'X does not have valid feature names' warning in predict / predict_proba.
  • BNMixture:

    • Manifest-based saveBNM/loadBNM; loadRetroCompatibleBNM for backward compatibility.
    • Fixed saveBNM/loadBNM on Windows (colons in BN names).
  • explain (ShAP / SHALL):

    • API improvements, causal SHAP fix, binary BN test resources added.
    • Fixed uninitialized array in _labelToPos (np.emptynp.zeros).
  • ipython / notebook utilities:

    • Rewritten ipython.py, new jt2graph.py, fixed prepareDot fontcolor.
    • Fixed html2image issues in notebook contexts; improved export cropping.
  • Infrastructure:

    • act install: support no-make mode via cmake --install; --only flag (alias for --build no-make).
    • act test pyAgrum: persistent --test_build_path option.
    • Fixed fastPrototype docstring separator.
    • Removed useless newFactory methods.

2. aGrUM

2.1 Breaking API

  • Causal module — renames and signature changes:

    • CausalModel::observedBN() renamed → observationalBN()
    • CausalModel::addLatentVariable(..., keepArcs=...) — parameter keepArcs renamed → assumeNonSpurious
    • CausalModel::backDoor(), frontDoor() — return type NodeSetstd::optional<NodeSet> (nullopt = no valid set; {} = empty set is valid).
    • DoorCriteria: EnumerationOptions struct removed; options are now direct parameters; all methods are now static.
    • DoorCriteria::nodesOnDirectedPaths(dag, X, Y) — return type NodeSetstd::optional<NodeSet>.
    • CausalImpact: direct access to result field removed — use impact().
    • Counterfactual<GUM_ELEMENT>: template parameter renamed from GUM_SCALAR; getResult()impact().
    • DSeparation (in CM/tools/) renamed → Separation.
  • Core containers — tryGet / tryFirst / trySecond / tryPos:

    • HashTable<Key,Val>::tryGet(key) and related methods:

      Old return typeNew return type
      Val* (nullptr if absent)optional_ref<Val>
      const Val* (nullptr if absent)optional_ref<const Val>
    • gum::optional_ref<T> behaves like std::optional for references (C++26 feature backported). Update callers:

      // before
      if (auto* p = table.tryGet(key)) { use(*p); }
      // after
      if (auto ref = table.tryGet(key)) { use(*ref); }
    • Same applies to tryFirst, trySecond, tryPos.

  • Graph — path-finding functions return std::optional:

    • directedPath, directedUnorientedPath, undirectedPath, mixedOrientedPath, mixedUnorientedPath now return std::optional<std::vector<NodeId>>nullopt when no path exists. Applies to DiGraph, UndiGraph, MixedGraph, PDAG, CliqueGraph, and all GUM_DiGraphable/GUM_UndiGraphable/GUM_MixedGraphable graphs.
  • Signaler — variadic template:

    • Signaler1<A>, Signaler2<A,B>, Signaler3<A,B,C>, … removed. Use variadic Signaler<A>, Signaler<A,B>, Signaler<A,B,C>, … instead.
  • string_view migration (widespread):

    • const std::string& parameters replaced by std::string_view across the public API (BayesNet, variables, I/O readers, learning, …). Passing std::string or string literals remains compatible. Code that stored or compared const std::string& bindings directly may need adjustment.
  • I/O writers — write() no longer const:

    • BNWriter<GUM_SCALAR>::write() (and all subclass writers including GumBNWriter) no longer takes the BN by const reference:

      // before
      void write(std::ostream& output, const IBayesNet<GUM_SCALAR>& bn);
      // after
      void write(std::ostream& output, IBayesNet<GUM_SCALAR>& bn);
    • Required to allow writers to call bn.updateMetaData() before serialization. CredalNet::saveBNsMinMax() is similarly affected.

  • DiscreteGraphicalModel — new base class:

    • IBayesNet and IMarkovRandomField now inherit from DiscreteGraphicalModel, which factors five variable-map accessors (variable, variableNodeMap, nodeId, idFromName, existsInModel) previously duplicated in each interface. Direct subclasses must no longer define those accessors themselves.
  • DiscreteVariable::closestLabel — now virtual:

    • ABI change: recompilation required for any code linking against aGrUM as a shared library.
  • DAGmodel::dag() and UGmodel::graph() — return named copies:

    • dag() and graph() now return value copies (with node names propagated), not const references. Use internalDag() / internalGraph() for the O(1) stable const reference when graph mutation through the model API is not needed. Code that held const auto& bindings to the old return values must switch to internalDag() / internalGraph().
  • StructuralComparator renamed to StructuralMetrics:

    • The class StructuralComparator has been renamed StructuralMetrics. Update all include paths and type names accordingly.

2.2 New Functionalities

  • FCI algorithm and PAG:

    • Added PAG (Partial Ancestral Graph) type for FCI output.
    • Implemented FCI (Fast Causal Inference) algorithm producing a PAG from data.
    • Integrated FCI into IBNLearner/BNLearner API (setAlgorithmFCI).
    • Fixed possibleDSep criterion (Zhang 2008); added exhaustive sepset mode (setFCIExhaustiveSepSet); enforced background knowledge in orientation rules (R1/R2/R9/R10).
  • PC algorithm:

    • Added PC constraint-based structure learning algorithm.
    • Integrated into IBNLearner/BNLearner alongside Miic.
  • GreedyThickThinning algorithm:

    • Added GreedyThickThinning score-based structure learning algorithm.
    • Integrated into IBNLearner/BNLearner.
  • GreedyHillClimbing — triangle deletions:

    • Extended structural constraints with arc-triangle deletion operations.
    • GraphChangesSelector4DiGraph now supports applyArcDeletion, applyArcReversal, applyTriangleDeletion.
    • LocalSearchWithTabuList updated; totalOrder constraint added.
  • Independence tests refactoring:

    • Extracted CachedContingencyCounter base class from Chi2 and G2 implementations.
    • IndependenceTest::statistics() made pure virtual; overridden in IndepTestChi2 and IndepTestG2.
    • Added silent-cell df correction in Chi2/G2 tests; fixed G2 df for sampling zeros.
    • New Chi2TestSuite.
  • ConstraintBasedLearning refactoring:

    • Extracted ConstraintBasedLearning base class from Miic.
    • Extracted CIBasedLearning base class adding scorer-agnostic API (learnPDAG/learnDAG/learnBN).
    • CMI types and comparators moved to Miic; setMutualInformation() injector added.
    • applyStructuralConstraints_ factored into ConstraintBasedLearning.
    • Renamed scores_and_tests/ directory to scores/; updated all includes.
  • Causal Module (CM) (thanks to SCALNYX):

    • Promoted the causal module from pure Python to a first-class C++ module.
    • Introduced CausalModel and CausalFormula.
    • Developed an AST for do-calculus with LaTeX export and evaluation.
    • Added DoorCriteria class for backdoor and frontdoor set enumeration.
    • Implemented ID/IDC algorithms and formula introspection.
    • Added counterfactual and counterfactualModel functions with tests.
  • Native GUM format (jgum / bgum):

    • Added GumBNReader/GumBNWriter, GumIDReader/GumIDWriter, GumMRFReader/GumMRFWriter supporting both JSON (.jgum) and binary (.bgum) serialization.
    • All GUM readers support proceedFromString() and a no-filename constructor for in-memory round-trips.
    • Extracted _readVector_/_writeVector_ helpers to GumBinaryIO.h.
    • Fixed empty-BN jgum serialization; fixed binary writers to open files with ios::binary.
    • Fixed IDReader::proceed() return type (voidSize).
  • Node names in NodeGraphPart:

    • Added optional node name support: nameFromId(), idFromName(), setName(), hasName().
    • Names propagated to toDot() output (format: id:name).
    • checkConsistency() made public; friend declarations for test suites removed.
    • GraphicalModel::_nameNodes_() propagates node names to returned graphs: moralGraph(), moralizedAncestralGraph(), EssentialGraph::pdag() / skeleton(), MarkovBlanket::dag().
  • C++20 graph concepts and generic algorithms:

    • Added C++20 concepts GUM_DiGraphable, GUM_UndiGraphable, GUM_MixedGraphable for graph-agnostic programming.
    • Added generic path/reachability/cycle algorithms operating on any concept-satisfying graph type.
    • Added generic moralization and separation algorithms; DAG/PDAG methods now delegate.
    • Added generic BayesBall algorithm; dSeparated and BayesBall::requisiteNodes delegate.
    • Promoted minimalCondSet, markovBlanket, areConnected to the generic graph layer.
  • StructuralMetrics (formerly StructuralComparator):

    • Renamed StructuralComparatorStructuralMetrics.
    • Added SHD metrics with tp/fp/fn/tn accessors.
    • Added SID (Structural Intervention Distance) for DAG-vs-DAG and BayesNet overload.
    • Name-based alignment for BN compare/SID.
  • BIFXMLBNReader improvements (thanks to Omi Johnson):

    • Added std::istream constructor for in-memory parsing.
    • Now reads the network NAME property from BIF/XML files.
  • Modeling & Core API:

    • Added connectedComponents() to DAGmodel and UGmodel; C++ and Python tests for BN, ID, MRF.
    • Added descendants() and ancestors() to MarkovBlanket and EssentialGraph.
    • Introduced DiscreteGraphicalModel to factor variable management across IBayesNet and IMarkovRandomField.
    • Replaced nullable pointers with optional_ref<T> and std::optional in various interfaces.
    • Added data() method to MultiDimArray for contiguous buffer access.
    • Made DiscreteVariable::closestLabel(double) virtual.
    • Added DiscreteVariable::isNumerical() — returns true iff varType != LABELIZED.
    • Added Tensor::mean(), variance(), stdDev() (fixes variance computation: was E[X²], now E[(X−μ)²]).
    • Added isCloseToZero() / isCloseToOne() helpers; used in Tensor::mean() / variance().
    • Tensor::toString now uses Unicode box-drawing characters (│ ║ ─) for table borders.
  • CN module:

    • Fixed critical bugs: removed invalid delete[] on strtok pointer, fixed setCPT const-ref signature, corrected insertEvidenceFile override placement, fixed operator precedence in LrsWrapper guards.
    • Fixed CNLoopyPropagation: arcs were rebuilt on every access instead of reused, risking ArcsL_min_/ArcsL_max_ desynchronization.
    • Fixed naming convention violations: protected attributes renamed to trailing-underscore convention.

2.3 Improvements

  • Performance:

    • Removed exception-based control flow (try/catch(NotFound)) from hot paths in BayesNetFactory, IBayesNet, ShaferShenoy, LazyPropagation, BNLearner, SimpleMiic, and CN, contributing to the overall execution-time reduction.
  • Code quality & C++20:

    • Replaced std::stringstream with std::format across all modules.
    • Added override specifier to all virtual method overrides across the codebase.
    • Added [[nodiscard]] to clone() and factory methods across all modules.
    • Normalized include guards to the GUM_SOMETHING_H convention across the codebase.
    • Added parent #include in _inl.h/_tpl.h files for IDE LSP support.
  • Portability & warnings:

    • Fixed int2Pow: use uint64_t for portable 64-bit shift on Windows.
    • Fixed GCC 16 warnings: -Warray-bounds pragmas, gum::Size casts for signed/unsigned comparisons.
    • Fixed GCC optimize pragma guard against Clang in BNLearner.
    • Fixed -Wextra-semi and extern template SWIG warnings.
    • Fixed Windows compilation issues (binary writers, CI uninstall).
  • Static analysis:

    • Integrated clang-tidy into act guideline (check tidy / --correction applies fixes).
    • DeepSeek static-analysis audit: fixed all CRIT/HIGH/MED/LOW issues across the codebase.
    • clang-format applied to C++ test suites and BN learning sources.
    • Fixed noexcept on IndepTestChi2/G2 moves, braced-init returns.
  • Learning:

    • MeekRules improvements.
    • Fixed propagateToCPDAG: restored edges-before-arcs insertion order in PDAG.
    • Fixed ArcDeletion bug in GraphChangesSelector4DiGraph.
    • BayesBall (_bayesBall_): replaced exists+insert+[] pattern with getWithDefault; added 9 deterministic tests.
  • Tests:

    • Replaced deprecated tmpnam with getTempFilePath helper across all test suites.
    • Added LpInterface/LrsWrapper tests and marginal sanity checks.
    • Updated doctest from 2.4.12 to 2.5.2; suppressed -Wc2y-extensions.
  • Move semantics:

    • Added move constructors and move assignment operators across the class hierarchy; fixed GUM debug macros and atexit table.
  • CMake:

    • Removed uninstall target and obsolete policies.
    • AVLTree: removed noexcept from move operator= (contains GUM_ERROR).
  • act:

    • --stats flag for project stats; --consolidate split; cm.h and base/io added to dep map.
    • act guideline: --dry-run, --check with +/- syntax, --verbose non-persistent.