Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,14 @@ def test_the_three_donor_factors_are_exact_and_only_money_may_apply_them():
assert receipt["target_schedule"] == ird["target_schedule"]
for bound in ("lower", "upper"):
root = Fraction(**receipt["root_bounds"][bound])
assert Fraction(**receipt["numerator_mean_bounds"][bound]) == (
Fraction("1700.68")
+ Fraction("1714.86") * sum(root**index for index in range(3))
) / 4
assert (
Fraction(**receipt["numerator_mean_bounds"][bound])
== (
Fraction("1700.68")
+ Fraction("1714.86") * sum(root**index for index in range(3))
)
/ 4
)
assert entry["declaration"]["purpose"] == "money_normalization_input"


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import json
import os
from datetime import date
from decimal import Decimal, localcontext
from decimal import ROUND_FLOOR, ROUND_HALF_EVEN, Decimal, localcontext
from fractions import Fraction
from pathlib import Path

Expand Down Expand Up @@ -222,18 +222,78 @@ def test_treasury_fy2027_source_column_uses_chronicle_2026_and_2027_refuses(
)


def test_p1_rounds_once_and_the_truncated_numerator_refuses(operand_facts):
# p1_ty2026_to_ty2027_central's unrounded value truncates to a different numerator.
declaration = _entry("p1_ty2026_to_ty2027_central")["declaration"]
def _p1_numerators(declaration):
"""A P1 factor's numerator under one half-even rounding, and what truncation
or an intermediate rounding would declare instead, all in millionths."""
value = {period: Decimal(text) for _, period, _, text in _OPERANDS}
sources = [
row["selector"]["period_value"] for row in declaration["source_schedule"]
]
cent = Decimal("0.01")
with localcontext() as context:
context.prec = 60
growth = Decimal("2.7") + Decimal(declaration["growth"]["scenario_delta"])
q = (1 + growth / 100).sqrt().sqrt()
q6 = q.quantize(Decimal("0.000001"))
june, september = value["2026-Q2"], value["2026-Q3"]
denominator = sum(value[period] for period in sources) / 4

def mean(projected):
return (june + september + sum(projected)) / 4

def millionths(numerator, rounding=ROUND_HALF_EVEN):
ratio = numerator / denominator * 1000000
return int(ratio.to_integral_value(rounding=rounding))

exact = mean([september * q, september * q * q])
return millionths(exact), {
"truncated": millionths(exact, ROUND_FLOOR),
"numerator rounded to cents": millionths(exact.quantize(cent)),
"projected quarters rounded to cents": millionths(
mean([(september * q**power).quantize(cent) for power in (1, 2)])
),
"q rounded to six digits": millionths(
mean([september * q6, september * q6 * q6])
),
}


@pytest.mark.parametrize("profile", ["low", "central", "high"])
def test_p1_rounds_once_and_refuses_truncated_or_intermediate_numerators(
profile, operand_facts
):
factor_id = f"p1_ty2026_to_ty2027_{profile}"
declaration = _entry(factor_id)["declaration"]
assert declaration["source_period"] == 2026
assert [
row["selector"]["period_value"] for row in declaration["source_schedule"]
] == ["2025-Q2", "2025-Q3", "2025-Q4", "2026-Q1"]
declaration["expected_operational_factor"]["numerator"] = 1029852
with pytest.raises(ValueError, match="expected factor mismatch"):
resolve_reference_factor(
declaration, operand_facts, _document()["feed_identity"]
)
once, alternatives = _p1_numerators(declaration)
assert once == _EXPECTED[factor_id]
for numerator in sorted(set(alternatives.values()) - {once}):
declaration["expected_operational_factor"]["numerator"] = numerator
with pytest.raises(ValueError, match="expected factor mismatch"):
resolve_reference_factor(
declaration, operand_facts, _document()["feed_identity"]
)


def test_p1_profiles_separate_one_rounding_from_each_alternative():
"""Each alternative misses some profile's declared numerator, so the
refusal test above exercises every alternative."""
missed = set()
for profile in ("low", "central", "high"):
declaration = _entry(f"p1_ty2026_to_ty2027_{profile}")["declaration"]
once, alternatives = _p1_numerators(declaration)
missed |= {
name for name, numerator in alternatives.items() if numerator != once
}
assert missed == {
"truncated",
"numerator rounded to cents",
"projected quarters rounded to cents",
"q rounded to six digits",
}


def test_root_enclosure_certifies_no_rounded_quarters_and_irrational_root():
Expand All @@ -247,9 +307,14 @@ def test_root_enclosure_certifies_no_rounded_quarters_and_irrational_root():
assert lower**4 < radicand < upper**4
assert receipt["root_bounds"]["exact"] is False
denominator = _fraction(receipt["denominator_mean"])
assert _fraction(receipt["numerator_mean_bounds"]["lower"]) == (
Fraction("1700.68") + Fraction("1714.86") * sum(lower**i for i in range(3))
) / 4
assert (
_fraction(receipt["numerator_mean_bounds"]["lower"])
== (
Fraction("1700.68")
+ Fraction("1714.86") * sum(lower**i for i in range(3))
)
/ 4
)
for bound in ("lower", "upper"):
value = _fraction(receipt["unrounded_factor_bounds"][bound])
assert (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -728,39 +728,10 @@ def add_quantiles(rows):
)
rows[at:at] = synthetic

def add_measurement(rows):
"""A compile-only producer of the stand-in columns, until G declares one."""
if any(
row.get("params", {}).get("measurement_id") == _MEASUREMENT for row in rows
):
return
prepare = next(row for row in rows if row["id"].endswith(".targets.prepare"))
rows.insert(
rows.index(prepare),
{
"id": prepare["id"].removesuffix(".targets.prepare")
+ ".synthetic_measurement",
"kernel": "measure.combine@1",
"population": prepare["population"],
"inputs": [{"entity": "person", "columns": ["age"]}],
"outputs": [
{"entity": "person", "column": column, "dtype": dtype}
for column, dtype in (
("ird_taxable_income_proxy_cents", "Int64"),
("ird_taxable_income_proxy_nzd", "float64"),
("ird_taxable_income_proxy_universe_status", "string"),
)
],
"params": {"measurement_id": _MEASUREMENT},
},
)

add_quantiles(resources["transport_graph"]["nodes"])
add_measurement(resources["transport_graph"]["nodes"])
for scenario in resources["scenarios"]["scenarios"]:
if scenario["tier"] == "calibration":
add_quantiles(scenario["nodes"])
add_measurement(scenario["nodes"])
rows = _instantiated_nz_rows(spec)
produced = {
(output["entity"], output["column"])
Expand Down Expand Up @@ -891,12 +862,13 @@ def test_nz_packaged_graph_compiles_with_only_pins_and_money_producers():
def test_every_calibration_node_has_its_own_standin_as_the_only_writer():
"""R2C-15 on every calibration node, the variant branches' included.

Composition checks the measurement proof on the skeleton alone:
``_check_measurement_proofs`` runs before the scenario extensions append
their branches, so it never sees ``nz.v.<V>.calibrate``. For each
calibration node of the composed graph, exactly one compiled ancestor
declares the measurement, it is that branch's stand-in, and it is the
only ancestor whose outputs or EXPAND cells hold each proved column.
Composition runs ``_check_measurement_proofs`` on the skeleton and again
on the extended graph, where the declaration names no producer for
``nz.v.<V>.calibrate`` and the compiled graph has to prove one. This test
checks NZ's graph directly: for each calibration node of the composed
graph, exactly one compiled ancestor declares the measurement, it is that
branch's stand-in, and it is the only ancestor whose outputs or EXPAND
cells hold each proved column.
"""
spec, _, graph, compiled = _nz_composed()
proofs, refusals = compose.measurement_proofs(spec)
Expand Down
Loading