vike
ReferenceBinaries

vike-study

The study runner that can fit — the one host that hands a compiled study a real LightGBM learner and leaves a run behind.

vike-study runs one compiled user study over the hist store and leaves a run behind. It is the study runner that can fit: until it existed, nothing in the workspace could fit a compiled study at all.

vike-study --study NAME --recipe FILE --from WHEN --to WHEN --store DIR
           [--lightgbm PATH] [--scratch DIR] [--runs-root DIR]

  --study NAME      the study's folder name under user_data/research/studies/rust/
  --recipe FILE     its .toml recipe; an optional [learner] table carries the GBDT base params
  --from/--to WHEN  YYYY-MM-DD, YYYY-MM-DDTHH, or bare unix SECONDS
  --store DIR       the hist store root the study reads
  --lightgbm PATH   the PINNED trainer. Omitted, the study runs with NO learner and a fitting
                    study refuses BY NAME rather than silently producing nothing
  --scratch DIR     per-fit scratch root (default: <runs-root>/../scratch/vike-study — the
                    project folder, never the system temp directory)
  --runs-root DIR   where the run is minted (default: user_data/runs)

The bin (crates/vike-studio-core/src/bin/vike-study.rs) is a thin shim over vike_studio_core::study_cli::run, which reads no environment and no std::env::args() — argv is a parameter, which is what lets the vike multicall dispatcher own the process's single sweep and link one copy of the DataFusion closure for every tool.

The gap it closes

The one caller of run_study_plan was the Studio, whose ML surface is the inference half only (crates/vike-studio-core/src/ml.rs). It supplies no learner, so a study needing a gradient-boosted classifier per fold answered StudyError::NoLearner on every invocation — "the documented ceiling of a host with no LightGBM binary". That left the whole compiled tier unrunnable in practice rather than in principle. This binary is the host that supplies a learner.

--lightgbm names the pinned trainer. Omit it and the study runs with no learner, and a fitting study refuses by name rather than silently producing nothing — the distinction between "you did not give me a trainer" and "there was nothing to find" is the whole point of the refusal.

Why a feature-gated bin in this crate

The host must sit strictly above run_study_plan (layer 55) and be able to construct a DataFusionHist. vike-cli (layer 65) clears the layer bound, but that crate is deliberately light: it reaches history through vike-datahub-client, over the wire, and names no DataFusion at all — the light-consumers CI lane compiles it as the default-features-off consumer precisely to keep that true. Buying a verb with that property would have charged every vike-cli user for it.

A bin beside the function it calls costs nothing instead. study-cli is off by default, so the studio-standalone lane's structural check — the default normal-dep tree must carry no DataFusion crate — is untouched, and required-features = ["study-cli"] (crates/vike-studio-core/Cargo.toml) keeps this bin out of every build that has not asked for it.

It is not in a default build

vike-study is a required-features bin. A plain cargo build of the workspace does not produce it, and the release ships only the configurations its workflow names. Build it with the study-cli feature enabled, or reach it through the vike dispatcher built with that feature.

The [learner] table

A study's LightGBM base parameters are a property of the study — the non-searched keys its published results were measured at — while vike_ml::GbdtLearner knows nothing about any study and the host is what constructs it. In the cohort study those keys had no home a host could reach: they lived in a #[cfg(test)] module whose own doc called itself "their only home", so any real run would have used LightGBM's defaults and quietly fitted a different model than the one every recorded number came from.

So the recipe carries them, under [learner], and this binary reads that table. The recipe is already the study's own file and already travels with it; the study itself ignores the table (its reader is lenient, as the strategy tier's is); and nothing study-specific is compiled into this generic verb. An absent key keeps LightGBM's default, so a partial table is meaningful rather than an error — a study that fixes two of five says exactly that (study_cli::learner_params). An absent table means GbdtParams::default(), which is the right answer for a caller with no opinion and the wrong one to assume in silence, so the run prints which of the two it used.

Time boundaries

--from and --to accept YYYY-MM-DD, YYYY-MM-DDTHH, or bare unix seconds, converted to the epoch milliseconds TsRange speaks (study_cli::boundary_ms). Seconds are accepted because every anchor in the cohort study's own record is written that way, and re-deriving one by hand is how a window silently moves.

--scratch defaults to a directory under the runs root's parent — the project folder, never the system temp directory — so a per-fit scratch tree lands beside everything else the program writes.

Related: research, running a study, Rust studies, training, the fit cache, runs on disk.

On this page