import { VecN } from '@holotope/core'; import { type XpbdIncrementalPotentialEvaluationN } from './xpbd-incremental-potential.js'; import { type XpbdIncrementalPotentialStepFilterN, type XpbdIncrementalPotentialStepFilterResultN } from './xpbd-incremental-potential-step-filter.js'; import { XpbdParticleN, type XpbdConservativeForceProviderN } from './xpbd-world.js'; export interface CompileXpbdIncrementalPotentialProblemNOptions { /** Ambient Euclidean dimension. */ readonly dimension: number; /** Complete authored particle identity list. */ readonly particles: readonly XpbdParticleN[]; /** Inertial prediction in particle order. */ readonly predictedPositions: readonly VecN[]; /** Positive outer-step duration in seconds. */ readonly deltaTime: number; /** Conservative candidate-state force providers. */ readonly providers: readonly XpbdConservativeForceProviderN[]; /** Optional ordered particle-space admissible-step filters. */ readonly stepFilters?: readonly XpbdIncrementalPotentialStepFilterN[]; } /** Packed free-coordinate evaluation with the complete particle-space evidence. */ export interface XpbdPackedIncrementalPotentialEvaluationN { readonly coordinates: Float64Array; readonly positions: readonly VecN[]; readonly objective: number; readonly gradient: Float64Array; readonly gradientNorm: number; readonly evaluation: XpbdIncrementalPotentialEvaluationN; } /** Defensive live-particle snapshot captured with one compiled step problem. */ export interface XpbdIncrementalPotentialParticleStateN { readonly index: number; readonly particle: XpbdParticleN; readonly particleId: string; readonly position: VecN; readonly velocity: VecN; readonly force: VecN; readonly inverseMass: number; readonly gravityScale: number; } /** * Deterministic solver view over one particle-identity incremental objective. * * Dynamic particles are packed in authored particle order, with all RN axes * contiguous. Fixed particles occupy no coordinates and are restored from * the compiled inertial prediction. */ export declare class XpbdIncrementalPotentialProblemN { readonly dimension: number; readonly particles: readonly XpbdParticleN[]; readonly predictedPositions: readonly VecN[]; readonly deltaTime: number; readonly providers: readonly XpbdConservativeForceProviderN[]; /** * Ordered particle-space filters, consulted for every automatic warm-start * movement and before Armijo trials. */ readonly stepFilters: readonly XpbdIncrementalPotentialStepFilterN[]; readonly freeParticleIndices: readonly number[]; readonly variableCount: number; private readonly compiledInverseMasses; private readonly compiledParticleStates; constructor(options: CompileXpbdIncrementalPotentialProblemNOptions); /** Returns defensive copies of the exact live state captured at compilation. */ particleStatesBeforeStep(): readonly XpbdIncrementalPotentialParticleStateN[]; /** Flattens only dynamic particles and verifies prescribed coordinates. */ packPositions(positions: readonly VecN[]): Float64Array; /** Restores particle-space positions from packed free coordinates. */ unpackPositions(coordinates: ArrayLike): readonly VecN[]; /** Evaluates objective and gradient without applying the candidate state. */ evaluate(coordinates: ArrayLike): XpbdPackedIncrementalPotentialEvaluationN; private assertCurrentMasses; } export declare function compileXpbdIncrementalPotentialProblemN(options: CompileXpbdIncrementalPotentialProblemNOptions): XpbdIncrementalPotentialProblemN; export interface SearchXpbdIncrementalPotentialArmijoNOptions { readonly problem: XpbdIncrementalPotentialProblemN; readonly coordinates: ArrayLike; readonly direction: ArrayLike; /** Default one. */ readonly initialStep?: number; /** Open interval `(0, 1)`; default `0.5`. */ readonly contractionFactor?: number; /** Armijo coefficient in `(0, 1)`; default `1e-4`. */ readonly sufficientDecrease?: number; /** Default 32. */ readonly maximumTrials?: number; } export type XpbdArmijoTrialStatusN = 'accepted' | 'insufficient-decrease' | 'domain-refused'; export interface XpbdArmijoDomainRefusalN { readonly lawId: string; readonly reason: string; readonly message: string; } export interface XpbdArmijoTrialN { readonly index: number; readonly stepLength: number; readonly coordinates: Float64Array; readonly armijoUpperBound: number; readonly status: XpbdArmijoTrialStatusN; readonly objective?: number; readonly refusal?: XpbdArmijoDomainRefusalN; } interface XpbdArmijoSearchBaseN { readonly base: XpbdPackedIncrementalPotentialEvaluationN; readonly directionalDerivative: number; readonly trials: readonly XpbdArmijoTrialN[]; /** Ordered admissible-step evidence evaluated for the initial segment. */ readonly stepFilters: readonly XpbdIncrementalPotentialStepFilterResultN[]; } export interface XpbdArmijoAcceptedN extends XpbdArmijoSearchBaseN { readonly status: 'accepted'; readonly stepLength: number; readonly accepted: XpbdPackedIncrementalPotentialEvaluationN; } export interface XpbdArmijoNotDescentN extends XpbdArmijoSearchBaseN { readonly status: 'not-descent'; } export interface XpbdArmijoExhaustedN extends XpbdArmijoSearchBaseN { readonly status: 'exhausted'; } /** Why Armijo could not begin from a certified positive step. */ export type XpbdArmijoStepFilterRefusalReasonN = 'indeterminate' | 'no-positive-step'; /** Explicit pre-trial refusal from an admissible-step filter. */ export interface XpbdArmijoStepFilterRefusedN extends XpbdArmijoSearchBaseN { /** Evaluated valid state from which the search direction begins. */ readonly base: XpbdPackedIncrementalPotentialEvaluationN; /** Dot product of the base gradient and proposed direction. */ readonly directionalDerivative: number; /** Empty because refusal occurs before an objective trial. */ readonly trials: readonly XpbdArmijoTrialN[]; /** Ordered certifications including the blocking filter result. */ readonly stepFilters: readonly XpbdIncrementalPotentialStepFilterResultN[]; /** Distinct refusal rather than search exhaustion. */ readonly status: 'step-filter-refused'; /** Whether certification failed or yielded no positive Float64 step. */ readonly reason: XpbdArmijoStepFilterRefusalReasonN; /** First authored filter that prevented a trial. */ readonly blockingFilter: XpbdIncrementalPotentialStepFilterResultN; } export type XpbdArmijoSearchResultN = XpbdArmijoAcceptedN | XpbdArmijoNotDescentN | XpbdArmijoExhaustedN | XpbdArmijoStepFilterRefusedN; /** * Deterministic Armijo backtracking over a compiled free-coordinate problem. * * Only typed potential-domain refusals are recoverable. Every malformed, * arithmetic, lineage, and generic provider error is rethrown. */ export declare function searchXpbdIncrementalPotentialArmijoN(options: SearchXpbdIncrementalPotentialArmijoNOptions): XpbdArmijoSearchResultN; /** * The same search, from a base state the caller has already evaluated. * * The minimizer holds the evaluation of its current iterate — that is what * `current` *is* — so letting the search re-derive it costs one full pass over * every registered provider **per line search**, for a value already in hand. * On a contact-dense scene that is the largest single duplication in the step. * * Per line search is the exact invariant, and it equals one per accepted * iteration except when a search is the one that terminates the step: a run * whose first search is refused outright saves an evaluation while accepting no * iteration at all. What holds on every path is that a step costs * `1 + trials` provider evaluations rather than `1 + searches + trials`. * * This is deliberately not a public option. A caller who supplies a base that * is not the evaluation of `options.coordinates` gets a silently wrong search, * and that invariant is not expressible in the signature. Inside this package * it is guaranteed: the minimizer only ever passes an evaluation it obtained * from these exact coordinates. * * `caller` is reported as the public entry point on both paths, so an error * message never names an internal a caller did not invoke. * * @internal */ export declare function searchXpbdIncrementalPotentialArmijoFromBaseN(options: SearchXpbdIncrementalPotentialArmijoNOptions, base: XpbdPackedIncrementalPotentialEvaluationN): XpbdArmijoSearchResultN; /** * Admissible-step certification of one automatic warm-start displacement. * * Retained on the integrated step result whenever registered filters were * consulted before an automatically selected minimizer base was installed. * This is SEGMENT evidence and deliberately separate from the feasible-base * recovery's POINT evidence: the recovery answers "is the objective defined at * this coordinate", while this answers "does every registered filter certify * the movement from the authored anchor to it". The released defect was * exactly the conflation — an endpoint-feasible far-side target installed as * the base with no filter consulted. * * `requestedStepLength` is the packed Euclidean length of the complete * anchor-to-target displacement, so `certifiedStepLength` is a length in the * same units, exactly as the filter contract publishes it — never a unitless * fraction. */ export interface XpbdIncrementalPotentialWarmStartCertificationN { /** Packed Euclidean length of the automatic anchor-to-target displacement. */ readonly requestedStepLength: number; /** Ordered per-filter certifications of the complete displacement. */ readonly stepFilters: readonly XpbdIncrementalPotentialStepFilterResultN[]; /** Most restrictive verdict across every registered filter. */ readonly outcome: 'safe' | 'limited' | 'indeterminate'; /** * Certified length of the anchor-to-target displacement. * * This is the FILTERS' verdict, not a report of the installed base. It * equals `requestedStepLength` when the outcome is `safe`, zero when the * outcome is `indeterminate`, and the certified prefix when `limited`. * Under `feasible-inertial-prediction` the chord search then samples within * that certified movement, so the installed base may sit strictly closer to * the anchor than this length. The authoritative record of where the solve * actually began is `minimization.initial`. */ readonly certifiedStepLength: number; /** First filter that refused certification, when the outcome refused. */ readonly blockingFilter?: XpbdIncrementalPotentialStepFilterResultN; /** Filter whose certified prefix is the installed movement, when limited. */ readonly limitingFilter?: XpbdIncrementalPotentialStepFilterResultN; } /** One certified warm-start base with its complete filter evidence. */ export interface XpbdCertifiedWarmStartBaseN { /** Evidence retained on the integrated step result. */ readonly certification: XpbdIncrementalPotentialWarmStartCertificationN; /** Packed coordinates of the certified base to install. */ readonly baseCoordinates: Float64Array; } /** * Certifies the movement from the authored anchor to an automatic target. * * Returns `undefined` when there is nothing to certify: no registered filter, * or zero displacement. A `safe` resolution installs the exact target * coordinates, so a fully certified warm start is bitwise identical to the * uncertified one. A `limited` resolution installs the certified prefix * `anchor + (certified / requested) · (target − anchor)`, rounded per * coordinate exactly as an Armijo trial rounds a point on its own certified * segment. An `indeterminate` resolution installs the anchor: an uncertifiable * automatic movement does not happen, and all subsequent movement goes through * the Armijo search, whose every segment the filters certify. * * Point feasibility of the installed base is deliberately NOT decided here — * that remains the recovery's and the minimizer's job, on the exact channels * they already own. * * @internal */ export declare function certifyXpbdIncrementalPotentialWarmStartN(problem: XpbdIncrementalPotentialProblemN, anchorCoordinates: Float64Array, targetCoordinates: Float64Array, caller: string): XpbdCertifiedWarmStartBaseN | undefined; export {}; //# sourceMappingURL=xpbd-incremental-potential-problem.d.ts.map