ZeroFolio
Algorithm selection with zero domain knowledge via text embeddings.
ZeroFolio selects the best algorithm for a problem instance using a three-step pipeline:
- Serialize — read the raw instance file as plain text (
zf.serialize) - Embed — pass the text through any pretrained embedding model (user-provided)
- Select — pick the best algorithm via weighted k-nearest neighbors (
zf.KNNSelector)
No feature engineering, no domain expertise, no training required. ZeroFolio handles serialization and selection; you bring your own embedding API.
Paper: arXiv:2604.19753 (AutoML 2026).
Supplementary material (data, embeddings, experiment tool):
doi:10.5281/zenodo.21296904.
From version 0.1.1 on, zf.serialize with line_shuffle produces the same text
as the paper's experiments for a given seed (0.1.0 shuffled differently).
Installation
pip install zerofolio
Verify Installation
A quick smoke test with synthetic data (no API key needed):
import numpy as np
import zerofolio as zf
# Synthetic embeddings and runtimes for 50 instances, 4 algorithms
X_train = np.random.rand(50, 128)
rt_train = np.random.rand(50, 4) * 100
selector = zf.KNNSelector(k=5)
selector.fit(X_train, rt_train)
X_test = np.random.rand(3, 128)
print(selector.predict(X_test)) # e.g. array([2, 0, 3])
Quick Start (with Gemini Embeddings)
A full example using Google's Gemini embedding model.
Requires: pip install google-genai
import os
import numpy as np
import zerofolio as zf
from google import genai
# --- Step 1: Serialize instance files ---
# Reads the file as plain text, shuffles lines, truncates to budget.
text = zf.serialize("instance.cnf", strategy="line_shuffle", max_chars=10000, seed=42)
# --- Step 2: Embed (user-provided) ---
# ZeroFolio does not call any API itself. You provide an embedding function.
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
def embed(text):
result = client.models.embed_content(
model="gemini-embedding-2-preview", # Gemini Embedding 2, as in the paper
contents=text,
)
return result.embeddings[0].values
# Embed all your training instances into numpy arrays:
# For each instance file, call zf.serialize() then embed().
# train_embeddings: np.array of shape (n_train, 3072)
# train_runtimes: np.array of shape (n_train, n_algorithms)
# Each row gives the runtime (seconds) of each algorithm on that instance.
# Use PAR10 values (10× cutoff for unsolved) for best results.
train_embeddings = np.array([embed(zf.serialize(f)) for f in train_files])
# --- Step 3: Select via k-NN ---
selector = zf.KNNSelector(k=10, metric="manhattan")
selector.fit(train_embeddings, train_runtimes)
test_embedding = np.array([embed(text)])
best_algo_idx = selector.predict(test_embedding)
print(f"Selected algorithm index: {best_algo_idx[0]}")
Using Algorithm Names
Pass algorithm names to get predictions as strings instead of indices:
algos = ["minisat", "glucose", "cadical", "kissat"]
selector = zf.KNNSelector(k=10, algorithm_names=algos)
selector.fit(train_embeddings, train_runtimes)
print(selector.predict(test_embedding)) # e.g. ["glucose"]
Multi-Seed Voting
Different random seeds expose different parts of the instance file to the embedding model.
Averaging the scores over several seeds (soft voting) is optional; in the paper, two-seed
voting lowered the mean PAR10 on SAT12-ALL from 1077 to 1030 (k=5).
(Assumes embed and selector are set up as in the Quick Start above.)
scores = []
for seed in [42, 100, 500]:
text = zf.serialize("instance.cnf", seed=seed)
emb = np.array([embed(text)])
scores.append(selector.predict_scores(emb)) # shape (1, n_algorithms)
avg_scores = np.mean(scores, axis=0) # shape (1, n_algorithms)
best_algo_idx = int(np.argmin(avg_scores[0]))
API Reference
zf.serialize(path, strategy, max_chars, seed)
Serialize an instance file to text. Works with any text-based format (CNF, WCNF, QDIMACS, MiniZinc, ASP, MPS, etc.). Gzipped files (.gz) are handled automatically.
strategy:"line_shuffle"(default, recommended) or"raw". Usezf.list_strategies()to see all options.max_chars: Character budget for truncation (default 10,000, as in the paper). The embedding model may truncate further at its own input limit (8,192 tokens for Gemini Embedding 2, 2,048 forgemini-embedding-001).seed: Random seed for line shuffling (default 42). Different seeds yield different views of the same instance. Ignored by the"raw"strategy.
zf.KNNSelector(k, metric, algorithm_names)
Weighted k-NN algorithm selector using inverse-distance weighting.
k: Number of neighbors (default 10). Must not exceed the number of training instances.metric:"manhattan"(default, as in the paper) or"cosine"(similar results in the paper).algorithm_names: Optional list of algorithm name strings. When provided,predict()returns a list of name strings; otherwise it returns a numpy array of column indices.
Methods:
.fit(embeddings, runtimes)— fit on training embeddings and runtime matrix. RaisesValueErrorifkexceeds the number of training instances..predict(embeddings)— return best algorithm per instance (names or indices).predict_scores(embeddings)— return per-algorithm weighted-average scores, shape(n_test, n_algorithms)
zf.list_strategies()
Return the list of available serialization strategy names.
Persistence
ZeroFolio selectors can be saved and loaded with pickle:
import pickle
with open("selector.pkl", "wb") as f:
pickle.dump(selector, f)
with open("selector.pkl", "rb") as f:
selector = pickle.load(f)
Citation
@inproceedings{szeider2026zerofolio,
title={Algorithm Selection with Zero Domain Knowledge via Text Embeddings},
author={Szeider, Stefan},
booktitle={International Conference on Automated Machine Learning (AutoML 2026)},
year={2026},
note={arXiv:2604.19753}
}
License
Apache 2.0
Metadata
Release files for zerofolio 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| zerofolio-0.1.1.tar.gz | 27.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zerofolio-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.1 kB
Release files / zerofolio-0.1.1.tar.gz
| Download URL | zerofolio-0.1.1.tar.gz |
|---|---|
| Size | 27.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7f6f45dc84efd6fb27b6844788c262bc617a9854187841c4362010629a83f10e
|
|
BLAKE2b-256 checksum How to use checksums |
e349e9564dfd18fc1afd778203e1a2d92b42ccf0cfff0722cdf1a0175dc98e94
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.2
|
Release files / zerofolio-0.1.1-py3-none-any.whl
| Download URL | zerofolio-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5f9b203cb1377665791091c3d67730cddc0395f2babf3049ccd3a6080fded39
|
|
BLAKE2b-256 checksum How to use checksums |
6f178c87cc70619ef07ef6c4f0bc8b59d01addff145e813525e47202eacbdf8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.2
|
Security for zerofolio 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.