perf(planning): validate each optimization configuration once
Every objective evaluation revalidated its optimization configuration. Rule normalization already validates each configuration once per calculation and keeps its own copy, the presets are constant, and the summed variant of a valid configuration stays valid, so the core now scores them without revalidating. evaluateOptimization still validates for other callers. Part of #302. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b7ec411c92
commit
9d9404c3db
4 files changed
+43
-14
No files matched your search
@@ -89,3 +89,22 @@ largest gap stayed between 58 and 67 ms in every run. Cancelling `three years`
|
||||
one second after the request returned the retained plan 3.9 to 4.0 ms after the
|
||||
cancel message reached the worker, against 7.2 to 8.7 ms before. This is desktop
|
||||
evidence, not a reference-phone measurement.
|
||||
|
||||
### Validate each optimization configuration once
|
||||
|
||||
Every objective evaluation revalidated its optimization configuration against
|
||||
the catalog. Rule normalization already validates each configuration once per
|
||||
calculation and keeps its own copy. The presets are constant, and the summed
|
||||
variant of a valid configuration is valid because every catalog row accepts
|
||||
the sum. The core therefore scores them without revalidating;
|
||||
`evaluateOptimization` still validates for its other callers.
|
||||
|
||||
| Scenario | Before (s) | After (s) |
|
||||
| --------------------------- | ---------: | --------: |
|
||||
| representative year | 0.35 | 0.30 |
|
||||
| typed obligations | 4.03 | 3.46 |
|
||||
| chained earned entitlements | 0.01 | 0.01 |
|
||||
| deliberate conflict | 0.00 | 0.00 |
|
||||
| loaded year | 48.12 | 44.03 |
|
||||
| two years | 1.04 | 0.96 |
|
||||
| three years | 3.35 | 3.03 |
|
||||
@@ -24,7 +24,7 @@ import { addPlanningYears, isPlanningDate, planningDateInParis } from './plannin
|
||||
import {
|
||||
compareObjectiveLists,
|
||||
compareObjectiveValues,
|
||||
evaluateOptimization,
|
||||
evaluateValidatedOptimization,
|
||||
firstDifferentObjective,
|
||||
OPTIMIZATION_PRESETS,
|
||||
validateOptimizationConfiguration,
|
||||
@@ -1896,11 +1896,12 @@ function reconcileOptimizationProvenance(
|
||||
// Another tied maximum can mask a placement's improvement to its own element.
|
||||
observations ??= optimizationObservations(state, context);
|
||||
const withoutObservations = optimizationObservations(withoutState, context);
|
||||
// Every catalog row accepts the sum, so this stays a valid configuration.
|
||||
const summed = { ...rule.configuration, aggregation: 'sum' as const };
|
||||
const summedComparison = compareObjectiveValues(
|
||||
objective.direction,
|
||||
evaluateOptimization(summed, observations),
|
||||
evaluateOptimization(summed, withoutObservations),
|
||||
evaluateValidatedOptimization(summed, observations),
|
||||
evaluateValidatedOptimization(summed, withoutObservations),
|
||||
);
|
||||
contributes =
|
||||
summedComparison < 0 ||
|
||||
@@ -1930,14 +1931,14 @@ function supportingCalendarBenefit(
|
||||
): boolean {
|
||||
const observations = optimizationObservations(after, context);
|
||||
return (
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
|
||||
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
|
||||
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2654,7 +2655,7 @@ function* optimizeRulePlacements(
|
||||
item.configuration.measure === 'public-holiday-week-value' &&
|
||||
item.configuration.direction === 'maximize'
|
||||
)
|
||||
return evaluateOptimization(item.configuration, {
|
||||
return evaluateValidatedOptimization(item.configuration, {
|
||||
leaveBalances: [],
|
||||
publicHolidayWeeks: context.holidayWeeks.map(({ value }) => ({
|
||||
value,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isWorkingPlanningDate } from './declared-non-working-days';
|
||||
import type { TimeOffPeriod } from './leave-planner.core';
|
||||
import {
|
||||
evaluateOptimization,
|
||||
evaluateValidatedOptimization,
|
||||
type OptimizationConfiguration,
|
||||
type OptimizationObservations,
|
||||
} from './planning-objectives';
|
||||
@@ -683,11 +683,12 @@ export function workSequenceDates(state: ObservedPlan, context: ObservationCalen
|
||||
}));
|
||||
}
|
||||
|
||||
/** The configuration was validated when the calculation's rules were normalized. */
|
||||
export function evaluateStateOptimization(
|
||||
configuration: OptimizationConfiguration,
|
||||
state: ObservedPlan,
|
||||
context: ObservationCalendar,
|
||||
observations = optimizationObservations(state, context),
|
||||
): number {
|
||||
return evaluateOptimization(configuration, observations);
|
||||
return evaluateValidatedOptimization(configuration, observations);
|
||||
}
|
||||
@@ -212,6 +212,14 @@ export function evaluateOptimization(
|
||||
): number {
|
||||
const validation = validateOptimizationConfiguration(configuration);
|
||||
if (!validation.valid) throw new Error(`${validation.code}: ${validation.message}`);
|
||||
return evaluateValidatedOptimization(configuration, observations);
|
||||
}
|
||||
|
||||
/** Scores a configuration that {@link validateOptimizationConfiguration} already accepted. */
|
||||
export function evaluateValidatedOptimization(
|
||||
configuration: OptimizationConfiguration,
|
||||
observations: OptimizationObservations,
|
||||
): number {
|
||||
const selected = (condition: OptimizationCondition) =>
|
||||
configuration.conditions.includes(condition);
|
||||
let values: number[];
|
||||
|
||||
Reference in new issue
Block a user