Skip to main content

PictModel

PictModel parses a complete PICT-format model — parameters, sub-models, and constraints — into a single object that can directly generate rows. It is exported from a separate entry point so it does not bloat the bundle of consumers who only need make.

PICT superset

CoverTable's model format is a superset of Microsoft PICT. It extends the original syntax with features such as arithmetic expressions ([A] * [B] > 100, ([A] + 1) * [B] <= 500), comment lines (#) in the constraint section, and the ~ negative-value prefix in output.

Models written for CoverTable may not run in the original PICT tool if they use these extensions. If you need compatibility with Microsoft PICT, avoid using arithmetic operators and other extended syntax in your constraints.

import { PictModel } from "covertable/pict";

const model = new PictModel(`
Type: Single, Span, Stripe, Mirror, RAID-5
Size: 10, 100, 500, 1000, 5000, 10000, 40000
Format method: Quick, Slow
File system: FAT, FAT32, NTFS
Cluster size: 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
Compression: On, Off

IF [File system] = "FAT" THEN [Size] <= 4096;
IF [File system] = "FAT32" THEN [Size] <= 32000;
`);

const rows = model.make();

Constructor

new PictModel(input: string, options?: PictModelOptions)
OptionTypeDefaultDescription
caseInsensitivebooleantrueWhen true (default, matching PICT), constraint comparisons and alias lookups ignore case.
strictbooleanfalseWhen true, the constructor throws PictModelError if any error-severity issue is collected.

The input string is split into three sections:

  1. ParametersName: value1, value2, ...
  2. Sub-models{ A, B, C } @ N
  3. ConstraintsIF [P] = "x" THEN [Q] = "y"; or unconditional [P] <> [Q];

Properties

PropertyTypeDescription
parametersRecord<string, (string | number)[]>Parsed parameter values (canonical form, with aliases removed).
subModelsSubModelType[]Parsed sub-model definitions.
constraints(FilterType | null)[]Parsed constraint filters. null entries indicate parse failure.
negativesMap<string, Set<string | number>>Values prefixed with ~ (invalid / negative-test values) per parameter.
weightsRecord<string, Record<number, number>>Weights extracted from (N) syntax.
issuesPictModelIssue[]All issues collected during parsing.
progressnumberCoverage progress (0 to 1) during generation.
statsControllerStats | nullGeneration statistics (available after make/makeAsync).

Issues

interface PictModelIssue {
severity: "error" | "warning";
source: "factor" | "subModel" | "constraint";
index: number; // 0-based index within the source
line: number; // 1-based line number in the original input
message: string;
}
const model = new PictModel(`
A: 1, 2
Empty:
`);
model.issues;
// [{ severity: "error", source: "factor", index: 1, line: 3,
// message: 'No values for parameter "Empty"' }]

Methods

MethodDescription
filter(row)Returns true if the row satisfies all constraints and has at most one invalid value.
make(options?)Generates rows with the model's parameters, constraints, sub-models, and weights. Accepts the same options as the top-level make.
makeAsync(options?)Generator version of make.

Supported PICT Syntax

FeatureExampleNotes
ParameterType: Single, Span, StripeBasic parameter declaration.
Comment# this is a commentLines starting with # are skipped.
Quoted stringsMsg: "hello, world"Required for values containing commas.
Parameter referenceB: <A>, extraExpands to all of parameter A's values.
AliasesOS: Windows | Win, LinuxFirst value is canonical. Aliases work in constraints.
Invalid valuesType: Valid, ~InvalidNegative-test value. At most one per row. Output includes ~ prefix.
WeightsBrowser: Chrome (10), FirefoxBiases value selection during completion.
Sub-models{ A, B, C } @ 3Different N-wise strength for listed parameters.
ConditionalIF [A] = 1 THEN [B] = 2;Standard PICT-style conditional constraints.
Unconditional[A] <> [B];Always-applied invariants.
Comparisons=, <>, >, <, >=, <=, IN, LIKEAll standard PICT operators.
LogicalAND, OR, NOTWith parentheses for grouping.

Invalid Values (~)

When a value is prefixed with ~, it is treated as a value that should appear in tests only one at a time. PictModel automatically rejects rows containing two or more invalid values. The ~ prefix is preserved in output rows.

const model = new PictModel(`
Age: 20, 30, ~-1, ~999
Country: Japan, USA, ~"Mars"
`);

const rows = model.make();
// { Age: 20, Country: "Japan" } — both valid
// { Age: "~-1", Country: "Japan" } — Age is invalid, Country is valid
// { Age: 20, Country: "~Mars" } — Age is valid, Country is invalid
// { Age: "~-1", Country: "~Mars" } — REJECTED (two invalid values)

Aliases

Aliases provide alternative names for the same value. The first name is canonical (appears in output), while alternates are accepted in constraints.

const model = new PictModel(`
OS: "Windows 10" | Win10, "Mac OS" | Mac, Linux
Result: pass, fail

IF [OS] IN {"Win10", "Mac"} THEN [Result] = "pass";
`);

weightsByValue

A helper that converts a value-keyed weight map into the index-keyed form expected by weights.

import { make } from "covertable";
import { weightsByValue } from "covertable/pict";

const factors = {
Browser: ["Chrome", "Firefox", "Safari"],
};

make(factors, {
weights: weightsByValue(factors, {
Browser: { Chrome: 10, Safari: 5 },
}),
// equivalent to: weights: { Browser: { 0: 10, 2: 5 } }
});
Try it interactively

Try the Compatible PICT tool to experiment with the constraint syntax in your browser.

Differences from the Original PICT

CoverTable's PictModel accepts PICT-format input, but the underlying engine works differently from the original PICT tool developed by Microsoft.

Generation Algorithm

Original PICTCoverTable
Greedy strategyPicks the combination with the most uncovered (OPEN) slots in a bitvector, then selects a value that maximizes globally covered pairsScores each uncovered pair by how many other uncovered pairs it would cover when added to the current row
Value selectionPickValue() maximizes complete (fully-bound combinations) then totalZeros (global coverage)Greedy selects full pairs; remaining factors are filled by close() using depth-first backtracking
Tie-breakingrand() with a configurable seedDeterministic hash (FNV1a32) of pair indices + salt string

Constraint Processing

Original PICTCoverTable
ApproachPre-compiles constraints into explicit exclusion sets before generationEvaluates constraints at runtime using three-valued (Kleene) logic
Internal formIF [A]="x" THEN [B]<>"y" → exclusion {A:"x", B:"y"}Constraints remain as expressions and are evaluated per candidate
ANDCross-product of exclusion sets (can grow exponentially)Short-circuit evaluation; returns False as soon as any branch fails
PropagationExclusionDeriver derives implicit exclusions before generationforwardCheck prunes domains dynamically (AC-3-style arc consistency)
BacktrackingExclusions are baked into a bitvector; no backtracking neededDepth-first backtracking fills remaining factors when greedy selection stalls
FlexibilityDSL only (IF/THEN/ELSE, comparisons, IN, LIKE)Supports arbitrary lambda predicates via the custom operator

Seeding / Presets

Original PICTCoverTable
APIPictAddSeed() / CLI -e seedfilepresets option (list of partial-row dicts/objects)
FormatArray of (parameter, value_index) tuplesValue dictionaries — e.g. { OS: "Win", Browser: "Chrome" }
Conflict handlingSeeds violating exclusions are silently removed by fixRowSeeds()Presets are processed first; close() fills remaining factors respecting constraints

Sub-models

Original PICTCoverTable
StructureFully hierarchical — sub-models are independent Model instances combined via pseudo-parametersFlat key-set partitioning with per-set strength
NestingSub-models can be nested arbitrarilySingle level only
OverlapParameters can appear in multiple sub-models; conflicts resolved by exclusion generationEach parameter belongs to at most one sub-model

Weights

Original PICTCoverTable
Syntaxvalue (weight) in model file / valueWeights[] in APIweights option — { factor: { valueIndex: weight } }
EffectInfluences tie-breaking in PickValue() and random-row generationInfluences only the close() (backtracking completion) phase — higher-weight values are tried first

Randomization and Determinism

Original PICTCoverTable
DefaultDeterministic (seed 0)Deterministic (hash sorter with empty salt)
Randomize-r [seed] flag; uses srand()/rand()Change salt for reproducible variation; use random sorter for non-deterministic output

Output and Streaming

Original PICTCoverTable
OutputBatch only — all rows generated before any are returned (TSV or JSON from CLI; index array from C API)Generator-based — rows yielded incrementally via makeAsync() / make_async()
FormatTab-separated table or JSON array of objectsPython list/dict rows; TypeScript typed arrays/objects with full type inference

Platform

Original PICTCoverTable
LanguageC++ (with C-compatible API via pictapi.h)Pure Python 3 + pure TypeScript — no native code, no compilation
RuntimeCompiled binary; Windows, Linux, macOSPython: pip install; TypeScript: npm install; also runs in browsers