Skip to content

fix(tests): correct five DateTime-uncertainty oracle values to match CQF reference-engine assertions - #175

Open
zacharywhitley wants to merge 1 commit into
cqframework:mainfrom
zacharywhitley:fix/datetime-uncertainty-oracle-corrections
Open

zacharywhitley wants to merge 1 commit into
cqframework:mainfrom
zacharywhitley:fix/datetime-uncertainty-oracle-corrections

Conversation

@zacharywhitley

Copy link
Copy Markdown

This PR corrects five <output> oracle values in
tests/cql/CqlTypesTest.xml and
tests/cql/CqlDateTimeOperatorsTest.xml whose current values
contradict the first-party CQF reference-engine Kotlin unit tests at
engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/.
Every corrected value is the value the Kotlin engine already asserts
by direct unit test; the fixture oracles were originally authored to
a stricter-than-spec rule that expanded the same-precision operand
to a sub-precision range, and the drift has been latent while the
Kotlin tests served as the de-facto oracle and the fixtures as a
secondary check.

Normative basis

  • CQL 1.5.2 §5.5.2 "Determining Difference and Duration"
    (https://cql.hl7.org/05-languagesemantics.html#determining-difference-and-duration):
    an operand is uncertain at precision P iff its stored precision
    is strictly less specific than P; a same-precision operand is
    used as a canonical point. The §5.5.2 worked example is explicit:
    for days between DateTime(2014, 1, 15) and DateTime(2014, 2) the
    DAY-precision LHS is the fixed point (its max and min coincide at
    Feb 15) and only the MONTH-precision RHS expands to [Feb 1, Feb 28], yielding [17, 44].
  • CQL 1.5.2 §5.5.3 "Uncertainty Propagation"
    (https://cql.hl7.org/05-languagesemantics.html#uncertainty): an
    arithmetic operator applied to two Interval<Integer> operands
    produces a new interval whose endpoints are the endpoint-wise
    application of the operator (start = op(A.start, B.start); end = op(A.end, B.end)). This is the behavior implemented in the
    CQF reference engine's AddEvaluator.kt, SubtractEvaluator.kt,
    and MultiplyEvaluator.kt (file:line citations below). It is NOT
    full-interval arithmetic ([A.low - B.high, A.high - B.low]); the
    two differ on the Subtract case (full-interval would give
    [1, 39]; endpoint-pairwise gives [12, 28]).

The strict-less-than admissibility rule is encoded verbatim in the
engine at
engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/runtime/DateTime.kt:197-205
(isUncertain(p)), and the endpoint-pairwise propagation rule in
.../elm/executing/AddEvaluator.kt:96-102,
.../SubtractEvaluator.kt:140-148, and
.../MultiplyEvaluator.kt:85-93.

Per-case diff

# File Line Before After Kotlin oracle (file:line + assert)
1 tests/cql/CqlTypesTest.xml 128 <output>Interval [ 18, 49 ]</output> <output>Interval [ 19, 49 ]</output> engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/CqlTypesTest.kt:163-165 — assertEquals(19.toCqlInteger(), (value as Interval).start, "DateTimeUncertain"); assertEquals(49.toCqlInteger(), value.end, "DateTimeUncertain")
2 tests/cql/CqlDateTimeOperatorsTest.xml 880 <output>Interval[ 4, 16 ]</output> <output>Interval[ 5, 16 ]</output> engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.kt:45-48 — assertIs<Interval>(value); assertEquals(5.toCqlInteger(), value.start); assertEquals(16.toCqlInteger(), value.end)
3 tests/cql/CqlDateTimeOperatorsTest.xml 891 <output>Interval[ 32, 88 ]</output> <output>Interval[ 34, 88 ]</output> engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.kt:52-55 — assertIs<Interval>(value); assertEquals(34.toCqlInteger(), value.start); assertEquals(88.toCqlInteger(), value.end)
4 tests/cql/CqlDateTimeOperatorsTest.xml 902 <output>Interval[ 0, 40 ]</output> <output>Interval[ 12, 28 ]</output> engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.kt:57-60 — assertIs<Interval>(value); assertEquals(12.toCqlInteger(), value.start); assertEquals(28.toCqlInteger(), value.end)
5 tests/cql/CqlDateTimeOperatorsTest.xml 913 <output>Interval[ 256, 1936 ]</output> <output>Interval[ 289, 1936 ]</output> engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/CqlDateTimeOperatorsTest.kt:62-65 — assertIs<Interval>(value); assertEquals(289.toCqlInteger(), value.start); assertEquals(1936.toCqlInteger(), value.end)

Case-by-case derivation

Case #1 — DateTimeUncertain (days between DateTime(2015, 2, 10) and DateTime(2015, 3)). LHS stored DAY, requested DAY → not
uncertain; used as fixed point Feb-10. RHS stored MONTH, requested
DAY → uncertain, expanded to [Mar-01, Mar-31]. Low bound =
days_between(Feb-10, Mar-01) = 19; high bound =
days_between(Feb-10, Mar-31) = 49. Result [19, 49]. The prior
fixture value [18, 49] requires expanding the DAY-stored LHS to
its sub-day millisecond range, which §5.5.2 does not authorize and
DateTime.isUncertain does not do.

Case #2 — DateTimeDurationBetweenUncertainInterval2
(months between DateTime(2005) and DateTime(2006, 5)). LHS stored
YEAR, requested MONTH → uncertain, expanded to
[2005-01-01, 2005-12-01]. RHS stored MONTH, requested MONTH → not
uncertain; used as fixed point 2006-05-01. Low bound =
months_between(2005-12, 2006-05) = 5; high bound =
months_between(2005-01, 2006-05) = 16. Result [5, 16]. The prior
fixture value [4, 16] requires expanding the MONTH-stored RHS to
its sub-month day range, which §5.5.2 does not authorize.

Case #3 — DateTimeDurationBetweenUncertainAdd (sum of two
row-37 intervals). Inner inputs each evaluate to [17, 44] per
§5.5.2 and CqlDateTimeOperatorsTest.kt:42-43. Add is endpoint-
pairwise per AddEvaluator.kt:96-102:
[17+17, 44+44] = [34, 88]. The prior fixture value [32, 88]
derives from a stricter-than-spec inner [16, 44] doubled; both the
inner and the propagation are corrected to the Kotlin-asserted
values.

Case #4 — DateTimeDurationBetweenUncertainSubtract (row-37
interval minus case-#2 interval). Inner inputs: LHS [17, 44], RHS
[5, 16]. Subtract is endpoint-pairwise per
SubtractEvaluator.kt:140-148: [17-5, 44-16] = [12, 28]. The
prior fixture value [0, 40] derives from stricter-than-spec inner
values [16, 44] and [4, 16] endpoint-pairwise'd; both the inner
inputs and the result are corrected. Note that [12, 28] is NOT the
full-interval-arithmetic result ([1, 39]); CQF is explicitly
endpoint-pairwise, which is the behavior preserved by this
correction.

Case #5 — DateTimeDurationBetweenUncertainMultiply (square of
two row-37 intervals). Multiply is endpoint-pairwise per
MultiplyEvaluator.kt:85-93: [17*17, 44*44] = [289, 1936]. The
prior fixture value [256, 1936] derives from [16*16, 44*44] with
the same stricter-than-spec inner.

Downstream engine impact

The CQF reference engine's engine/src/jvmTest/... Kotlin tests
already assert the proposed values, so this PR does NOT require any
reference-engine change. It only aligns the shared fixture corpus
(which downstream engines consume as an acceptance oracle) with the
reference engine's actual behavior.

Downstream consumer coordination

This fixture oracle correction will cause a flip for any downstream
engine that currently matches the stricter-than-spec oracle (and
will continue to flip for any engine that does NOT). One such
downstream — cql-wasm — is actively implementing the
§5.5.2-compliant uncertainty contract and the §5.5.3 endpoint-
pairwise interval arithmetic (M9) under internal docket
CQL-W54-LOWER-TEMPORAL-UNCERTAINTY-CONTRACT-1. cql-wasm will
publish a parallel downstream implementation PR coordinated with
this upstream fixture correction. No request is made here for the
downstream PR to merge first; this fixture PR is independently
correct against the CQF reference-engine oracle and against the
§5.5.2 / §5.5.3 normative text.

Backward compatibility

Fixture corrections are retroactive — any engine whose result was
previously accepted because it matched the stricter-than-spec oracle
will now be exposed as actually wrong. This is the intent: aligning
the fixture oracle with the first-party Kotlin reference removes a
stale acceptance of spec-divergent behavior. Engines that pass today
by matching [17, 44] on row-37 (which the pre-existing
DateTimeDurationBetweenUncertainInterval fixture already asserts
and CqlDateTimeOperatorsTest.kt:40-43 already verifies) are
unaffected.

Checklist

  • Five <output> edits applied as per table above
  • No other fixture oracle changed
  • No test grouping, capability code, or expression text modified
  • XML well-formedness preserved (no whitespace outside the edited <output> element)
  • CQF reference-engine Kotlin tests at engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/*.kt pass unchanged (they already assert the proposed values)

…CQF reference-engine assertions

Correct five `<output>` oracle values in tests/cql/CqlTypesTest.xml and
tests/cql/CqlDateTimeOperatorsTest.xml whose current values contradict
the first-party CQF reference-engine Kotlin unit tests at
engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/*.kt.
Every corrected value is the value the Kotlin engine already asserts
by direct unit test; the fixture oracles were originally authored to a
stricter-than-spec rule that expanded the same-precision operand to a
sub-precision range, and the drift has been latent while the Kotlin
tests served as the de-facto oracle and the fixtures as a secondary
check.

Per-case diff:

- tests/cql/CqlTypesTest.xml:128 DateTimeUncertain
  Interval [ 18, 49 ] -> Interval [ 19, 49 ]
  Kotlin oracle: CqlTypesTest.kt:163-165
- tests/cql/CqlDateTimeOperatorsTest.xml:880 DateTimeDurationBetweenUncertainInterval2
  Interval[ 4, 16 ] -> Interval[ 5, 16 ]
  Kotlin oracle: CqlDateTimeOperatorsTest.kt:45-48
- tests/cql/CqlDateTimeOperatorsTest.xml:891 DateTimeDurationBetweenUncertainAdd
  Interval[ 32, 88 ] -> Interval[ 34, 88 ]
  Kotlin oracle: CqlDateTimeOperatorsTest.kt:52-55
- tests/cql/CqlDateTimeOperatorsTest.xml:902 DateTimeDurationBetweenUncertainSubtract
  Interval[ 0, 40 ] -> Interval[ 12, 28 ]
  Kotlin oracle: CqlDateTimeOperatorsTest.kt:57-60
- tests/cql/CqlDateTimeOperatorsTest.xml:913 DateTimeDurationBetweenUncertainMultiply
  Interval[ 256, 1936 ] -> Interval[ 289, 1936 ]
  Kotlin oracle: CqlDateTimeOperatorsTest.kt:62-65

Normative basis: CQL 1.5.2 §5.5.2 "Determining Difference and Duration"
(strict-less-than admissibility rule, encoded at DateTime.kt:197-205)
and §5.5.3 "Uncertainty Propagation" (endpoint-pairwise interval
arithmetic, encoded at AddEvaluator.kt:96-102,
SubtractEvaluator.kt:140-148, MultiplyEvaluator.kt:85-93).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant