Framework
scallop-lang/scallop avatar
scallop-lang/scallop

Scallop: a Datalog dialect where rules carry probabilities and gradients

Framework and Language for Neurosymbolic Programming.

509 stars36 forksRustMIT

At a glance

What is it?
Scallop is a Rust-implemented language and framework for neurosymbolic programming, built on a generalized provenance semiring. It fits teams that already have a neural model producing distributions and want logical rules on top, and it is the wrong tool for anyone who just needs a fast plain Datalog solver.
Who is it for?
Adopt Scallop if your pipeline already produces probability distributions over symbols and you want to express the combinatorial part as rules rather than as tensor operations. Do not adopt it if you only need plain Datalog evaluation, or if you cannot pin a nightly Rust toolchain and Python 3.8 in your build environment.
Can I use it commercially?
Yes. MIT 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 82 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

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

DEEP OPEN-SOURCE ANALYSIS

The gap Scallop targets: symbolic rules over uncertain inputs

Most machine learning pipelines that need combinatorial reasoning end up encoding it as tensor operations. Summing two recognized digits, or counting the objects in a scene, becomes a hand-written indexing trick. Scallop's premise is that this part should be a program. The README describes it as "a language based on DataLog that supports differentiable logical and relational reasoning," and the example it leads with is exactly that shape: facts about animal taxonomy, a recursive rule deriving the name of an animal at a higher level of the hierarchy, and a probabilistic block where an image classifier has assigned 0.3 to one label and 0.7 to another. The final rule counts animals with an aggregation, n = count(o: name(o, "animal")).

The audience is narrow and specific. It is researchers and engineers who already have a neural component emitting distributions or scores, and who want the relational and combinatorial layer above it written declaratively. It is not a general-purpose machine learning framework, despite the repository topic tag. There is no training loop, no optimizer, no dataset abstraction in the material. What it offers is a place to put the logic.

Provenance semirings are the actual mechanism

The single design decision that explains everything else is stated in one line of the README: "Internally, Scallop is built on a generalized Provenance Semiring framework." A provenance semiring is an algebraic structure attached to each derived fact, tracking not just whether the fact holds but how it was derived. Because the semiring is configurable, the same rule set can be evaluated under different interpretations. The README names three: discrete logical reasoning, probabilistic reasoning, and differentiable reasoning.

This is why the probabilistic example needs an explicit flag. Running scli on a probabilistic program without it is not an error in the tool; the default interpretation is discrete, and under a discrete semiring the probabilities are simply not the thing being computed. The README says plainly that "by default we don't accept probabilistic input," and the fix is to pass -p minmaxprob, described as "a simple provenance semiring that allows for probabilistic reasoning." The PyTorch example uses a different one, difftopkproofs, constructed as scallopy.ScallopContext(provenance=provenance, k=k). The k parameter is part of that semiring's configuration, not a general Scallop setting.

The consequence for anyone reading the source or the docs: provenance is not a debugging feature bolted on afterwards. It is the evaluation model. Choosing a semiring is choosing what your program computes, and the material does not enumerate the full set of available semirings or their exact semantics. That is a real gap if you are trying to pick one for a non-standard use case.

Three binaries, one Python binding, and how to build them

The prerequisite is explicit and unusual: Rust on the nightly channel, set as default. The README gives the rustup commands to install and then `rustup default nightly`. If your organization pins a stable toolchain, this is a friction point at the very first step, and there is no statement in the material about which nightly versions are known to work.

From a clone, three binaries are available through make targets. `make install-scli` builds the interpreter, `make install-sclc` the compiler, and `make install-sclrepl` the REPL. The interpreter runs a .scl file directly: `scli examples/animal.scl`. The REPL is installed separately via `cargo install --path etc/sclrepl`, and its session in the README shows relations being declared, a recursive path rule typed with `\/` for disjunction and `/\` for conjunction, and a query returning the transitive closure of a two-edge graph.

The Python binding is a separate build. The README instructs creating a virtual environment first (make py-venv, or a Conda environment on Python 3.8), then `pip install maturin`, then `make install-scallopy`. The verification step it suggests is running `python etc/scallopy/examples/edge_path.py` from inside that environment. Note the Python version: 3.8 is what the README specifies, and there is no statement about newer versions.

There is also a VSCode plugin built from source with `make vscode-plugin`, which requires npm and vsce and produces a .vsix file in etc/vscode-scl.

The PyTorch integration is where the interesting constraint lives

The MNIST sum example is the clearest statement of what Scallop is for. A standard MNIST network produces two distributions over ten digits. Those are fed into the reasoning module as named inputs, digit_1 and digit_2, and a single rule sum_2(a + b) = digit_1(a), digit_2(b) computes the distribution over sums. The output is described in a comment as a size 19 tensor, and the forward function returns it directly.

The constraint is in how the relation is declared. `add_relation("digit_1", int, input_mapping=list(range(10)))` binds a Scallop relation to a fixed input mapping, and the output is materialized with `forward_function("sum_2", list(range(19)))`. Both the input and output dimensions are written by hand in the Python code, not inferred from the program. Add a third digit and you are editing the range arguments yourself. For a fixed task this is fine. For a system where the number of recognized objects varies at runtime, the material does not show how you would express that, and the fixed-range pattern suggests it is not the intended shape.

The other thing worth noting is that the reasoning module is constructed in __init__ and called in forward, so it participates in the module tree like any other layer. Whether gradients flow back through the logical rules into the digit network is exactly what the differentiable semiring is for, but the README does not state which parameters receive gradients or how the semiring's k interacts with that.

Where Scallop is the wrong choice

If you have a purely discrete problem, Scallop is a heavier way to get a Datalog engine. The README itself offers the fallback: "You can also use it as another DataLog solver." But the cost of that mode is the whole build chain: a nightly Rust toolchain, a make-based install, and for the Python path, maturin and a pinned Python 3.8 environment. If you never touch a probabilistic or differentiable semiring, you are paying for machinery you are not using.

The second case is scale. The README's examples are small: a two-edge graph, a three-level animal taxonomy, two MNIST digits. Nothing in the material discusses performance characteristics, memory behavior on large fact sets, or how the semiring evaluation scales with rule depth. Aggregations like count appear in the example, and aggregations over probabilistic derivations are the kind of thing where the cost is not obvious from the syntax. Treat any large-fact-base use as unverified until you have measured it yourself.

The third case is toolchain rigidity. A nightly-only Rust requirement plus a Python 3.8 requirement is a combination that many production build systems will reject outright, and the material gives no timeline for when either constraint might relax. The most recent release listed is 0.2.4 from August 2024, so the versioning is still in the 0.x range.

How it differs from ProbLog and from plain Datalog engines

The nearest well-known point of comparison is ProbLog, which also extends logic programming with probabilities. The difference is architectural. ProbLog's probabilistic semantics are built into the language. Scallop's are one semiring among several, selected at evaluation time via the -p flag or the provenance argument to ScallopContext. That means the same .scl file can be run discretely, probabilistically, or differentiably without rewriting the rules, which is a genuinely different property and the main reason to prefer Scallop for a neurosymbolic pipeline where the same logic needs to appear in both a discrete inference pass and a differentiable training pass.

Against a plain Datalog engine such as Souffle, the difference runs the other way. Souffle is built for compiling Datalog to fast native code over large fact sets, and it has no notion of a provenance semiring or a Python tensor interface. Scallop's Rust core and compiler binary suggest it is aware of that world, but the material does not make any performance claim relative to it, and none should be assumed. If your problem is discrete and large, the plain engine is the more direct answer.

One more comparison worth making: against writing the combinatorial layer as tensor operations in PyTorch directly, Scallop trades some flexibility for declarativeness. The sum_2 rule is far shorter than the equivalent index arithmetic, but the fixed input_mapping and output range mean you have not escaped shape bookkeeping entirely. You have moved it.

Maintenance, licensing, and what to check before you commit

The licence is MIT, which is permissive and imposes no copyleft obligation on your own code. That is a straightforward answer to the licensing question, and it is the one thing in this review that requires no caveat. This is not legal advice; read the licence text in the repository if the distinction matters to your organization.

On maintenance, the release history in the material shows 0.2.0 in September 2023, 0.2.1 in October 2023, and 0.2.4 in August 2024. The repository is not archived and the last push timestamp is recent, so development has not stopped. But the version numbers are still 0.x, which in most ecosystems means the API is not promised to be stable across minor releases. If you build against scallopy, budget for the possibility that a 0.3 release changes the context construction API.

Upgrade cost is dominated by the environment rather than the code. Because the build requires a nightly Rust toolchain, an upgrade of Scallop may pull in a nightly that has moved, and because scallopy is built with maturin against a specific Python version, a Python upgrade in your environment is a rebuild at minimum. Neither of these is a code migration, but both are recurring operational work that a pure-Python dependency would not impose.

The concrete first step is to build the interpreter and run one of the shipped examples before writing any rules of your own: `make install-scli` followed by `scli examples/animal.scl`, then the same file with `-p minmaxprob` to see how the output changes under a different semiring. That single comparison tells you more about whether the provenance model matches your problem than any amount of reading the documentation.

Editorial conclusion

Adopt Scallop if your pipeline already produces probability distributions over symbols and you want to express the combinatorial part as rules rather than as tensor operations. Do not adopt it if you only need plain Datalog evaluation, or if you cannot pin a nightly Rust toolchain and Python 3.8 in your build environment. Before committing, verify three things from the repository itself: that make install-scli succeeds on your platform, that scallopy builds against your PyTorch version, and that your chosen provenance semiring actually produces the output shape your downstream code expects.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. scallop-lang/scallop on GitHub
Community notes

Community notes