Library / SDK
BaseModelAI/cleora avatar
BaseModelAI/cleora

Cleora: exact random walks by matrix multiplication, and what that buys you

Cleora AI is a general-purpose open-source model for efficient, scalable learning of stable and inductive entity embeddings for heterogeneous relational data. Created by Synerise.com team.

544 stars58 forksJupyter NotebookNOASSERTION

At a glance

What is it?
Cleora is a Python package with a compiled Rust core that computes entity embeddings by iterative Markov propagation with whitening, avoiding negative sampling and GPUs. The design is deterministic and cheap to install, but the repository's own claims about accuracy and speed come from the project's benchmark page, not from independent measurement.
Who is it for?
Adopt Cleora if you need reproducible entity embeddings from typed TSV relations and cannot justify a GPU or a PyTorch install, and if you are willing to validate the embedding quality on your own graph rather than trusting the benchmark headline. Do not adopt it if your task depends on node features or learned message passing, which the spectral propagation approach here does not provide.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 166 days ago.
What is it written in?
Mainly Jupyter Notebook, 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 problem Cleora targets: embeddings from relations, without a GPU or a random seed

The input Cleora expects is a set of relations, not a feature matrix. The README's quick start passes an iterator of space-separated edge strings such as "alice item_laptop" and "bob item_keyboard", and the step-by-step example builds the same structure from a pandas DataFrame of customer and product columns. That is the shape of the problem: you have interaction logs, co-purchase records, or a bipartite graph, and you want a dense vector per entity. The intended users are engineers running recommender or similarity pipelines, plus researchers who need to rerun an experiment and get the same numbers. The README states the pitch directly: the library computes all possible random walks in a single matrix multiplication, with no negative sampling and no GPU. That combination is the whole argument for the project. It is not trying to be a general graph neural network framework. It is a narrow tool for producing entity vectors from typed relations, and the package size claim of roughly 5 MB with no CUDA dependency tells you who it is aimed at: teams that do not want a deep learning stack in the dependency tree of a production service.

Markov propagation with per-iteration whitening, and why determinism falls out of it

The mechanism is visible in the manual example. You construct a SparseMatrix from an iterator and a column specification string, in the README's case complex::reflexive::product. You then call initialize_deterministically(256) to get a starting embedding matrix. The loop that follows is the algorithm: left_markov_propagate, then row-wise L2 normalization with numpy, then whiten_embeddings, repeated for NUM_ITERATIONS, which the example sets to 40. Nothing in that loop samples. There is no skip-gram window, no negative sampling draw, no dropout. Each iteration is a sparse matrix product followed by two deterministic transforms. That is why the README can claim the same input always produces the same output, and it is the strongest technical claim in the material because it follows from the construction rather than from a benchmark. The default embed() function wraps this loop with feature_dim=256, num_iterations=40, and whitening after every propagation step, and release v3.2.1 is titled Default to interwhiten, which indicates the default whitening behaviour was changed in that release. The column string is where the heterogeneous part lives: complex::reflexive::product tells the parser how to interpret the typed columns, which is how multi-type nodes and edges, bipartite graphs and hypergraphs are handled without a separate preprocessing step.

Getting it running: pip, the embed() call, and the CLI subcommands

Installation is a single command, pip install pycleora, with optional extras for visualization (pycleora[viz] pulls matplotlib) and a full set (matplotlib, networkx, tqdm). The Python path is three lines: import SparseMatrix, embed and find_most_similar from pycleora, build the graph with SparseMatrix.from_iterator, call embed(graph, feature_dim=256, num_iterations=40), then query neighbours with find_most_similar(graph, embeddings, "alice", top_k=5), which returns dictionaries with entity_id and similarity keys. The CLI mirrors this: pycleora embed --input graph.tsv --output embeddings.npz --dim 256 --iterations 40, plus pycleora info --input graph.tsv to inspect the parsed graph, pycleora similar --input graph.tsv --entity alice --top-k 10 for neighbour queries, and pycleora benchmark --dataset karate_club. The algorithm is a parameter, not a separate install: --algorithm cleora, prone, node2vec and the others listed in the README all run through the same embed subcommand. If you need control over the propagation rather than the wrapper, the manual loop shown above is the escape hatch, and it is short enough to copy. One thing the README does not show is the exact TSV layout that the CLI expects for typed columns, so the info subcommand is the fastest way to confirm your file parses as intended before running a full embedding job.

Where the approach breaks down: node features, transductive limits, and the whitening default

Cleora embeds structure, not attributes. If your entities carry text, images or numeric profiles and the signal lives in those attributes, spectral propagation over the relation graph has nothing to propagate them with. GraphSAGE and similar message-passing models consume node features; this does not. The README's inductive claim, that new nodes can be embedded without retraining the entire graph, is stated but not demonstrated in the supplied material, and it deserves a test on your own data before you build an incremental pipeline around it. The whitening step is another place to be careful. Applying whitening after every propagation step is a strong transform, and v3.2.1 changed the default to interwhiten, which means embeddings produced by earlier versions are not directly comparable to embeddings produced now. If you have stored vectors from a previous run, an upgrade can invalidate them for similarity search unless you re-embed. The README also lists a supervised refinement mode that fine-tunes unsupervised embeddings with positive and negative entity pairs, but the description is cut off mid-sentence in the material, so the pairing format and loss are not something I can describe. Treat that feature as undocumented until you read the full docs page.

The bundled alternatives and how they differ in approach

The same package ships ProNE, RandNE, NetMF, DeepWalk, Node2Vec, HOPE, GraRep and a two-layer MLP classifier written in numpy and scipy. Switching is one flag. The differences are real and worth stating. DeepWalk and Node2Vec approximate random walks with skip-gram and negative sampling, which introduces stochasticity that Cleora's exact matrix multiplication avoids; Node2Vec additionally exposes BFS/DFS bias parameters that Cleora has no equivalent for. NetMF factorizes the DeepWalk matrix explicitly, which the README contrasts with Cleora on memory, claiming 50x less. ProNE uses Chebyshev polynomial approximation for spectral propagation, and RandNE uses Gaussian random projection, which is the fastest and most approximate of the set. HOPE and GraRep are matrix factorization methods preserving different proximity definitions. Having them behind one API is useful for a sanity check: if Cleora and NetMF disagree sharply on your graph, the disagreement is informative. But the comparison only holds if you keep the input and evaluation split fixed, and the README's benchmark table, including the 240x figure against GraphSAGE attributed to Zomato, is a vendor claim from the project's benchmark page rather than an independent result.

Maintenance, versioning and the licence question

Release cadence is uneven. v2.0.0 landed in November 2024, then v3.2.0 and v3.2.1 arrived within two days of each other in late March and early April 2026. That gap suggests a long quiet period followed by a concentrated rewrite, and the v3.2.1 default change to interwhiten confirms that the 3.x line is not output-compatible with 2.x. Pin your version in requirements and record which one produced any embedding you store. The repository metadata reports the licence as NOASSERTION, which means GitHub could not match the licence file to a known identifier. The README and the metadata do not state the terms in plain terms, and the project is described as created by the Synerise.com team. For internal experimentation this rarely matters; for redistribution or for shipping the compiled Rust extension inside a commercial product, read the actual licence file in the repository before you depend on it. I cannot tell you what it permits from the material available, and this is not legal advice.

Who should pick Cleora up, and the first thing to check

The case for Cleora is strongest when the graph is the whole signal, the install footprint matters, and reproducibility is a requirement rather than a preference. A recommendation service that already stores user-item interactions as rows, and that wants vectors it can regenerate byte-for-byte on every deploy, is the natural fit. So is a research pipeline where rerunning an experiment must give identical numbers. The case is weak when entities have rich features, when you need learned aggregation over neighbourhoods, or when you need the biased walk behaviour that Node2Vec exposes. The accuracy and speed claims in the README come from the project's own benchmark page and from a customer anecdote, so the honest first step is not to trust the headline but to run pycleora info on your TSV to verify the column types parse correctly, then run pycleora embed twice with the same arguments and diff the .npz output to confirm determinism holds on your data. If those two checks pass, the rest of the decision is about whether structural embeddings are enough for your task.

Editorial conclusion

Adopt Cleora if you need reproducible entity embeddings from typed TSV relations and cannot justify a GPU or a PyTorch install, and if you are willing to validate the embedding quality on your own graph rather than trusting the benchmark headline. Do not adopt it if your task depends on node features or learned message passing, which the spectral propagation approach here does not provide. Before committing, run pycleora info on your input to confirm the column types are parsed as you expect, then compare embed() output against one of the bundled alternatives such as --algorithm node2vec on a held-out link prediction split, because the README's accuracy claims are the project's own and the licence file is not machine-readable.

Official sources

  1. BaseModelAI/cleora on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Community notes

Community notes