HypercubeWTF Python SDK

September 11, 2026 · View on GitHub

Static fields have no natural clock. HypercubeWTF invents a short stretch of synthetic time: a frozen Boolean-hypercube reservoir drives each length-N field for a short episode, then a small HypercubeCNN readout trains only on the end state. One class — WTF — owns collect → train → predict.

This is the episode API, not a stream API. There is no per-tick input sequence and no next-step fit on a 1D signal (that is HypercubeESN). Time here is synthetic and per sample.

C++ core and contracts: CPP_SDK.md.
PyPI-facing package story: python/README.md.
Package version: single source python/hypercube_wtf/_version.py (hypercube_wtf.__version__ and wheel metadata both read it).

Contents

Installation

From PyPI (preferred)

Pre-built wheels — no compiler required:

pip install hypercube-wtf

Import as import hypercube_wtf as hw (PyPI name hypercube-wtf). Wheels cover Python 3.10–3.14 on common Windows (x64), Linux (x86_64, aarch64), and macOS (x86_64, arm64) builds. NumPy is the only runtime dependency.

From source (full repository)

Compile only from a full clone of HypercubeWTF. The extension links the C++ core and vendored HypercubeCNN that sit outside the python/ package directory; a python/-only tree is not enough.

Requirements: Python 3.10+, C++23 compiler (GCC 13+, Clang 17+, MSVC 2022+), CMake 3.20+, scikit-build-core, pybind11, NumPy.

git clone https://github.com/dliptak001/HypercubeWTF.git
cd HypercubeWTF/python
pip install .

On Windows with MinGW (e.g. CLion toolchain):

pip install scikit-build-core pybind11 numpy
$env:PATH = "C:\path\to\mingw\bin;" + $env:PATH
$env:CMAKE_GENERATOR = "Ninja"
$env:CMAKE_MAKE_PROGRAM = "C:\path\to\ninja.exe"
$env:CC = "C:\path\to\mingw\bin\gcc.exe"
$env:CXX = "C:\path\to\mingw\bin\g++.exe"
pip install . --no-build-isolation

Running tests

From the python/ directory after install:

pip install ".[test]"
pytest tests/ -v --import-mode=importlib

Or from the repository root: pytest python/tests/ -v --import-mode=importlib. Importlib mode avoids the source tree shadowing the installed _core extension.

Examples

The Quick start below is enough after pip install. Longer demos live in the git tree under python/examples/ — they are not part of the wheel. From a clone, repository root:

pip install hypercube-wtf   # or: pip install ./python
python python/examples/synthetic_classification.py

Quick start

import numpy as np
import hypercube_wtf as hw

dim = 7
N = 1 << dim
rng = np.random.default_rng(0)
fields = rng.standard_normal((128, N), dtype=np.float32)
labels = rng.integers(0, 4, size=128, dtype=np.int32)

wtf = hw.WTF(
    dim=dim,
    seed=1,
    ic_seed=2,
    readout_num_outputs=4,
    readout_task="classification",
    readout_epochs=80,
)
wtf.fit(fields, labels)

print(wtf.accuracy_on_collected())  # train-set only — not a test score
print(wtf.predict_class(fields[0]))

Explicit (full control)

wtf = hw.WTF(
    dim=6,
    history_depth=8,
    T=64,
    readout_num_outputs=3,
    readout_task="classification",
)
wtf.collect_episodes(fields_train, labels_train)
wtf.train()
logits = wtf.predict(fields_test[0])   # shape (num_outputs,)
cls = wtf.predict_class(fields_test[0])

fit is clear_collected (optional) → collect_episodestrain. Prefer fit for a first pass; use collect/train when you append batches or retrain without re-driving every field.

What an episode is

x  (length-N field, host-packed)


 reload frozen IC  →  drive field for T passes  →  pack B end ages


 features (B×N)  →  HypercubeCNN  →  logits / values
  • N = 2^dim vertices / field length (dim 5…16).
  • Reservoir weights are frozen after construction; only the readout trains.
  • Predict always runs a fresh episode (no collect-time train noise).
  • Host packing (MNIST → N, spectra → N, …) is your problem — this package does not reshape domain data onto the cube.

The CNN head never sees the original field; it sees what the orbit leaves behind.

Pipeline vocabulary

TermMeaning
FieldLength-N float32 vector on the cube (you pack domain data)
EpisodeReload frozen IC → drive field for T passes → pack end features
CollectRun episode (optional train noise) → append features + label/target
TrainBatch-train HCNN on all collected episodes
PredictFresh episode (no train noise) + readout forward
NNeurons / field length = 2^dim
Mhistory_depth — delay-line depth
Breadout_slices — ages packed into features (power of two, 1 ≤ B ≤ M)
TDrive-pass count per episode (T=0 expands to N at construction)

Not HypercubeESN’s stream pipeline: no reservoir_warmup, no next-step fit on a 1D signal.

API reference

Constructor WTF(dim, **kwargs)

All knobs are fixed at construction (same contract as C++ WTFConfig).

import hypercube_wtf as hw

wtf = hw.WTF(
    dim=7,                         # required; 5–16
    seed=7934791766227647176,      # reservoir weight init
    spectral_radius=0.999,
    input_scaling=0.02,
    leak_rate=1.0,
    history_depth=16,              # M
    verbose=False,
    bias_scaling=0.003,
    ic_seed=1,                     # frozen episode IC (not weight seed)
    T=100,                         # drive passes; 0 → N
    readout_slices=1,              # B
    collect_threads=0,             # 0 = auto
    train_input_noise_sigma=0.0,   # collect only
    bypass_reservoir=False,        # field → features if True (needs B=1)
    readout_num_outputs=1,
    readout_task="regression",     # or "classification"
    # … readout_* kwargs below
)

Reservoir and episode

ParameterTypeDefaultDescription
dimintrequiredHypercube dimension [5, 16]. N = 2^dim.
seedint7934791766227647176Reservoir weight-init seed (matches C++).
spectral_radiusfloat0.999Target spectral radius for recurrent weights.
input_scalingfloat0.02Input drive coefficient.
leak_ratefloat1.0Leaky integrator; 1.0 = full replacement.
history_depthint16Delay-line depth M ∈ [1, 64].
verboseboolFalseReservoir construction banner.
bias_scalingfloat0.003Per-neuron bias after tanh; 0 disables.
ic_seedint1Frozen episode IC seed (separate from seed).
Tint100Drive-pass count; 0 expands to N after construction.
readout_slicesint1B ages packed into features (power of two, ≤ M).
collect_threadsint0Bulk collect workers: 0 = auto, 1 = serial, K = K workers.
train_input_noise_sigmafloat0.0Gaussian σ on the field during collect only (not predict).
bypass_reservoirboolFalseSkip orbit; features are the packed field (requires B = 1).

Readout (HCNN)

ParameterTypeDefaultDescription
readout_num_outputsint1Classes (classification) or regression width.
readout_taskstr"regression""regression" or "classification".
readout_num_layersint1Conv(+Pool) stages. 0 = auto min(dim−2, 2).
readout_conv_channelsint16Base channel count for the first conv.
readout_epochsint200Batch-train epochs.
readout_batch_sizeint32Mini-batch size.
readout_lr_maxfloat0.0015Cosine peak LR. Keep ≤ ~0.005 to avoid NaN.
readout_lr_min_fracfloat0.01Floor = lr_max * lr_min_frac.
readout_lr_decay_epochsint0Cosine horizon; 0 = use readout_epochs.
readout_weight_decayfloat0.0L2 on CNN weights.
readout_momentumfloat0.9SGD momentum; ignored under the default Adam optimizer.
readout_activationstr"tanh""tanh", "relu", "leaky_relu", or "none".
readout_seedint42CNN weight-init seed.
readout_num_threadsint0HCNN workers: 0 = auto, 1 = single-threaded.
readout_restore_best_epochboolTrueRestore best-epoch weights after batch train.
readout_best_epoch_holdout_fracfloat0.0Tail hold-out for best-epoch scoring; 0 = full train set.
readout_use_poolingboolTrueAntipodal pool after each conv.

Not bound in Python yet (C++ ReadoutConfig only): optimizer choice (C++ default Adam), pool type, channel growth, batch-norm. C++ defaults apply.

Methods

MethodRole
run_episode(x)Drive one episode (or bypass copy). Updates last_features().
`last_features()$\text{Length} \text{B} \times \text{N} \text{float32} \text{from} \text{the} \text{last} \text{path} \text{that} \text{writes} \text{the} \text{primary} \text{buffer}: $run_episode, serial collect_episode, predict, or predict_class. **Not** updated by bulk collect_episodes` (those features go only into the training set).
clear_collected()Drop the batch training buffer.
collect_episode(x, target)Serial append one sample (label or regression vector).
collect_episodes(fields, targets)Bulk parallel append.
fit(fields, targets, *, clear=True)Optional clear → collect → train. Returns self.
train()Batch-train HCNN on all collected episodes. Does not clear the set.
predict(x)Fresh episode + forward → shape (num_outputs,) float32.
predict_class(x)Fresh episode + argmax class (classification task only).
accuracy_on_collected()Accuracy on the collected training set only.
r2_on_collected()R² on the collected training set only.
save(path) / load(path)Pickle constructor config + readout weights.
save_readout_hcnn_model(path_stem)Portable stem.hcnw + stem.arch.json.
load_readout_hcnn_model(path_stem, *, mode="eval")Load HCNW into this instance ("eval" or "resume_train").
readout_arch_summary()Human-readable HCNN architecture and parameter counts.

Properties

PropertyMeaning
dim, N, T, B, MGeometry and episode knobs (N = 2^dim, B = readout slices, M = history depth)
feature_sizeB × N floats per sample / last_features
num_collectedEpisodes in the batch training buffer
num_outputsReadout width
seed, ic_seedWeight seed vs frozen IC seed
spectral_radius, realized_spectral_radiusTarget vs post-rescale estimate
input_scaling, leak_rate, history_depth, bias_scalingReservoir config mirrors
bypass_reservoir, collect_threads, train_input_noise_sigmaEpisode / collect mirrors
readout_task"regression" or "classification"
readout_best_epoch1-based best epoch after restore; else 0
verboseConstruction banner flag

Input data layout

  • Fields must be length N per sample. Prefer shape (count, N) for bulk APIs; a flat length count * N vector is also accepted.
  • Host packing (images, spectra, sensors → N) is outside this package.
  • Classification labels: integer class indices in [0, num_outputs) (enforced at collect). Shape (count,) for bulk collect / fit.
  • Regression targets: shape (count, num_outputs) float32 (or flat count * num_outputs).
  • Single-sample methods accept any array that ravel-flattens to the right length.

Data types

RolePreferred typeNotes
Fields / features / predictionsfloat32Other dtypes converted via NumPy to contiguous float32
Class labelsint32 (or Python int)Must be in [0, num_outputs) at collect (C++ enforces)
Bool as a class labelrejected on serial collectcollect_episode raises TypeError; use an integer index. Bulk collect_episodes coerces via int32 (do not rely on bool labels).

Error handling

Python-side checks raise ValueError or TypeError with a short message (bad dim, task string, activation, field shape, label count, …). Native std::invalid_argument maps to ValueError; other C++ failures typically surface as RuntimeError via pybind11.

Typical mistakes:

  • Field length ≠ N
  • Bulk fields / targets row counts disagree
  • Class label outside [0, num_outputs)
  • predict_class / accuracy_on_collected on a regression model
  • Calling train or accuracy_on_collected with an empty collected set
  • bypass_reservoir=True with B ≠ 1 (rejected at construction in C++)

Model persistence

MechanismWhat is storedCollected episodes?
save / pickleConstructor config + readout weight blobNo (num_collected is 0 after load)
save_readout_hcnn_modelPortable HCNW + arch sidecarNo

Pickle version is bumped when the serialized layout changes; newer libraries reject unknown future versions with an upgrade message.

wtf.save("model.pkl")
wtf2 = hw.WTF.load("model.pkl")   # same ctor knobs + weights; empty collect buffer

wtf.save_readout_hcnn_model("export/stem")   # stem.hcnw + stem.arch.json
# Target instance must build a matching HCNN input shape / task (same dim, B,
# M, and readout_* architecture knobs as the exporter — not only dim/outputs).
wtf3 = hw.WTF(
    dim=wtf.dim,
    history_depth=wtf.history_depth,
    readout_slices=wtf.B,
    readout_num_outputs=wtf.num_outputs,
    readout_task=wtf.readout_task,
    # plus any non-default readout_num_layers / channels / pooling / …
)
wtf3.load_readout_hcnn_model("export/stem", mode="eval")

Prefer save / load when you want a full Python round-trip of the product config. Prefer HCNW when you need a portable HypercubeCNN weight export.

Security: load uses pickle.load. Never load untrusted files.

Limitations

  • One WTF instance is not thread-safe for concurrent public calls from multiple host threads. Bulk collect parallelism is internal only.
  • accuracy_on_collected / r2_on_collected only score samples you already collected (and typically trained on). Hold fields out and call predict / predict_class for real evaluation.
  • last_features() is not updated by bulk collect_episodes (see Methods).
  • A few readout knobs remain C++-only (optimizer, pool type, channel growth, batch-norm); see constructor tables above.
  • Native contracts, episode mechanics, and host integration detail: CPP_SDK.md.

Dependencies

LayerWhat
RuntimeNumPy
Wheel installNo compiler
From-source buildFull repo clone, C++23, CMake ≥ 3.20, scikit-build-core, pybind11

The HypercubeCNN readout is built into the extension — no separate HCNN package.