Something went wrong. Try again.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Something went wrong. Try again.
8.1 kB · 246 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247// The standard block registry — every shipping block under a *versioned*// registry code (`levelset.PeakCap.v1`). Versioned names are the// forward-compatibility seam: when a block's semantics change, ship a `.v2`// and keep `.v1` resolving, so a run config saved in March still produces// the same schedule in September. Renames get an alias entry, not a break.//// `createRegistry` stays side-effect-free and singleton-less; this module// just curates the defs. Apps with their own catalogs (codes, tiers,// shadows) keep building their own registries — or extend this one via// `standardRegistry(extraDefs)`.
import { ConcurrentResourceCostBlock } from "./concurrentResourceCost.ts";import { ConcurrentUnitsLimitBlock } from "./concurrentUnitsLimit.ts";import { CrewBorrowBlock } from "./crewBorrow.ts";import { DeadlineBlock } from "./deadline.ts";import { GateBlock } from "./gate.ts";import { HiringLagPenaltyBlock } from "./hiringLagPenalty.ts";import { KBestBlock } from "./kBest.ts";import { MakespanBlock } from "./makespan.ts";import { MaxConcurrentResourceBlock } from "./maxConcurrentResource.ts";import { ModeSelectionBlock } from "./modeSelection.ts";import { OpenUnitPenaltyBlock } from "./openUnitPenalty.ts";import { ParetoBlock } from "./pareto.ts";import { PhaseReleaseBlock } from "./phaseRelease.ts";import { PeakCapBlock } from "./peakCap.ts";import { PhaseEarlinessBlock } from "./phaseEarliness.ts";import { PhaseLatenessBlock } from "./phaseLateness.ts";import { createRegistry, type BlockDef, type BlockRegistry,} from "./registry.ts";import { ReleaseBlock } from "./release.ts";import { SerialSGSBlock } from "./serialSgsSearch.ts";import { UnimodalDeviationBlock } from "./unimodalDeviation.ts";
/** Versioned registry codes for the shipping blocks. */export const STANDARD_CODES = { maxConcurrentResource: "levelset.MaxConcurrentResource.v1", peakCap: "levelset.PeakCap.v1", concurrentUnitsLimit: "levelset.ConcurrentUnitsLimit.v1", deadline: "levelset.Deadline.v1", release: "levelset.Release.v1", phaseRelease: "levelset.PhaseRelease.v1", modeSelection: "levelset.ModeSelection.v1", crewBorrow: "levelset.CrewBorrow.v1", concurrentResourceCost: "levelset.ConcurrentResourceCost.v1", openUnitPenalty: "levelset.OpenUnitPenalty.v1", hiringLagPenalty: "levelset.HiringLagPenalty.v1", phaseLateness: "levelset.PhaseLateness.v1", phaseEarliness: "levelset.PhaseEarliness.v1", unimodalDeviation: "levelset.UnimodalDeviation.v1", makespan: "levelset.Makespan.v1", serialSGS: "levelset.SerialSGS.v1", kBest: "levelset.KBest.v1", pareto: "levelset.Pareto.v1", gate: "levelset.Gate.v1",} as const;
export function standardBlockDefs(): BlockDef[] { return [ { code: STANDARD_CODES.maxConcurrentResource, family: "constraint", tier: "estimator", block: MaxConcurrentResourceBlock, defaultShadow: { resourceUniqueId: 0, max: 1 }, assumptionTemplate: "at most {max} concurrent tasks on resource {resourceUniqueId}", }, { code: STANDARD_CODES.peakCap, family: "constraint", tier: "estimator", block: PeakCapBlock, defaultShadow: { resourceUniqueId: 0, cap: 1 }, assumptionTemplate: "peak demand on resource {resourceUniqueId} capped at {cap} units", }, { code: STANDARD_CODES.concurrentUnitsLimit, family: "constraint", tier: "estimator", block: ConcurrentUnitsLimitBlock, defaultShadow: { unitIds: [], max: 1 }, assumptionTemplate: "at most {max} work units open at once", }, { code: STANDARD_CODES.deadline, family: "constraint", tier: "estimator", block: DeadlineBlock, defaultShadow: { taskUniqueId: 0, latestFinish: 0 }, assumptionTemplate: "task {taskUniqueId} finishes by day {latestFinish}", }, { code: STANDARD_CODES.release, family: "constraint", tier: "estimator", block: ReleaseBlock, defaultShadow: { taskUniqueId: 0, earliestStart: 0 }, assumptionTemplate: "task {taskUniqueId} starts no earlier than day {earliestStart}", }, { code: STANDARD_CODES.phaseRelease, family: "constraint", tier: "estimator", block: PhaseReleaseBlock, defaultShadow: { taskUniqueIds: [0], earliestStart: 0 }, assumptionTemplate: "phase tasks start no earlier than day {earliestStart}", }, { code: STANDARD_CODES.modeSelection, family: "constraint", tier: "engineer", block: ModeSelectionBlock, defaultShadow: { taskUniqueId: 0, modes: [{ modeId: 1, durationDays: 1, resourceDemand: [] }], }, assumptionTemplate: "task {taskUniqueId} chooses among its listed duration/crew modes", }, { code: STANDARD_CODES.crewBorrow, family: "constraint", tier: "engineer", block: CrewBorrowBlock, defaultShadow: { taskUniqueIds: [0], homeResourceUniqueId: 0, borrowResourceUniqueId: 1, }, assumptionTemplate: "listed tasks may substitute resource {borrowResourceUniqueId} for home resource {homeResourceUniqueId}", }, { code: STANDARD_CODES.concurrentResourceCost, family: "scorer", tier: "estimator", block: ConcurrentResourceCostBlock, defaultShadow: { resourceUniqueId: 0, basePrice: 100, growthBase: 2, threshold: 0, }, }, { code: STANDARD_CODES.openUnitPenalty, family: "scorer", tier: "estimator", block: OpenUnitPenaltyBlock, defaultShadow: { unitIds: [], softMax: 0, weight: 1 }, }, { code: STANDARD_CODES.hiringLagPenalty, family: "scorer", tier: "estimator", block: HiringLagPenaltyBlock, defaultShadow: { resourceUniqueId: 0, trainingWeeks: 2, costPerCrewWeek: 1000, }, }, { code: STANDARD_CODES.phaseLateness, family: "scorer", tier: "estimator", block: PhaseLatenessBlock, defaultShadow: { phases: [] }, }, { code: STANDARD_CODES.phaseEarliness, family: "scorer", tier: "estimator", block: PhaseEarlinessBlock, defaultShadow: { phases: [] }, }, { code: STANDARD_CODES.unimodalDeviation, family: "scorer", tier: "engineer", block: UnimodalDeviationBlock, defaultShadow: { resourceUniqueId: 0, allowSecondPeak: false, jitterTolerance: 1, }, }, { code: STANDARD_CODES.makespan, family: "scorer", tier: "sales", block: MakespanBlock, defaultShadow: {}, }, { code: STANDARD_CODES.serialSGS, family: "search", tier: "engineer", block: SerialSGSBlock, defaultShadow: {}, }, { code: STANDARD_CODES.kBest, family: "collector", tier: "engineer", block: KBestBlock, // Placeholder scorer reference — the app rewires `by` to real scorer // instanceIds before building a config (same spirit as the id-0 // placeholders on the constraint shadows). defaultShadow: { k: 3, by: ["makespan"] }, }, { code: STANDARD_CODES.pareto, family: "collector", tier: "engineer", block: ParetoBlock, defaultShadow: { over: ["makespan", "cost"] }, }, { code: STANDARD_CODES.gate, family: "collector", tier: "engineer", block: GateBlock, // Placeholder scorer reference + bound — the app rewires `limits` to // real scorer instanceIds and caps before building a config. defaultShadow: { limits: [{ scorer: "makespan", max: 0 }], fallback: "none", }, }, ];}
/** A registry pre-loaded with the standard defs; pass extra defs to extend * (plugins, app-specific blocks, `.v2` revisions). */export function standardRegistry( extraDefs?: readonly BlockDef[],): BlockRegistry { return createRegistry([...standardBlockDefs(), ...(extraDefs ?? [])]);}