Documentation
¶
Overview ¶
Package synth produces synthetic .pulse cohorts from either a schema declaration ("from-schema") or a statistical profile of a real cohort ("from-profile"). The generator is deterministic given a seed and writes directly into the .pulse binary format using the encoding package, so outputs are byte-identical for the same (spec, seed) pair.
Two top-level entry points are exported by this package:
Synth(spec Spec, opts Options) (*Result, error) Profile(schema, records, opts ProfileOptions) (*Profile, error)
Pulse embedders should use the higher-level pulse.Pulse.Synth and pulse.Pulse.Profile facade methods instead.
Index ¶
- Constants
- func AllDistributions() []string
- func BuildCategoricalNumericPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func BuildCategoricalPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func BuildPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func BuildSetCategoricalPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func BuildSetNumericPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func BuildSetSetPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, ...)
- func SyntheticAsCategoricalSchema(schema *encoding.Schema) *encoding.Schema
- type CategoricalNumericCategoryFidelity
- type CategoricalNumericCategorySpec
- type CategoricalNumericCategoryStat
- type CategoricalNumericPairFidelity
- type CategoricalNumericPairProfile
- type CategoricalNumericPairSpec
- type CategoricalPairCellSpec
- type CategoricalPairFidelity
- type CategoricalPairProfile
- type CategoricalPairSpec
- type CategoricalProfile
- type CategoryHit
- type ConditionalProfile
- type ConstraintSpec
- type ContingencyCell
- type CorrelationSpec
- type CorrelationStat
- type DateProfile
- type FidelityReport
- type FieldFidelity
- type FieldProfile
- type FieldSpec
- type NumericPairProfile
- type NumericProfile
- type Options
- type PairwiseFidelity
- type Profile
- type ProfileOptions
- type Result
- type SetCategoricalPairFidelity
- type SetCategoricalPairProfile
- type SetCategoricalPairSpec
- type SetFieldFidelity
- type SetNumericPairFidelity
- type SetNumericPairProfile
- type SetNumericPairSpec
- type SetOptionFidelity
- type SetOptionStat
- type SetProfile
- type SetSetPairFidelity
- type SetSetPairProfile
- type SetSetPairSpec
- type ShapeProfile
- type Spec
- type TestRunner
Constants ¶
const ( DistUniform = "uniform" DistNormal = "normal" DistLogNormal = "lognormal" DistExponential = "exponential" DistPoisson = "poisson" DistPareto = "pareto" DistBernoulli = "bernoulli" DistMonotonicFrom = "monotonic_from" DistWeightedCategorical = "weighted_categorical" DistUniformDate = "uniform_date" DistRegex = "regex" DistConstant = "constant" DistMixture = "mixture" DistSetBernoulli = "set_bernoulli" )
Distribution kind constants.
const ContingencyCellCap = 128
ContingencyCellCap bounds the number of distinct (a_value, b_value) cells CategoricalPairProfile.Cells retains per categorical-categorical pair. A pair of two 50-plus category fields can still produce thousands of joint combinations even after each field's own ProfileOptions.TopK collapse (e.g. two fields each capped at the default TopK=32 allow up to 33*33=1089 joint cells) — this second, joint-cardinality cap keeps the profile document bounded regardless of either field's own cardinality. 128 was chosen as generous enough to preserve real structure for the common case while still bounding a pathological high-cardinality pair; every cell beyond the cap collapses into one ("other","other") catch-all cell rather than being dropped silently.
const MinPairObservations = 30
MinPairObservations is the minimum number of co-occurring, non-null observations a captured pair needs before its reconstruction parameters are trusted. This threshold and the warning mechanism below are deliberately generic across pair *kind* — numeric-numeric is the only kind this story populates, but categorical and set_* pairs (later epics) reuse the same threshold and the same thinPairWarning helper rather than inventing their own. Below the threshold the profile still ships the pair — a thin pair is never a refusal — but records a warning on Profile.Warnings so a caller downstream of profile capture knows the figure may be unstable.
const MinShapeFitObservations = 50
MinShapeFitObservations is the minimum number of retained samples a numeric field needs before ProfileOptions.FitShape attempts a mixture fit at all. Below this threshold the field falls back to the plain normal capture unconditionally — mirroring MinPairObservations' philosophy elsewhere in this package (a thin sample can't support a richer model), though here it gates whether a fit is even ATTEMPTED rather than emitting a warning about an already-shipped result.
const SyntheticFieldName = "_synthetic"
SyntheticFieldName is the provenance column every AugmentFromProfile output cohort carries: false for rows copied from the source cohort, true for newly generated rows.
Variables ¶
This section is empty.
Functions ¶
func AllDistributions ¶
func AllDistributions() []string
AllDistributions returns the registered kind names in sorted order. Used by the manifest and tests.
func BuildCategoricalNumericPairwise ¶ added in v0.32.0
func BuildCategoricalNumericPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []CategoricalNumericPairSpec)
BuildCategoricalNumericPairwise computes each categorical-numeric pair's per-category conditional mean/std delta and appends the resulting CategoricalNumericPairFidelity entries onto report.CategoricalNumericPairwise. mergedSchema/records must be the same physical schema and record buffer BuildFidelityReport was given.
pairs is typically Spec.CategoricalNumericPairs — the source's captured per-category conditional mean/std synth/conditional_sample.go's categoricalNumericPairSampler draws from. A category with no realized synthetic observation gets an Error entry rather than aborting the rest of that pair's categories or the report — the same non-fatal contract every other Fidelity entry kind follows. As with BuildCategoricalPairwise, this function takes no warnings parameter: thin-cell warnings for this pair kind already ride Profile.Warnings through BuildPairwise's copy.
func BuildCategoricalPairwise ¶ added in v0.32.0
func BuildCategoricalPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []CategoricalPairSpec)
BuildCategoricalPairwise computes each categorical-categorical pair's contingency-table delta (categoricalTVD) and appends the resulting CategoricalPairFidelity entries onto report.CategoricalPairwise. mergedSchema/records must be the same physical schema and record buffer BuildFidelityReport was given for report's Fields section.
pairs is typically Spec.CategoricalPairs — the source's captured contingency cells synth/conditional_sample.go's categoricalPairSampler was built from, not a fresh re-derivation of it.
A pair whose synthetic partition produced no co-occurring observation at all gets an Error entry rather than aborting the rest of the pairs or the report — the same non-fatal contract BuildPairwise's own per-pair entries follow. This function takes no warnings parameter of its own: thin-cell warnings for this pair kind already ride Profile.Warnings alongside the numeric-pair warnings BuildPairwise copies onto report.Warnings, so giving this function a second copy point would either duplicate them or require the caller to split one warnings slice by kind for no reason — one shared shape, one place it lands.
func BuildPairwise ¶ added in v0.32.0
func BuildPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []CorrelationSpec, warnings []string)
func BuildSetCategoricalPairwise ¶ added in v0.32.0
func BuildSetCategoricalPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []SetCategoricalPairSpec)
BuildSetCategoricalPairwise computes each set-option x categorical pair's contingency-table delta (categoricalTVD, reused verbatim from BuildCategoricalPairwise — SetCategoricalPairSpec.Cells is the same []CategoricalPairCellSpec shape) and appends the resulting SetCategoricalPairFidelity entries onto report.SetCategoricalPairwise (E5-S4). mergedSchema/records must be the same physical schema and record buffer BuildFidelityReport was given.
pairs is typically Spec.SetCategoricalPairs — the source's captured contingency cells synth/conditional_sample.go's setCategoricalPairSampler was built from. A pair whose synthetic partition produced no co-occurring observation at all gets an Error entry rather than aborting the rest of the pairs or the report — the same non-fatal contract BuildCategoricalPairwise's own per-pair entries follow.
func BuildSetNumericPairwise ¶ added in v0.32.0
func BuildSetNumericPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []SetNumericPairSpec)
BuildSetNumericPairwise computes each set-option x numeric pair's per-bucket conditional mean/std delta and appends the resulting SetNumericPairFidelity entries onto report.SetNumericPairwise (E5-S4). mergedSchema/records must be the same physical schema and record buffer BuildFidelityReport was given.
pairs is typically Spec.SetNumericPairs. A bucket with no realized synthetic observation gets an Error entry rather than aborting the rest of that pair's categories or the report — the same non-fatal contract BuildCategoricalNumericPairwise's own entries follow.
func BuildSetSetPairwise ¶ added in v0.32.0
func BuildSetSetPairwise(report *FidelityReport, mergedSchema *encoding.Schema, records []byte, pairs []SetSetPairSpec)
BuildSetSetPairwise computes each option x option pair's 2x2 contingency-table delta between two DIFFERENT set_* fields (categoricalTVD, reused verbatim — SetSetPairSpec.Cells is the same []CategoricalPairCellSpec shape) and appends the resulting SetSetPairFidelity entries onto report.SetSetPairwise (E5-S4). mergedSchema/records must be the same physical schema and record buffer BuildFidelityReport was given.
pairs is typically Spec.SetSetPairs. A pair whose synthetic partition produced no co-occurring observation at all gets an Error entry rather than aborting the rest of the pairs or the report.
func SyntheticAsCategoricalSchema ¶ added in v0.32.0
SyntheticAsCategoricalSchema returns a copy of schema whose SyntheticFieldName field is redeclared as categorical_u8 with a two-entry dictionary ({"false","true"} at IDs 0/1 — matching packed_bool's 0/1 on-wire values verbatim, no value remapping). TEST_KS.SplitBy and TEST_CHISQ's Rows/Cols both require a categorical field, but _synthetic itself is packed_bool (see CLAUDE.md Byte-layout invariants) — this presentation-only schema is what lets the two unmodified operators treat it as their split/contingency field.
It is never used to decode bytes — see TestRunner's physicalSchema contract — only to give a TestRunner's constructed Record something whose Field(SyntheticFieldName).Type reads back as categorical.
Types ¶
type CategoricalNumericCategoryFidelity ¶ added in v0.32.0
type CategoricalNumericCategoryFidelity struct {
Category string `json:"category"`
// SourceMean/SourceStd echo the captured
// CategoricalNumericCategorySpec verbatim.
SourceMean float64 `json:"source_mean"`
SourceStd float64 `json:"source_std"`
// SyntheticMean/SyntheticStd are the realized conditional mean/std
// over the _synthetic=true partition. MeanDelta/StdDelta are
// abs(source-synthetic). All four are zero-valued and meaningless
// when Error is set.
SyntheticMean float64 `json:"synthetic_mean,omitempty"`
SyntheticStd float64 `json:"synthetic_std,omitempty"`
MeanDelta float64 `json:"mean_delta,omitempty"`
StdDelta float64 `json:"std_delta,omitempty"`
// N is the number of _synthetic=true rows carrying this category
// value with a non-null B — the realized category's own observation
// count.
N int `json:"n"`
// Error is set instead of the Synthetic*/*Delta fields when the
// synthetic partition realized no observation of this category at
// all — mirrors PairwiseFidelity's own per-entry non-fatal contract.
Error string `json:"error,omitempty"`
}
CategoricalNumericCategoryFidelity is one observed category's conditional mean/std delta — the categorical-numeric analogue of PairwiseFidelity, one entry per category rather than one entry per pair, since a categorical-numeric pair reconstructs a whole distribution per category rather than a single scalar.
type CategoricalNumericCategorySpec ¶ added in v0.32.0
type CategoricalNumericCategorySpec struct {
Category string `json:"category"`
Mean float64 `json:"mean"`
Std float64 `json:"std"`
}
CategoricalNumericCategorySpec is the numeric field's conditional mean/std for one observed category value (or the "other" catch-all) of the paired categorical field — the Spec-facing counterpart of CategoricalNumericCategoryStat.
type CategoricalNumericCategoryStat ¶ added in v0.32.0
type CategoricalNumericCategoryStat struct {
Category string `json:"category"`
Mean float64 `json:"mean"`
Std float64 `json:"std"`
// N is the number of non-null B observations conditioned on this
// category — the input thinPairWarning checks against
// MinPairObservations, exactly as NumericPairProfile.N and each
// ContingencyCell.Count already do for the other two pair kinds.
N int `json:"n"`
}
CategoricalNumericCategoryStat is the numeric field's conditional mean/std/observation-count for one observed category value (or the otherCategoryLabel catch-all) of the paired categorical field.
type CategoricalNumericPairFidelity ¶ added in v0.32.0
type CategoricalNumericPairFidelity struct {
A string `json:"a"`
B string `json:"b"`
Categories []*CategoricalNumericCategoryFidelity `json:"categories,omitempty"`
}
CategoricalNumericPairFidelity is one row of FidelityReport.CategoricalNumericPairwise: the per-category delta between a captured categorical-numeric pair's source conditional mean/std (CategoricalNumericPairSpec.Categories — the same figures synth/conditional_sample.go's categoricalNumericPairSampler draws from) and that category's REALIZED conditional mean/std over the _synthetic=true partition of the output cohort.
type CategoricalNumericPairProfile ¶ added in v0.32.0
type CategoricalNumericPairProfile struct {
A string `json:"a"` // categorical field name
B string `json:"b"` // numeric field name
Categories []CategoricalNumericCategoryStat `json:"categories"`
// N is the number of rows in the capture where both A and B were
// simultaneously non-null, summed across every category (including
// "other") — the pair's true co-occurrence count, mirroring
// CategoricalPairProfile.N and NumericPairProfile.N.
N int `json:"n"`
}
CategoricalNumericPairProfile is one captured categorical-numeric pair's reconstruction input: the numeric field B's conditional mean/std, broken out per observed category of the categorical field A. Categories already reflect A's own per-field top-K cap (ProfileOptions.TopK, via FieldProfile.Categorical.Top) — any category outside A's own top-K collapses into one otherCategoryLabel bucket before its conditional numeric summary is computed, the same per-field collapse CategoricalPairProfile applies on each of its own axes. Unlike CategoricalPairProfile there is no second, joint-cell cap: the number of retained Categories entries is already bounded by ProfileOptions.TopK (plus one "other" bucket), since this section captures one numeric summary per category rather than a joint table over two categorical axes.
type CategoricalNumericPairSpec ¶ added in v0.32.0
type CategoricalNumericPairSpec struct {
A string `json:"a"`
B string `json:"b"`
Categories []CategoricalNumericCategorySpec `json:"categories"`
// Min/Max clamp the conditional draw exactly as field B's own
// unconditional normal reconstruction clamps to its observed range
// (see SpecFromProfile). HasClamp distinguishes "clamp to [0,0]"
// (rare, but a legitimate observed range for a constant field) from
// "no clamp was declared" — a hand-authored from-schema spec that
// omits Min/Max gets no clamping, exactly as normal's own optional
// min/max params behave when absent.
Min float64 `json:"min,omitempty"`
Max float64 `json:"max,omitempty"`
HasClamp bool `json:"has_clamp,omitempty"`
}
CategoricalNumericPairSpec is one categorical-numeric pair's generation-time reconstruction input — the Spec-facing counterpart of CategoricalNumericPairProfile, carrying only what a conditional resample needs (each category's mean/std, plus the numeric field's own overall min/max so the conditional draw clamps exactly as the field's unconditional `normal` reconstruction already does; N is capture-time provenance and is not needed here).
type CategoricalPairCellSpec ¶ added in v0.32.0
type CategoricalPairCellSpec struct {
AValue string `json:"a_value"`
BValue string `json:"b_value"`
Count int `json:"count"`
}
CategoricalPairCellSpec is one observed (a_value, b_value) co-occurrence count — the Spec-facing counterpart of ContingencyCell.
type CategoricalPairFidelity ¶ added in v0.32.0
type CategoricalPairFidelity struct {
A string `json:"a"`
B string `json:"b"`
// Delta is the total variation distance between the source and
// synthetic joint-frequency tables (each normalized to proportions
// before comparison), in [0, 1] — 0 when the two tables carry
// identical support and proportions, 1 when they share none. Chosen
// over a chi-square-style distance because it stays well-defined and
// boundedly comparable to a fixed tolerance even when synthesis
// realizes a cell the capture never saw (or vice versa) — a
// chi-square statistic diverges when an expected cell is zero. Zero
// valued and meaningless when Error is set.
Delta float64 `json:"delta,omitempty"`
// N is the number of _synthetic=true rows with both A and B
// simultaneously non-null and dictionary-resolvable — the realized
// pair's own co-occurrence count. CategoricalPairSpec carries no
// source N of its own; the source total used to normalize its side
// of the comparison is recovered by summing Cells' Count.
N int `json:"n"`
// Error is set instead of Delta when the synthetic partition
// produced no co-occurring observation at all for this pair — mirrors
// PairwiseFidelity's own per-pair non-fatal failure contract.
Error string `json:"error,omitempty"`
}
CategoricalPairFidelity is one row of FidelityReport.CategoricalPairwise: the delta between a captured categorical-categorical pair's source joint-frequency table (CategoricalPairSpec.Cells — the same contingency cells synth/conditional_sample.go's categoricalPairSampler was built from) and that pair's REALIZED joint-frequency table over the _synthetic=true partition of the output cohort, expressed as total variation distance (see categoricalTVD) — the categorical-pair analogue of PairwiseFidelity's numeric-numeric correlation delta.
type CategoricalPairProfile ¶ added in v0.32.0
type CategoricalPairProfile struct {
A string `json:"a"`
B string `json:"b"`
Cells []ContingencyCell `json:"cells"`
// N is the number of rows in the capture where both A and B were
// simultaneously non-null — the pair's true co-occurrence count,
// summed across every cell (the cells kept plus whatever is folded
// into the "other"/"other" catch-all).
N int `json:"n"`
}
CategoricalPairProfile is one captured categorical-categorical pair's reconstruction input: a bounded contingency table of observed (a_value, b_value) co-occurrence counts. Values already reflect each field's own per-field top-K cap (ProfileOptions.TopK, via each field's own FieldProfile.Categorical.Top) — any category outside a field's own top-K collapses to "other" before the joint table is built — and the table is further bounded to at most ContingencyCellCap cells by co-occurrence count, with everything past that cut folded into one ("other","other") catch-all cell. The two caps compose: the existing per-field cap is an input bound to this story's new joint-cell cap, never replaced by it.
type CategoricalPairSpec ¶ added in v0.32.0
type CategoricalPairSpec struct {
A string `json:"a"`
B string `json:"b"`
Cells []CategoricalPairCellSpec `json:"cells"`
}
CategoricalPairSpec is one categorical-categorical pair's generation-time reconstruction input — the Spec-facing counterpart of CategoricalPairProfile, carrying only what a conditional resample needs (the cells; N is capture-time provenance and is not needed here).
type CategoricalProfile ¶
type CategoricalProfile struct {
Cardinality int `json:"cardinality"`
Top []CategoryHit `json:"top"`
}
CategoricalProfile holds the top-K observed values and the total distinct count.
type CategoryHit ¶
CategoryHit records a categorical value with its observed weight.
type ConditionalProfile ¶ added in v0.32.0
type ConditionalProfile struct {
// NumericPairs lists the numeric-numeric pairs captured: the
// row-aligned Pearson correlation and the count of rows where both
// fields were simultaneously non-null (N) — the input
// thinPairWarning checks against MinPairObservations. Capped at
// ProfileOptions.CorrelationTopK strongest |rho| pairs, same
// convention as Profile.Pairwise.
NumericPairs []NumericPairProfile `json:"numeric_pairs,omitempty"`
// CategoricalPairs lists the categorical-categorical pairs
// captured: a bounded contingency table of observed
// (a_value, b_value) co-occurrence counts per pair. See
// CategoricalPairProfile for the two composed collapse points
// (per-field top-K, then joint-cell ContingencyCellCap) that keep
// this section bounded regardless of either field's cardinality.
CategoricalPairs []CategoricalPairProfile `json:"categorical_pairs,omitempty"`
// CategoricalNumericPairs lists the categorical-numeric pairs
// captured: the numeric field's conditional mean/std broken out per
// observed category of the categorical field. See
// CategoricalNumericPairProfile for the single collapse point
// (per-field top-K) that keeps this section bounded — no separate
// joint-cardinality cap is needed here, unlike CategoricalPairs,
// because a conditional numeric summary is one row per already-capped
// category rather than a joint cell per pair of categories.
CategoricalNumericPairs []CategoricalNumericPairProfile `json:"categorical_numeric_pairs,omitempty"`
// SetCategoricalPairs lists the set-option x categorical pairs
// captured (E5-S2): one bounded contingency table per (set field,
// option, categorical field) combination, the set-option axis fixed
// to the two-value {"selected","not_selected"} Bernoulli domain. See
// SetCategoricalPairProfile.
SetCategoricalPairs []SetCategoricalPairProfile `json:"set_categorical_pairs,omitempty"`
// SetNumericPairs lists the set-option x numeric pairs captured
// (E5-S2): one conditional mean/std pair (selected vs. not_selected)
// per (set field, option, numeric field) combination. See
// SetNumericPairProfile.
SetNumericPairs []SetNumericPairProfile `json:"set_numeric_pairs,omitempty"`
// SetSetPairs lists the option x option pairs captured between two
// DIFFERENT set_* fields (E5-S2): a 2x2 contingency table per
// (fieldA option, fieldB option) combination. Never captured between
// two options of the same field. See SetSetPairProfile.
SetSetPairs []SetSetPairProfile `json:"set_set_pairs,omitempty"`
}
ConditionalProfile carries the joint reconstruction structure `profile create --conditional` captures beyond independent per-field marginals. E2-S1 covers numeric-numeric pairs; E3-S1 adds categorical-categorical pairs; E3-S2 adds categorical-numeric pairs — all to the same struct rather than a new top-level section, so the same nil-check keeps gating all of them. E5-S2 extends this to pairs involving a set_* field: SetCategoricalPairs, SetNumericPairs and SetSetPairs, one entry PER OPTION (bit position) rather than per field — E5-S1's design decision that each dictionary entry is its own independent Bernoulli sub-field composes directly with E3's pairwise machinery by running it once per option, reusing ContingencyCell / ContingencyCellCap / condCatNumAcc verbatim rather than inventing a parallel mechanism for set_* fields.
type ConstraintSpec ¶
type ConstraintSpec struct {
Expr string `json:"expr"`
}
ConstraintSpec wraps an expression evaluated against the in-memory row.
type ContingencyCell ¶ added in v0.32.0
type ContingencyCell struct {
AValue string `json:"a_value"`
BValue string `json:"b_value"`
Count int `json:"count"`
}
ContingencyCell is one observed (a_value, b_value) combination and its co-occurrence count, as captured by --conditional's categorical-categorical contingency table. AValue/BValue read "other" (otherCategoryLabel) when the underlying value fell outside its field's own top-K, or when the cell itself fell outside the pair's ContingencyCellCap.
type CorrelationSpec ¶
type CorrelationSpec struct {
A string `json:"a"`
B string `json:"b"`
Correlation float64 `json:"correlation"`
}
CorrelationSpec declares a target Pearson correlation between two numeric fields.
type CorrelationStat ¶
CorrelationStat is a captured pairwise correlation entry.
type DateProfile ¶
type DateProfile struct {
Start string `json:"start"`
End string `json:"end"`
Weekdays [7]int `json:"weekdays"`
}
DateProfile holds the (start, end) range of date values plus a weekday histogram for Mode-A reconstruction.
type FidelityReport ¶ added in v0.32.0
type FidelityReport struct {
SourceRows int `json:"source_rows"`
SyntheticRows int `json:"synthetic_rows"`
Fields []*FieldFidelity `json:"fields,omitempty"`
Pairwise []*PairwiseFidelity `json:"pairwise,omitempty"`
CategoricalPairwise []*CategoricalPairFidelity `json:"categorical_pairwise,omitempty"`
CategoricalNumericPairwise []*CategoricalNumericPairFidelity `json:"categorical_numeric_pairwise,omitempty"`
SetFields []*SetFieldFidelity `json:"set_fields,omitempty"`
SetCategoricalPairwise []*SetCategoricalPairFidelity `json:"set_categorical_pairwise,omitempty"`
SetNumericPairwise []*SetNumericPairFidelity `json:"set_numeric_pairwise,omitempty"`
SetSetPairwise []*SetSetPairFidelity `json:"set_set_pairwise,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
FidelityReport is the JSON document written to Options.FidelityReportPath, comparing the freshly generated rows against the source cohort's rows inside the one combined output. Fields is the per-field marginal section (E1-S2); Pairwise is the numeric-numeric correlation-delta section (E2-S3); CategoricalPairwise and CategoricalNumericPairwise are the categorical-categorical contingency-delta and categorical-numeric conditional-mean/std-delta sections (E3-S4); SetFields is the set_* per-option marginal frequency-delta section, and SetCategoricalPairwise / SetNumericPairwise / SetSetPairwise are the three set_* joint-structure delta sections (E5-S4) — all populated only when the spec/schema carried at least one entry of the matching kind — absent (omitempty), never an empty placeholder array, on every plain synthesis and every from-profile run with no captured structure of that kind. Warnings mirrors any thin-pair/thin-cell warnings the caller supplied (see Options.FidelityWarnings) — typically Profile.Warnings from a --conditional capture, covering every pair kind through the same shared shape — so a reader sees "how well did it match" (the pairwise sections) and "which parts were built on thin data" (Warnings) in one document (FR-18).
func BuildFidelityReport ¶ added in v0.32.0
func BuildFidelityReport(mergedSchema *encoding.Schema, records []byte, sourceRows, syntheticRows int, run TestRunner) *FidelityReport
BuildFidelityReport compares every eligible field of mergedSchema (skipping SyntheticFieldName itself) between the _synthetic=false and _synthetic=true partitions of records: TEST_KS for numeric fields, TEST_CHISQ for categorical fields, both driven through run; a direct per-option frequency comparison (computeSetFieldFidelity, no run/TestRunner involved) for set_* fields, appended onto report.SetFields instead of report.Fields (E5-S4 — see SetFieldFidelity for why this section does not reuse TEST_KS/ TEST_CHISQ). Fields of any other on-wire type (date, datetime, packed_bool, u4) are out of scope for both sections; later stories may widen coverage.
A single field's failing test (entry.Error set, entry.Result nil) never aborts the rest of the report — run is called once per field on its own Test, not once for every field on a shared Request.
type FieldFidelity ¶ added in v0.32.0
type FieldFidelity struct {
Field string `json:"field"`
Test types.TestType `json:"test"`
Result *types.TestResult `json:"result,omitempty"`
Error string `json:"error,omitempty"`
}
FieldFidelity is one row of FidelityReport.Fields: the marginal comparison between the source and generated partitions of a single output-cohort field, computed by driving the field's numeric values through TEST_KS (SplitBy=_synthetic) or its categorical values through TEST_CHISQ (contingency against _synthetic) — never new comparison math, always the existing operator run through the ordinary Process pipeline (see TestRunner).
Result is nil (Error carries the reason instead) when the operator itself could not produce a statistic for this field — e.g. a categorical column with fewer than 2 distinct values, or a numeric field with fewer than 2 observations in one partition — which never fails the surrounding synth call; a fidelity check that cannot run for one field must not withhold the report for every other field, let alone the generated cohort itself.
type FieldProfile ¶
type FieldProfile struct {
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description,omitempty"`
NullRate float64 `json:"null_rate"`
Numeric *NumericProfile `json:"numeric,omitempty"`
Categorical *CategoricalProfile `json:"categorical,omitempty"`
Date *DateProfile `json:"date,omitempty"`
// Set carries per-option marginal selection-frequency stats for a
// set_* (multi-select bitmask) field (E5-S1). Populated instead of
// Numeric/Categorical/Date — exactly one of the four is non-nil per
// field, based on the field's schema type.
Set *SetProfile `json:"set,omitempty"`
// Precision/Scale carry decimal128 metadata so synth-from-profile can
// reconstruct the original field shape.
Precision uint8 `json:"precision,omitempty"`
Scale uint8 `json:"scale,omitempty"`
}
FieldProfile holds per-field summary statistics. Exactly one of Numeric, Categorical, Date, or Set is populated based on the field's type.
type FieldSpec ¶
type FieldSpec struct {
Name string `json:"name"`
Type string `json:"type"`
Nullable bool `json:"nullable,omitempty"`
Description string `json:"description,omitempty"`
Distribution string `json:"distribution"`
Params map[string]any `json:"params,omitempty"`
// Precision and Scale apply to decimal128.
Precision uint8 `json:"precision,omitempty"`
Scale uint8 `json:"scale,omitempty"`
// NullRate is the per-row probability that the field will be null.
// Only meaningful when Nullable is true; ignored otherwise.
NullRate float64 `json:"null_rate,omitempty"`
}
FieldSpec is a single column declaration.
type NumericPairProfile ¶ added in v0.32.0
type NumericPairProfile struct {
A string `json:"a"`
B string `json:"b"`
Rho float64 `json:"rho"`
// N is the number of rows in the capture where both A and B were
// simultaneously non-null — the pair's true co-occurrence count,
// unlike Profile.Pairwise's Rho (computed from two independently
// capped reservoirs that can drift out of alignment once either
// field has nulls).
N int `json:"n"`
}
NumericPairProfile is one captured numeric-numeric pair's reconstruction input.
type NumericProfile ¶
type NumericProfile struct {
Min float64 `json:"min"`
Max float64 `json:"max"`
Mean float64 `json:"mean"`
Std float64 `json:"std"`
// Percentiles holds {p1, p5, p25, p50, p75, p95, p99} when
// IncludeStats was on; nil otherwise.
Percentiles []float64 `json:"percentiles,omitempty"`
// Shape carries a fitted 2-component Gaussian mixture, captured
// only when ProfileOptions.FitShape was set AND the mixture is a
// genuine improvement over the plain Mean/Std normal above (BIC
// selection — see fitNumericShape in shape.go). Additive +
// omitempty: absent from every profile document captured without
// --fit-shape (including every pre-existing document), and absent
// for any numeric field whose observed distribution is already
// close to normal — SpecFromProfile falls back to the ordinary
// Mean/Std/Min/Max normal reconstruction whenever this is nil,
// exactly as it always has.
Shape *ShapeProfile `json:"shape,omitempty"`
}
NumericProfile is the detail block for numeric fields.
type Options ¶
type Options struct {
// Seed makes the output deterministic. Same spec + same seed must
// produce a byte-identical .pulse file.
Seed int64
// SourceCohort activates the tagged top-up path (AugmentFromProfile)
// when non-empty: real rows from this cohort are copied into the
// output tagged _synthetic=false, spec.RowCount new rows are
// generated and tagged _synthetic=true, and the source is never
// opened for write. Leaving it empty (the default) reproduces
// today's behavior exactly — a plain synthesis with no tag column,
// used by synth from-schema and any caller building a Spec directly.
SourceCohort string
// FidelityReportPath activates the post-generation fidelity report
// (`synth from-profile --fidelity-report`) when non-empty: after
// generation completes, AugmentFromProfile drives TEST_KS (numeric
// fields, SplitBy=_synthetic) / TEST_CHISQ (categorical fields,
// contingency against _synthetic) against the tagged output cohort
// and writes the resulting FidelityReport JSON document to this
// path. Only meaningful alongside SourceCohort — the plain Synth
// path (SourceCohort empty) has no _synthetic partition to compare
// against and ignores this field entirely.
FidelityReportPath string
// FidelityWarnings, when non-empty, are copied verbatim onto the
// written FidelityReport's own Warnings slot — the mechanism that
// surfaces thin-pair warnings (e.g. Profile.Warnings from a
// `profile create --conditional` capture) beside the pairwise
// correlation deltas they qualify in the same document (FR-18),
// rather than requiring a caller to cross-reference the profile
// file separately. This package has no Profile type of its own to
// read Warnings from directly — the caller (internal/cli's `synth
// from-profile`, or any programmatic caller building Options from a
// Profile it already has in hand) is expected to populate this.
// Ignored when FidelityReportPath is empty, exactly like every
// other Fidelity* field.
FidelityWarnings []string
}
Options modulates how the spec is realized.
type PairwiseFidelity ¶ added in v0.32.0
type PairwiseFidelity struct {
A string `json:"a"`
B string `json:"b"`
SourceRho float64 `json:"source_rho"`
SyntheticRho float64 `json:"synthetic_rho,omitempty"`
// Delta is abs(SourceRho - SyntheticRho). Zero-valued and
// meaningless when Error is set.
Delta float64 `json:"delta,omitempty"`
// N is the number of _synthetic=true rows with both A and B
// simultaneously non-null — the realized pair's own co-occurrence
// count, distinct from (and typically much larger than) the source
// capture's own N.
N int `json:"n"`
// Error is set instead of SyntheticRho/Delta when the synthetic
// partition could not produce a defined correlation for this pair
// (fewer than two co-occurring non-null observations) — mirrors
// FieldFidelity's own per-field failure contract: one pair's
// failure never withholds the rest of the report.
Error string `json:"error,omitempty"`
}
PairwiseFidelity is one row of FidelityReport.Pairwise: the delta between a captured numeric-numeric pair's source correlation (the same rho synth/copula.go's conditional-Gaussian reconstruction was asked to hit — CorrelationSpec.Correlation, itself sourced from Profile.Conditional.NumericPairs when --conditional captured it, or Profile.Pairwise otherwise, via SpecFromProfile) and that pair's REALIZED Pearson correlation over the _synthetic=true partition of the output cohort. The realized figure is computed with the same `pearson` helper Profile capture itself uses (see computeConditionalNumericPairs) — never a second, divergent implementation, and the exact computation TestSynth_CorrelationReconstructionWithinTolerance and its siblings already assert against.
type Profile ¶
type Profile struct {
RowCount int `json:"row_count"`
Fields []FieldProfile `json:"fields"`
Pairwise []CorrelationStat `json:"pairwise,omitempty"`
// Conditional carries joint-reconstruction structure captured only
// when ProfileOptions.IncludeConditional was set. Additive and
// omitempty: absent on every profile document captured without
// --conditional, including every pre-existing one, and
// SpecFromProfile treats a nil Conditional exactly as it always
// has (falling back to Pairwise).
Conditional *ConditionalProfile `json:"conditional,omitempty"`
Warnings []string `json:"warnings,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
Profile is a serialization-friendly statistical summary of a cohort. It contains everything needed to drive synth from-profile without retaining any individual rows from the source data.
func ProfileBytes ¶
func ProfileBytes(data []byte, opts ProfileOptions) (*Profile, error)
ProfileBytes summarizes a .pulse file given its raw bytes.
func ProfileFile ¶
ProfileFile reads a .pulse file from fs and produces a Profile.
func (*Profile) MarshalJSON ¶
MarshalJSON serializes the profile.
type ProfileOptions ¶
type ProfileOptions struct {
// TopK is the number of top categorical values to capture per
// categorical field. Defaults to 32 when zero.
TopK int
// IncludeStats turns on percentile / stdev / kurtosis collection.
// When false, only mean / min / max / null-rate are recorded for
// numeric fields. Defaults to true.
IncludeStats bool
// IncludeCorrelations enables pairwise Pearson correlation capture
// between numeric fields. Off by default to keep profile size bounded.
IncludeCorrelations bool
// CorrelationTopK caps the number of strongest |rho| pairs retained.
// Defaults to 16 when IncludeCorrelations or IncludeConditional is
// true.
CorrelationTopK int
// IncludeConditional enables capture of the joint reconstruction
// structure (`profile create --conditional`): for numeric-numeric
// field pairs, a row-aligned correlation + observation count
// (Profile.Conditional.NumericPairs) that SpecFromProfile uses to
// drive the exact conditional-Gaussian reconstruction in
// synth/copula.go, in place of the plain (and, for cohorts with
// nulls, only approximately aligned) Pairwise correlation stats.
// Off by default; the profile document's `conditional` key is
// entirely absent (omitempty) when this is false, and every
// pre-existing profile document — which has no such key — remains
// valid SpecFromProfile input either way.
IncludeConditional bool
// SampleLimit caps the number of records ingested for the profile.
// Zero = unlimited.
SampleLimit int
// FitShape enables shape-fitting for numeric fields
// (`profile create --fit-shape`, E4-S2): instead of always
// summarizing a numeric field as a single normal(mean, std),
// attempt a 2-component Gaussian mixture fit and keep it — as
// FieldProfile.Numeric.Shape — only when it is a genuine
// improvement over the plain normal by BIC (see fitNumericShape in
// shape.go for the full selection rule and its documented
// limitations). Off by default; a field whose fit is not kept (not
// enough data, or the source is already close to normal) carries no
// Shape, and SpecFromProfile reconstructs it exactly as it always
// has. Forces sample retention (as if IncludeStats were also set)
// for numeric fields so there is something to fit against, even
// when IncludeStats itself is false.
FitShape bool
// Seed drives the reservoir-sampling RNG used by
// IncludeConditional's categorical-categorical contingency capture
// (E7-S2) once the source cohort exceeds conditionalJointCap rows.
// Zero (the default) is itself a valid, deterministic seed — same
// as synth.Options.Seed's convention — so two profile runs against
// the same source with the same Seed (including the unset zero
// value) produce byte-identical captured output. It does not affect
// any other capture path: every other section in this file is
// either an exact online accumulation or a first-N reservoir with
// no random draws.
Seed int64
}
ProfileOptions modulates how Profile summarizes a cohort.
type Result ¶
type Result struct {
RowsGenerated int `json:"rows_generated"`
RowsRejected int `json:"rows_rejected"`
OutputPath string `json:"output_path"`
Warnings []string `json:"warnings,omitempty"`
// FidelityReportPath echoes Options.FidelityReportPath when a
// fidelity report was written alongside the output cohort. Empty
// when the flag was not set.
FidelityReportPath string `json:"fidelity_report_path,omitempty"`
}
Result is what the writer reports after a successful Synth call.
func AugmentFromProfile ¶ added in v0.32.0
func AugmentFromProfile(fs afero.Fs, spec *Spec, sourcePath, output string, opts Options) (*Result, error)
AugmentFromProfile is the tagged top-up entry point behind `synth from-profile`. It never opens sourcePath for write: it reads the source cohort, generates spec.RowCount brand-new rows, and writes a single combined cohort to output containing every source row (tagged _synthetic=false) followed by every generated row (tagged _synthetic=true). output must be a distinct path from sourcePath — synthesis never mutates the source in place.
spec.RowCount is an explicit count of NEW rows to generate; it is never a "top up to N total" target. A 200-row source with spec.RowCount=500 produces a 700-row output.
func Synth ¶
Synth materializes a synthetic .pulse file from a Spec into the given path on the provided filesystem. It is the package-level entry point; pulse.Pulse.Synth wraps it.
When opts.SourceCohort is non-empty, this delegates to AugmentFromProfile instead: the source cohort's real rows are copied into output tagged _synthetic=false, spec.RowCount new rows are generated and tagged _synthetic=true, and the source is never opened for write. opts.SourceCohort empty (the default) reproduces the unmodified plain-synthesis behavior below, byte-for-byte.
type SetCategoricalPairFidelity ¶ added in v0.32.0
type SetCategoricalPairFidelity struct {
Set string `json:"set"`
Option string `json:"option"`
Categorical string `json:"categorical"`
Delta float64 `json:"delta,omitempty"`
// N is the number of _synthetic=true rows with both the set field
// and the categorical field simultaneously non-null — the realized
// pair's own co-occurrence count.
N int `json:"n"`
// Error is set instead of Delta when the synthetic partition
// produced no co-occurring observation at all for this pair —
// mirrors CategoricalPairFidelity's own per-pair contract.
Error string `json:"error,omitempty"`
}
SetCategoricalPairFidelity is one row of FidelityReport.SetCategoricalPairwise: the delta between a captured set-option x categorical pair's source contingency table (SetCategoricalPairSpec.Cells) and that pair's REALIZED contingency table over the _synthetic=true partition of the output cohort — the set-option analogue of CategoricalPairFidelity, with the A axis fixed to the option's {"selected","not_selected"} Bernoulli domain instead of an arbitrary categorical value set. Delta reuses categoricalTVD verbatim (SetCategoricalPairSpec.Cells is the same []CategoricalPairCellSpec shape CategoricalPairSpec.Cells is).
type SetCategoricalPairProfile ¶ added in v0.32.0
type SetCategoricalPairProfile struct {
Set string `json:"set"`
Option string `json:"option"`
Categorical string `json:"categorical"`
Cells []ContingencyCell `json:"cells"`
// N is the number of rows where both the set field and the
// categorical field were simultaneously non-null — this option's
// true co-occurrence count, mirroring CategoricalPairProfile.N.
N int `json:"n"`
}
SetCategoricalPairProfile is one captured set-option x categorical pair's reconstruction input (E5-S2): the bounded contingency table between one dictionary option (bit position) of a set_* field — treated as its own two-valued "selected"/"not_selected" Bernoulli sub-field, per E5-S1's per-option representation — and a categorical field's observed values. Reuses the exact same ContingencyCell / ContingencyCellCap / otherCategoryLabel machinery CategoricalPairProfile already established for two categorical fields; the only difference is the A axis is always the fixed two-value domain {"selected","not_selected"} rather than an arbitrary categorical value set, so it needs no per-field top-K collapse of its own — only B's (the categorical field's) values are collapsed, exactly as CategoricalPairProfile already does for either of its two axes.
type SetCategoricalPairSpec ¶ added in v0.32.0
type SetCategoricalPairSpec struct {
Set string `json:"set"`
Option string `json:"option"`
Categorical string `json:"categorical"`
Cells []CategoricalPairCellSpec `json:"cells"`
}
SetCategoricalPairSpec is one set-option x categorical pair's generation-time reconstruction input — the Spec-facing counterpart of SetCategoricalPairProfile. Cells reuse CategoricalPairCellSpec verbatim: AValue is always "selected"/"not_selected" (the set option's own fixed two-value domain), BValue is the categorical field's observed value.
type SetFieldFidelity ¶ added in v0.32.0
type SetFieldFidelity struct {
Field string `json:"field"`
Options []*SetOptionFidelity `json:"options,omitempty"`
}
SetFieldFidelity is one row of FidelityReport.SetFields: the per-option marginal frequency comparison for a set_* (multi-select bitmask) field, extending E1-S2's per-field marginal section to set_* fields (E5-S4). It deliberately does NOT reuse TEST_KS/ TEST_CHISQ the way FieldFidelity does — driving each option through the ordinary Process pipeline would need a presentation schema widening every option into its own pseudo-categorical field, purely to satisfy TEST_CHISQ's Rows/Cols contract (the same trick SyntheticAsCategoricalSchema plays for _synthetic itself, multiplied by option count). A direct frequency comparison is simpler, equally rigorous for a Bernoulli marginal (E5-S1's own representation), and composes with the same per-option addressing SetProfile.Options and ConditionalProfile's three set-pair kinds already use — see computeSetFieldFidelity.
type SetNumericPairFidelity ¶ added in v0.32.0
type SetNumericPairFidelity struct {
Set string `json:"set"`
Option string `json:"option"`
Numeric string `json:"numeric"`
Categories []*CategoricalNumericCategoryFidelity `json:"categories,omitempty"`
}
SetNumericPairFidelity is one row of FidelityReport.SetNumericPairwise: the per-bucket delta between a captured set-option x numeric pair's source conditional mean/std (SetNumericPairSpec.Categories, keyed "selected"/"not_selected") and that bucket's REALIZED conditional mean/std over the _synthetic=true partition — the set-option analogue of CategoricalNumericPairFidelity.
type SetNumericPairProfile ¶ added in v0.32.0
type SetNumericPairProfile struct {
Set string `json:"set"`
Option string `json:"option"`
Numeric string `json:"numeric"`
// Categories carries at most two entries, keyed "selected" /
// "not_selected" — reuses CategoricalNumericCategoryStat verbatim
// rather than a bespoke two-field struct, so a caller already
// handling CategoricalNumericPairProfile.Categories needs no second
// shape to learn.
Categories []CategoricalNumericCategoryStat `json:"categories"`
// N mirrors CategoricalNumericPairProfile.N: the total non-null
// co-occurrence count summed across both categories.
N int `json:"n"`
}
SetNumericPairProfile is one captured set-option x numeric pair's reconstruction input (E5-S2): the numeric field's conditional mean/std broken out by whether the set field's option (bit position) was selected — the set-field analogue of CategoricalNumericPairProfile, with the categorical axis fixed to the two-value {"selected","not_selected"} domain instead of an arbitrary category set.
type SetNumericPairSpec ¶ added in v0.32.0
type SetNumericPairSpec struct {
Set string `json:"set"`
Option string `json:"option"`
Numeric string `json:"numeric"`
Categories []CategoricalNumericCategorySpec `json:"categories"`
Min float64 `json:"min,omitempty"`
Max float64 `json:"max,omitempty"`
HasClamp bool `json:"has_clamp,omitempty"`
}
SetNumericPairSpec is one set-option x numeric pair's generation-time reconstruction input — the Spec-facing counterpart of SetNumericPairProfile. Categories carries at most two entries keyed "selected" / "not_selected", reusing CategoricalNumericCategorySpec verbatim. Min/Max/HasClamp mirror CategoricalNumericPairSpec: the numeric field's own observed range, applied identically regardless of which bucket the conditional draw lands in.
type SetOptionFidelity ¶ added in v0.32.0
type SetOptionFidelity struct {
Value string `json:"value"`
// SourceFrequency/SyntheticFrequency are P(bit set) over each
// partition's non-null rows of Field. Delta is
// abs(SourceFrequency - SyntheticFrequency). SyntheticFrequency and
// Delta are zero-valued and meaningless when Error is set.
SourceFrequency float64 `json:"source_frequency"`
SyntheticFrequency float64 `json:"synthetic_frequency,omitempty"`
Delta float64 `json:"delta,omitempty"`
// N is the number of _synthetic=true rows with a non-null Field —
// the realized synthetic partition's own denominator for this
// option's frequency, shared across every option of the same field.
N int `json:"n"`
// Error is set instead of SyntheticFrequency/Delta when the
// synthetic partition has no non-null observation of Field at all —
// mirrors every other Fidelity entry kind's non-fatal per-entry
// contract.
Error string `json:"error,omitempty"`
}
SetOptionFidelity is one dictionary entry (bit position)'s realized selection-frequency comparison between the source (_synthetic=false) and synthetic (_synthetic=true) partitions of the output cohort — the set_* analogue of FieldFidelity, one entry per option rather than one entry per field, mirroring CategoricalNumericCategoryFidelity's per-category breakout for the same reason: a set_* field reconstructs one independent Bernoulli marginal per option rather than a single scalar.
type SetOptionStat ¶ added in v0.32.0
type SetOptionStat struct {
Value string `json:"value"`
// Count is the number of non-null rows with this option's bit set.
Count int `json:"count"`
// Frequency is Count / SetProfile.N — P(bit set), the per-option
// marginal Bernoulli rate.
Frequency float64 `json:"frequency"`
}
SetOptionStat is one dictionary entry (bit position)'s observed marginal selection frequency within a set_* field's non-null rows.
type SetProfile ¶ added in v0.32.0
type SetProfile struct {
// N is the number of non-null rows Options' frequencies are computed
// over — the same denominator FieldProfile.NullRate already implies,
// surfaced here so a caller reading only the Set block can interpret
// Count as a rate without cross-referencing NullRate.
N int `json:"n"`
// Options lists EVERY dictionary entry in bit/dictionary order (bit i
// first) — NOT sorted by frequency like CategoricalProfile.Top — so
// two profiles of the same schema are directly comparable
// position-for-position regardless of which option happens to be
// more popular.
Options []SetOptionStat `json:"options"`
}
SetProfile holds per-option (marginal) selection-frequency statistics for a set_* (multi-select bitmask) field. E5-S1's design decision: each dictionary entry (bit position) is profiled as an independent Bernoulli-selected sub-field — Frequency is P(bit i set | field non-null) — matching "select all that apply" semantics, where each option genuinely is its own independent yes/no question a respondent answers. This is deliberately NOT "whole mask as one categorical" (which would treat every distinct combination as an arbitrary, combinatorially exploding category) nor "N fully independent binaries with no shared identity" (which would drop the fact that every option rides one bounded, shared dictionary — the same per-option addressing E5-S2's joint/conditional capture reuses). Full rationale: skills/synthetic-data.md ("Set (multi-select) field profiling").
This section is marginal-only: joint/conditional structure between options, or between a set_* field and another field, is E5-S2's job.
type SetSetPairFidelity ¶ added in v0.32.0
type SetSetPairFidelity struct {
SetA string `json:"set_a"`
OptionA string `json:"option_a"`
SetB string `json:"set_b"`
OptionB string `json:"option_b"`
Delta float64 `json:"delta,omitempty"`
// N is the number of _synthetic=true rows with both set fields
// simultaneously non-null — the realized pair's own co-occurrence
// count.
N int `json:"n"`
// Error is set instead of Delta when the synthetic partition
// produced no co-occurring observation at all for this pair.
Error string `json:"error,omitempty"`
}
SetSetPairFidelity is one row of FidelityReport.SetSetPairwise: the delta between a captured option x option pair's source 2x2 contingency table (SetSetPairSpec.Cells) between two DIFFERENT set_* fields, and that pair's REALIZED 2x2 contingency table over the _synthetic=true partition — the cross-field-option analogue of CategoricalPairFidelity, with both axes fixed to their own option's {"selected","not_selected"} domain.
type SetSetPairProfile ¶ added in v0.32.0
type SetSetPairProfile struct {
SetA string `json:"set_a"`
OptionA string `json:"option_a"`
SetB string `json:"set_b"`
OptionB string `json:"option_b"`
Cells []ContingencyCell `json:"cells"`
// N is the number of rows where both fields were simultaneously
// non-null — this option pair's true co-occurrence count.
N int `json:"n"`
}
SetSetPairProfile is one captured option x option pair's reconstruction input between two DIFFERENT set_* fields (E5-S2): a bounded 2x2-cell contingency table of {"selected","not_selected"} x {"selected","not_selected"} co-occurrence counts for one option of each field. Never captured between two options of the SAME set field — E5-S2's scope is cross-field option association only, per the PRD's "do two different set_* fields' option selections correlate with each other?" framing.
type SetSetPairSpec ¶ added in v0.32.0
type SetSetPairSpec struct {
SetA string `json:"set_a"`
OptionA string `json:"option_a"`
SetB string `json:"set_b"`
OptionB string `json:"option_b"`
Cells []CategoricalPairCellSpec `json:"cells"`
}
SetSetPairSpec is one option x option pair's generation-time reconstruction input between two DIFFERENT set_* fields — the Spec-facing counterpart of SetSetPairProfile. Cells reuse CategoricalPairCellSpec verbatim: AValue is SetA's OptionA state ("selected"/"not_selected"), BValue is SetB's OptionB state.
type ShapeProfile ¶ added in v0.32.0
type ShapeProfile struct {
Means []float64 `json:"means"`
Stds []float64 `json:"stds"`
Weights []float64 `json:"weights"`
}
ShapeProfile is a fitted 2-component Gaussian mixture: parallel means/stds/weights, directly usable as the "mixture" distribution's (synth.DistMixture, E4-S1) own "means"/"stds"/"weights" params with no translation needed at generation time. See fitNumericShape in shape.go for how and when this gets populated.
type Spec ¶
type Spec struct {
// RowCount is the number of rows to generate. Required (>0).
RowCount int `json:"row_count"`
// Fields declares each column with its type and distribution.
Fields []FieldSpec `json:"fields"`
// Constraints, if non-empty, are evaluated per row by the expression
// evaluator. Rows that fail any constraint are rejected and re-drawn.
Constraints []ConstraintSpec `json:"constraints,omitempty"`
// MaxRejectionRate caps the fraction of rejected rows during
// constraint-driven rejection sampling. Defaults to 0.5 if zero.
MaxRejectionRate float64 `json:"max_rejection_rate,omitempty"`
// Correlations lists optional pairwise correlations to induce via
// Gaussian copula post-processing. Each entry references two numeric
// fields by name with a target Pearson correlation in [-1, 1]. Only
// numeric fields can participate.
Correlations []CorrelationSpec `json:"correlations,omitempty"`
// CategoricalPairs lists optional categorical-categorical joint
// structure to reproduce during generation: for each pair, field B
// is resampled conditioned on field A's independently-drawn value
// using the captured contingency cells, instead of being drawn from
// its own unconditional marginal. Populated by SpecFromProfile from
// ConditionalProfile.CategoricalPairs (`profile create --conditional`)
// when present; absent (nil, the zero value) reproduces today's
// independent-marginal behavior exactly — every pre-existing spec,
// hand-authored or profile-derived, has no such key.
CategoricalPairs []CategoricalPairSpec `json:"categorical_pairs,omitempty"`
// CategoricalNumericPairs lists optional categorical-numeric
// conditional structure to reproduce during generation: for each
// pair, numeric field B is resampled from Normal(mean, std)
// parameterized by categorical field A's drawn value, using the
// captured per-category conditional mean/std, instead of being drawn
// from its own unconditional normal. Populated by SpecFromProfile
// from ConditionalProfile.CategoricalNumericPairs when present;
// absent (nil) reproduces today's independent-marginal behavior
// exactly.
CategoricalNumericPairs []CategoricalNumericPairSpec `json:"categorical_numeric_pairs,omitempty"`
// SetCategoricalPairs lists optional set-option x categorical joint
// structure to reproduce during generation (E5-S3): for each pair,
// the set field's declared Option (bit) is resampled from
// Bernoulli(P(selected | categorical field's drawn value)) using the
// captured contingency cells, instead of the option's own
// independent marginal frequency. Populated by SpecFromProfile from
// ConditionalProfile.SetCategoricalPairs when present; absent (nil)
// reproduces the independent per-option marginal behavior exactly.
SetCategoricalPairs []SetCategoricalPairSpec `json:"set_categorical_pairs,omitempty"`
// SetNumericPairs lists optional set-option x numeric joint
// structure to reproduce during generation (E5-S3): for each pair,
// numeric field B is resampled from Normal(mean, std) conditioned on
// whether the set field's declared Option (bit) was independently
// drawn selected, using the captured per-bucket conditional
// mean/std. Populated by SpecFromProfile from
// ConditionalProfile.SetNumericPairs when present; absent (nil)
// reproduces today's independent-marginal behavior exactly.
SetNumericPairs []SetNumericPairSpec `json:"set_numeric_pairs,omitempty"`
// SetSetPairs lists optional option x option joint structure between
// two DIFFERENT set_* fields to reproduce during generation (E5-S3):
// for each pair, set B's declared OptionB (bit) is resampled from
// Bernoulli(P(selected | set A's OptionA drawn state)) using the
// captured 2x2 contingency cells. Populated by SpecFromProfile from
// ConditionalProfile.SetSetPairs when present; absent (nil)
// reproduces the independent per-option marginal behavior exactly.
SetSetPairs []SetSetPairSpec `json:"set_set_pairs,omitempty"`
}
Spec is the parsed top-level synthesis request. It is the in-memory shape of from-schema JSON. A from-profile call builds a Spec internally from the Profile and shares the rest of the writer pipeline.
func SpecFromProfile ¶
SpecFromProfile builds a Spec the synth pipeline can execute. Numeric fields are reconstructed as normal distributions (mean, std clipped at min/max), categorical fields as weighted_categorical, and date fields as uniform_date over the observed range.
The second return value carries any conditional-relationship conflict warnings resolveConflicts (synth/conflict.go, E6-S1) produces when run against the just-composed Spec — e.g. two captured pairs both legitimately targeting the same field. generate() (synth/writer.go) independently runs the same pass again at its own setup time, so these two calls always agree; this one exists only because Profile.Warnings is serialized to disk at `profile create` time, before SpecFromProfile ever runs at `synth from-profile` time — there is no way to retroactively write a synth-time conflict warning back into that already-persisted document. Returning the warnings here lets a caller (internal/cli's `synth from-profile`) fold them into the same FidelityWarnings channel it already uses for prof.Warnings, so both surface together through `--fidelity-report`. Callers that don't need them may discard with `_`.
type TestRunner ¶ added in v0.32.0
type TestRunner func(physicalSchema, presentedSchema *encoding.Schema, records []byte, test *types.Test) (*types.TestResult, error)
TestRunner executes a single statistical Test against an encoded record buffer and returns its result. BuildFidelityReport calls it once per eligible field rather than reimplementing KS/chi-square math itself.
This package cannot import processing directly to provide its own implementation: descriptor imports synth (for the built-in distribution registry), and processing's own internal test files import descriptor — synth importing processing back would close that cycle. pulse.go, which already imports both synth and processing, supplies the real implementation and is the only intended caller of BuildFidelityReport.
physicalSchema is records' actual on-wire layout and MUST be used to decode it. presentedSchema is a view schema — same fields, same order, but with SyntheticFieldName's type widened from packed_bool to categorical_u8 (see SyntheticAsCategoricalSchema) — that the implementation must attach to each decoded record for type and dictionary lookups instead of physicalSchema. The two diverge only so the provenance column can satisfy TEST_KS.SplitBy / TEST_CHISQ's categorical requirement without changing the on-wire byte format.