stringforge quickstart#

This notebook walks through the three main entry points of stringforge:

  1. Catalogue query – inspect the lightweight TDF catalogue (no shard I/O, just the index).

  2. Model loading – pull one model’s geometry shard in mirror convention and obtain a FluxVacuaFinder ready for use.

  3. Vault round-trip – write a tiny vacuum record to the local vault and read it back.

All three are lazy: only the catalogue is downloaded up-front (~10 MB) and only the shards a query touches are pulled afterwards.

Setup#

import warnings; warnings.filterwarnings("ignore")

import os
import jax
import jax.numpy as jnp
import pandas as pd
jax.config.update("jax_enable_x64", True)

# Point the cache at a sandbox directory for this notebook (the
# default is `${cwd}/.stringforge_cache`).  Everything downloaded
# here stays local until you decide otherwise.
import stringforge
stringforge.set_data_dir("./.sf_demo_cache")

1. Catalogue query (no shard I/O)#

The TDF catalogue indexes every Trilayer Double-Favourable toric model in the cy-database. A query(...) call is a pure-pandas filter on the in-memory catalogue – no per-shard downloads happen yet.

from stringforge import LCSDatabase

# mirror-convention wrapper: h12 is *jaxvacua* h12 (= catalogue h11)
db = LCSDatabase(dataset="tdf")
df = db.query(h12=2, has_conifolds=True)
print(f"matching rows: {len(df)}")
print()
df[["ks_id", "triang_id", "h11", "h12", "n_conifolds"]].head()

2. Lazy model loading#

Calling db.load_model(...) triggers download (and cache) of just the shards that one model needs, and returns a JAXVacua FluxVacuaFinder that you can use immediately.

row = df.iloc[0]

# This is the one call that touches the network the first time round.
# Subsequent calls for the same model are served from the local cache.
model = db.load_model(
    ks_id              = int(row["ks_id"]),
    triang_id          = int(row["triang_id"]),
    h11                = int(row["h11"]),
    h12                = int(row["h12"]),
    include_gv         = False,    # speed
    include_conifolds  = False,
    maximum_degree     = 2,
)
print(type(model).__name__, "ready;  h12 =", model.h12)

3. Vault round-trip#

The vault persists vacuum solutions in a parquet layout that other ecosystem tools can read. Here we manufacture a one-row toy vacuum record, designate it under a label, query it back, and (for a clean demo) retract it.

import numpy as np

# A toy vacuum record -- in real use this comes out of a vacuum search.
toy_vacua = pd.DataFrame({
    "flux"     : [[0, 0, -2, 3, 0, 1, 0, 0, 0, 0, 0, 0]],
    "moduli_re": [[0.0, 0.0]],
    "moduli_im": [[2.5, 3.0]],
    "tau_re"   : [0.0],
    "tau_im"   : [4.0],
    "is_susy"  : [True],
})

# Designate -- write to the local vault under a label
db.designate_vacua(
    toy_vacua,
    label        = "stringjax_demo",
    committed_by = "quickstart_notebook",
    h11          = int(row["h11"]),
    h12          = int(row["h12"]),
    ks_id        = int(row["ks_id"]),
    triang_id    = int(row["triang_id"]),
)

# Query the vault catalogue
catalog = db.query_designated(label="stringjax_demo")
print(catalog[["label", "n_vacua", "created"]])

# Clean up the demo entry from the local vault
db.retract_designated(label="stringjax_demo")

What next#

  • See the stringforge documentation for the full database stack (TDFDatabase, CICYDatabase, KKLTDatabase), the cluster-side Vulcan write tier, and the vault validation tools.

  • Try the jaxvacua quickstart to see how a loaded model is used to find vacua.

  • Combine the two: use a stringforge model in the jaxvacua quickstart by replacing FluxVacuaFinder(h12=2, model_ID=1) with db.load_model(...) above.