Library / SDK
xunzheng/notears avatar
xunzheng/notears

NOTEARS: learning DAGs without a combinatorial search

DAGs with NO TEARS: Continuous Optimization for Structure Learning

693 stars137 forksPythonApache-2.0

At a glance

What is it?
The notears repository implements the NOTEARS algorithm, which turns Bayesian network structure learning into continuous optimisation over real matrices so the acyclicity constraint never has to be checked. The reference implementation is a single file of under 60 lines, the newest tagged release is from 2020, and the authors now point users at a newer gradient-based library for anything beyond teaching the method.
Who is it for?
Use this repository if you want a readable reference implementation of NOTEARS, if you are teaching or prototyping the method, or if you need to check what the acyclicity formulation actually buys you in practice. Do not start new research on top of it, because the README directs anyone interested in faster and more accurate structure learning to the dagrad library for newer differentiable methods, and the newest tagged release here is v2.3 from 2020-12-17.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 150 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem the acyclicity constraint creates

A directed acyclic graphical model, also known as a Bayesian network, with d nodes defines a distribution of a random vector of size d. The problem the repository addresses is Bayesian Network Structure Learning: given n samples from such a distribution, how do you estimate the graph G?

The obstacle is the acyclicity constraint, and the README calls it combinatorial. That is the whole difficulty in one word. A directed graph on d nodes has an enormous number of possible edge sets, and the subset of them that are acyclic is still enormous. Existing approaches rely on local heuristics, which means they search over a restricted neighbourhood of graphs and accept whatever they find.

The strategy introduced in the paper is fundamentally different: formulate the problem as a purely continuous optimization problem over real matrices that avoids the combinatorial constraint entirely. The mechanism is a function h whose level set exactly characterizes the space of DAGs, and the important word in that sentence is smooth. Because h is smooth, you can differentiate through it, and you can optimise the whole thing with a gradient method on unconstrained real parameters. The acyclicity requirement stops being a thing you check or penalise and becomes part of the objective.

That reformulation is why the implementation can be so small. There is no search over topologies, no topological sort inside the loop, no combinatorial penalty with a tuning weight. You solve a smooth problem and the acyclicity falls out of the solution.

Sixty lines that implement the whole method

The headline of the README is that structure learning takes fewer than 60 lines, and the file that delivers it is linear.py. The implementation covers L2, Logistic and Poisson loss functions with an L1 penalty.

The supporting files are listed in the contents section, and each one is small and single-purpose. linear.py is the 60-line implementation with L1 regularisation for the various losses. nonlinear.py is nonlinear NOTEARS using an MLP or basis expansion. locally_connected.py is the special layer structure used for the MLP. lbfgsb_scipy.py is a wrapper for scipy's LBFGS-B. utils.py holds graph simulation, data simulation and accuracy evaluation.

That last file matters for reading the examples, because it means the accuracy metrics in the output are computed against a graph the same code generated, and the data is simulated rather than real. The evaluation is self-contained by design, which is what makes the repository usable as a teaching artefact.

The solver choice also tells you something. L-BFGS-B is a bound-constrained quasi-Newton method, and it is what NOTEARS uses for the linear case, which fits a problem where the parameters are the entries of a weight matrix and the only awkwardness is the smoothness of the constraint. The SciPy wrapper exists because the optimisation loop has to hand back a value and gradient that the SciPy optimiser expects, and because the linear model's Hessian-vector products are computed by a custom routine.

Running the demo and using your own data

The quickest way to see it work is to run the shipped example, which needs a clone and one script:

bash
$ git clone https://github.com/xunzheng/notears.git
$ cd notears/
$ python notears/linear.py

That runs the L1-regularised NOTEARS on a randomly generated 20-node Erdos-Renyi graph with 100 samples, and the documentation says that within a few seconds you should see output like this:

code
{'fdr': 0.0, 'tpr': 1.0, 'fpr': 0.0, 'shd': 0, 'nnz': 20}

The four keys are the structural error metrics: false discovery rate, true positive rate, false positive rate and structural Hamming distance, with nnz counting the non-zero entries in the estimated weight matrix. The data, the ground truth graph and the estimate are written to X.csv, W_true.csv and W_est.csv, so you can inspect all three afterwards.

If you already have a CSV of data, the package installs a command. The README calls this the alternative to running the script, and it needs pip install git+git://github.com/xunzheng/notears followed by notears_linear X.csv, with the output graph stored in W_est.csv.

The package manifest shows what gets installed. The name is notears, the version is 3.0, the licence is Apache License 2.0, and the install_requires are numpy, scipy and python-igraph. Two scripts are installed, notears_linear and notears_nonlinear, and the package is classified as Development Status 3 - Alpha with the Intended Audience of Science/Research.

The requirements list in the README adds detail to that. Python 3.6 or newer, numpy, scipy, and python-igraph, with a note that you need to install the igraph C core and pkg-config first. Torch is optional and only used for the nonlinear model.

What the Erdos-Renyi and scale-free examples show

The README runs two families of experiments and the point of both is the same: the L1 penalty matters, and the method does not care what the graph looks like.

For the Erdos-Renyi case the ground truth has d = 20 nodes with 2d = 40 expected edges, and the estimates are run with n = 1000 samples at lambda = 0, lambda = 0.1, and with FGS as a baseline. The observation is that both lambda = 0 and lambda = 0.1 are close to the ground truth when n is large. With n = 20 samples the picture changes: lambda = 0 performs worse while lambda = 0.1 remains accurate, which the README presents as the advantage of L1 regularisation.

The scale-free case uses d = 20 nodes with 4d = 80 expected edges, and the degree distribution is significantly different from the Erdos-Renyi graph. One nice property of the method is that it is agnostic about the graph structure, and the same pattern of results holds: with n = 1000 both settings accurately estimate the ground truth, and with n = 20 lambda = 0 suffers while lambda = 0.1 remains accurate.

Two things follow from this. The first is that sparsity is not a cosmetic choice, it is what makes the estimator work in the small-sample regime, because an unregularised continuous solution will happily keep a dense set of small weights and the thresholding step then has to guess where to cut. The second is that the method does not assume a particular graph family, which is a meaningful claim given that a lot of structure learning assumes sparsity or a particular degree distribution.

The honest caveat about all of this is that these are simulation experiments on graphs the same code generated. They show the estimator recovers what it was given, not that it works on real data with real dependencies.

The authors have moved on to a different library

The first line of the README is an update notice, and it points somewhere else. If you are interested in faster and more accurate structure learning, it says to see the new dagrad library for developing and experimenting with newer differentiable, gradient-based structure learning methods.

That single line tells you most of what you need to know about the state of this repository. The reference implementation here remains valuable, because NOTEARS is the paper that made continuous structure learning a mainstream approach, and the repository is where most people meet it. But the people who wrote it are pointing new work elsewhere, and the release history bears that out.

The tags are v2.3 from 2020-12-17, v2.2 from 2020-03-07 and v2.1 from 2019-11-17. The last push to the repository was on 2026-05-26, so the branch is not dead, but there has been no tagged release since 2020. The version in setup.py is 3.0, which does not correspond to any of those tags, so the packaging metadata and the release history are not in step.

The repository implements two papers: the 2018 NeurIPS spotlight on DAGs with NO TEARS, and the 2020 AISTATS paper on learning sparse nonparametric DAGs. The README also asks users to cite the papers, with two BibTeX entries provided, and gives the arXiv links for both.

The practical reading is that this is a stable artefact of a finished piece of research, and the recommended starting point for new work is dagrad.

Where this repository is the wrong tool

The first limitation is the recommendation itself. If your goal is structure learning that works on real data today, this repository is the one its own authors tell you not to use, and the pointer to dagrad is the specific place to look.

The second limitation is the dependency on igraph. The requirements call for the igraph C core and pkg-config to be installed first, and the Dockerfile confirms it, building on ubuntu:18.04 with libigraph0v5 and libigraph0-dev among the apt packages. The igraph dependency is there for graph utilities, and igraph is a C library with its own build story. On a modern machine that story has moved on, and the base image pinned in the Dockerfile is long out of support, so the environment is a small project of its own to reproduce.

The third limitation is scale. The demonstrations are 20 node graphs with 20 or 1000 samples. The formulation scales the way continuous optimisation scales, which is well in principle and less well in practice as the dimension of the weight matrix grows quadratically with the number of nodes. Nothing in the documentation discusses what happens at a few hundred nodes, so if your data has many variables you should establish that yourself before designing around it.

The real alternatives are the ones the field offers, and the difference in approach is the point. The classical alternatives, greedy score based search and the FGS baseline used in the examples, are combinatorial but scale differently and are often more accurate on real data with strong sparsity. The newer differentiable methods, which is what dagrad is for, keep the continuous formulation but improve the optimisation and the identification guarantees. And a completely different approach is to use a constraint-based method, PC or GES, which asks conditional independence questions rather than optimising a likelihood, and which is what you want when the data is small and the assumptions of a score are doubtful. This repository remains the clearest possible statement of what the continuous idea looks like in code, and that is a real reason to read it, but it is not where you should start a new project.

Editorial conclusion

Use this repository if you want a readable reference implementation of NOTEARS, if you are teaching or prototyping the method, or if you need to check what the acyclicity formulation actually buys you in practice. Do not start new research on top of it, because the README directs anyone interested in faster and more accurate structure learning to the dagrad library for newer differentiable methods, and the newest tagged release here is v2.3 from 2020-12-17. Before you build on it, check the igraph and Python dependency requirements, since the installation notes call for the igraph C core and pkg-config first, and decide whether the 20 node random graph in the demo tells you anything about your own data.

Frequently asked questions

What is the notears repository?

It is an implementation of the NOTEARS algorithm, which learns directed acyclic graph structure by formulating it as a continuous optimisation problem over real matrices instead of a combinatorial search. It implements the 2018 NeurIPS paper on DAGs with NO TEARS and the 2020 AISTATS paper on sparse nonparametric DAGs, under Apache License 2.0.

How do I run a NOTEARS demo?

Clone the repository, change into the notears directory and run python notears/linear.py. That runs the L1-regularised algorithm on a randomly generated 20-node Erdos-Renyi graph with 100 samples, and writes the data, the ground truth graph and the estimate to X.csv, W_true.csv and W_est.csv.

What dependencies does notears need?

The requirements list Python 3.6 or newer, numpy, scipy and python-igraph, with a note that the igraph C core and pkg-config must be installed first. Torch is optional and only used for the nonlinear model, and the package manifest lists numpy, scipy and python-igraph as install_requires.

Is notears still being developed?

The README opens with an update pointing to the dagrad library for newer differentiable structure learning methods, and the newest tagged release is v2.3 from 2020-12-17. The last push to the repository was on 2026-05-26, and the version in setup.py is 3.0, which does not match any of those tags.

What files does the notears package contain?

linear.py is the 60-line implementation of NOTEARS with L1 regularisation for various losses, nonlinear.py is the nonlinear version using an MLP or basis expansion, locally_connected.py is the layer structure used for the MLP, lbfgsb_scipy.py wraps SciPy's LBFGS-B, and utils.py handles graph simulation, data simulation and accuracy evaluation.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. xunzheng/notears on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xunzheng-notears.svg)](https://hysenlabs.com/projects/xunzheng-notears)

Was this useful?