Skip to main content

kaft

Geometric dynamics for knowledge fields on curved manifolds.

Standard embedding models project knowledge into flat Euclidean space which has every document equidistant, every domain indistinguishable. kaft replaces flat distance with Fisher-Rao information geometry, letting domain structure emerge from the manifold itself.

pip install kaft

What it does

kaft computes a K-density field over an embedding corpus using a pluggable Riemannian metric, then evolves that field through time using wave dynamics and Jordan boundary detection. Two things become measurable that flat geometry cannot see:

  1. Domain structure — K-density varies meaningfully across documents; Softmax (flat) returns uniform density
  2. Soliton locking — the field self-organises into a stable attractor; geometry and noise anneal together until the field holds

Quickstart

from kaft.ingest.router import ArxivRouter
from kaft.ingest.embedder import Embedder
from kaft.geometry import get as get_metric
from kaft.dynamics.kstate import KDensity
from kaft.dynamics.softmax_dynamics import SoftmaxDynamics

# 1. Ingest
router     = ArxivRouter()
records    = router.fetch("Fisher-Rao information geometry", max_results=20)

# 2. Embed + manifold
embedder   = Embedder()
embeddings = embedder.encode([r["text"] for r in records])
metric     = get_metric("fisher_rao")
kd         = KDensity(embeddings, metric)
kd.compute()

# 3. Flat baseline
flat       = SoftmaxDynamics(temperature=1.0).run(embeddings)

# 4. Compare
div = SoftmaxDynamics().distribution_divergence(kd.density, flat.K)
print(f"KAFT K_var  : {kd.density.var():.6f}")
print(f"Softmax K_var: {flat.K_var:.6f}")
print(f"Divergence  : {div:.4f}")

Output on a real 80-paper, 4-domain arXiv corpus:

KAFT (Fisher-Rao) Softmax (Euclidean)
K variance 0.037222 0.000000
Divergence 0.1802 —

Softmax returns K=1.000 for every document. Fisher-Rao geometry ranges 0.000–1.000, resolving domain boundaries invisible to flat distance.


Simulation dynamics

from kaft.dynamics.kstate import KDensity, KState
from kaft.dynamics.jordan import JordanBoundary, JordanNoise
from kaft.dynamics.metric_evolution import MetricEvolution
from kaft.simulate.crucible import SimulationCrucible
from kaft.simulate.runner import SimulationRunner
import numpy as np

noise          = JordanNoise(sigma=0.05, rng=np.random.default_rng(42))
metric_evolver = MetricEvolution(temperature=1.0, base_step=0.1)

ks = KState(); ks.update(kd.density)
jb = JordanBoundary(kd); jb.detect()

runner = SimulationRunner(
    embeddings=embeddings, records=records,
    k_density=kd, k_state=ks, jordan=jb,
    crucible=SimulationCrucible("demo", "fisher_rao", "output"),
    metric_evolver=metric_evolver,
    softmax=SoftmaxDynamics(temperature=1.0),
    noise=noise,
)

SIGMA_MIN = 0.005

for _ in range(50):
    frame = runner.step()
    if frame.phase == "locked":
        metric_evolver.base_step = 0.0       # freeze geometry
        noise.sigma              = SIGMA_MIN  # hold the field
    else:
        noise.sigma              = max(SIGMA_MIN, 0.05 * (1.0 - frame.K_mean))
        metric_evolver.base_step = max(0.001, 0.1  * (1.0 - frame.K_mean))

    print(f"step={frame.step:>3}  K_mean={frame.K_mean:.4f}"
          f"  phase={frame.phase:<12}  locked={frame.soliton_locked}")

Output on 80-paper corpus, 4 domains:

step=  0  K_mean=0.3887  phase=clustering   locked=False
step= 10  K_mean=0.5458  phase=clustering   locked=False
step= 20  K_mean=0.6004  phase=locked       locked=False
step= 24  K_mean=0.6153  phase=locked       locked=True
step= 30  K_mean=0.6153  phase=locked       locked=True
...
step= 90  K_mean=0.6153  phase=locked       locked=True

Soliton lock at step 24. Field holds identical K_mean=0.6153 from step 30 through 90 — the attractor is stable.

The adaptive schedule reflects the physics: soliton formation requires a frozen medium. Once the phase gates to locked, geometry stops evolving and the field settles.


Metric zoo

from kaft.geometry import get, list_metrics

list_metrics()
# ['fisher_rao', 'kl_divergence', 'jensen_shannon',
#  'alpha_divergence', 'euclidean', 'minkowski', 'alpha_hellinger',
#  'alpha_bhattacharyya', 'bregman_kl', 'bregman_euclidean', 
#  'bregman_itakura_saito', 'bregman_tsallis_2'
#  'gaussian_curved', 'wasserstein']

metric = get("jensen_shannon")   # drop-in replacement

All metrics implement AbstractMetric — swap one line to run the same experiment on any geometry.


Library structure

kaft/
├── geometry/     Riemannian metrics — Fisher-Rao, KL, Jensen-Shannon,
│                 Alpha, Bregman, Wasserstein, Euclidean, Minkowski
├── dynamics/     K-density field, KState, JordanBoundary, WaveEngine,
│                 MetricEvolution, GradientFlow, SoftmaxDynamics
├── simulate/     SimulationRunner, SimulationCrucible, Frame log,
│                 SimulationVisualiser
├── navigate/     GeodesicNavigator, GeodesicPath, DomainSeeder
└── ingest/       ArxivRouter, Embedder

kaft is domain-agnostic. The same primitives that resolve arXiv domain boundaries work on protein sequence corpora, patent claim graphs, or financial time series. Any corpus that embeds into a vector space.


Experiments

Full experiment scripts and the N-scaling study (soliton lock step as a function of corpus size) are in the GitHub repository.


License

MIT

Metadata

Release files for kaft 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kaft 0.3.0
File Size Uploaded
kaft-0.3.0.tar.gz 49.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kaft 0.3.0
File Interpreter ABI Platform
kaft-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 105.6 kB

Release files / kaft-0.3.0.tar.gz

Download URL kaft-0.3.0.tar.gz
Size 49.5 kB
Tags Source
SHA-256 checksum
How to use checksums
e2ae95ac456c545ab0faaa3c22205ea63754a01fa4b35d47f00b6659203f1477
BLAKE2b-256 checksum
How to use checksums
70a96a76abd241ff2b870ca1b7946d5abd61ca5e7b48e23a490f1d27ba7b6cd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / kaft-0.3.0-py3-none-any.whl

Download URL kaft-0.3.0-py3-none-any.whl
Size 56.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf1ff4973bd2cb1419698460a68418db5964bd8b76e594e16db8c943ac77a032
BLAKE2b-256 checksum
How to use checksums
4cedbf53b029e19e4d9dd7d3dad5fb7fd747c762b5162fc4fc228e4b6765ad8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page