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
|
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
|
cancel message reached the worker, against 7.2 to 8.7 ms before. This is desktop
|
||||||
evidence, not a reference-phone measurement.
|
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 {
|
import {
|
||||||
compareObjectiveLists,
|
compareObjectiveLists,
|
||||||
compareObjectiveValues,
|
compareObjectiveValues,
|
||||||
evaluateOptimization,
|
evaluateValidatedOptimization,
|
||||||
firstDifferentObjective,
|
firstDifferentObjective,
|
||||||
OPTIMIZATION_PRESETS,
|
OPTIMIZATION_PRESETS,
|
||||||
validateOptimizationConfiguration,
|
validateOptimizationConfiguration,
|
||||||
@@ -1896,11 +1896,12 @@ function reconcileOptimizationProvenance(
|
|||||||
// Another tied maximum can mask a placement's improvement to its own element.
|
// Another tied maximum can mask a placement's improvement to its own element.
|
||||||
observations ??= optimizationObservations(state, context);
|
observations ??= optimizationObservations(state, context);
|
||||||
const withoutObservations = optimizationObservations(withoutState, 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 summed = { ...rule.configuration, aggregation: 'sum' as const };
|
||||||
const summedComparison = compareObjectiveValues(
|
const summedComparison = compareObjectiveValues(
|
||||||
objective.direction,
|
objective.direction,
|
||||||
evaluateOptimization(summed, observations),
|
evaluateValidatedOptimization(summed, observations),
|
||||||
evaluateOptimization(summed, withoutObservations),
|
evaluateValidatedOptimization(summed, withoutObservations),
|
||||||
);
|
);
|
||||||
contributes =
|
contributes =
|
||||||
summedComparison < 0 ||
|
summedComparison < 0 ||
|
||||||
@@ -1930,14 +1931,14 @@ function supportingCalendarBenefit(
|
|||||||
): boolean {
|
): boolean {
|
||||||
const observations = optimizationObservations(after, context);
|
const observations = optimizationObservations(after, context);
|
||||||
return (
|
return (
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, observations) >
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.holidayWeekValue, before) ||
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, observations) >
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.nonWorkingDayGain, before) ||
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, observations) <
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.longestWorkSequence, before) ||
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, observations) <
|
||||||
evaluateOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
|
evaluateValidatedOptimization(OPTIMIZATION_PRESETS.shortSequenceDeficit, before)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2654,7 +2655,7 @@ function* optimizeRulePlacements(
|
|||||||
item.configuration.measure === 'public-holiday-week-value' &&
|
item.configuration.measure === 'public-holiday-week-value' &&
|
||||||
item.configuration.direction === 'maximize'
|
item.configuration.direction === 'maximize'
|
||||||
)
|
)
|
||||||
return evaluateOptimization(item.configuration, {
|
return evaluateValidatedOptimization(item.configuration, {
|
||||||
leaveBalances: [],
|
leaveBalances: [],
|
||||||
publicHolidayWeeks: context.holidayWeeks.map(({ value }) => ({
|
publicHolidayWeeks: context.holidayWeeks.map(({ value }) => ({
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isWorkingPlanningDate } from './declared-non-working-days';
|
import { isWorkingPlanningDate } from './declared-non-working-days';
|
||||||
import type { TimeOffPeriod } from './leave-planner.core';
|
import type { TimeOffPeriod } from './leave-planner.core';
|
||||||
import {
|
import {
|
||||||
evaluateOptimization,
|
evaluateValidatedOptimization,
|
||||||
type OptimizationConfiguration,
|
type OptimizationConfiguration,
|
||||||
type OptimizationObservations,
|
type OptimizationObservations,
|
||||||
} from './planning-objectives';
|
} 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(
|
export function evaluateStateOptimization(
|
||||||
configuration: OptimizationConfiguration,
|
configuration: OptimizationConfiguration,
|
||||||
state: ObservedPlan,
|
state: ObservedPlan,
|
||||||
context: ObservationCalendar,
|
context: ObservationCalendar,
|
||||||
observations = optimizationObservations(state, context),
|
observations = optimizationObservations(state, context),
|
||||||
): number {
|
): number {
|
||||||
return evaluateOptimization(configuration, observations);
|
return evaluateValidatedOptimization(configuration, observations);
|
||||||
}
|
}
|
||||||
@@ -212,6 +212,14 @@ export function evaluateOptimization(
|
|||||||
): number {
|
): number {
|
||||||
const validation = validateOptimizationConfiguration(configuration);
|
const validation = validateOptimizationConfiguration(configuration);
|
||||||
if (!validation.valid) throw new Error(`${validation.code}: ${validation.message}`);
|
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) =>
|
const selected = (condition: OptimizationCondition) =>
|
||||||
configuration.conditions.includes(condition);
|
configuration.conditions.includes(condition);
|
||||||
let values: number[];
|
let values: number[];
|
||||||
|
|||||||
Reference in new issue
Block a user