Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b8b8cb97f | ||
|
|
8e589fee3f |
@@ -378,7 +378,11 @@ check("BUYING in a negative-price hour is still permitted - §17 forbids "
|
||||
"refusing to be paid to charge",
|
||||
act(NEG_PLAN, NEG.index(min(NEG)), thresh=_shipped,
|
||||
unc=NO_WEATHER).action == CHARGE)
|
||||
# A zero price is the boundary and it is NOT negative.
|
||||
# A zero price is the boundary and it is NOT negative. ⚠️ KEEP THIS PROBE. It
|
||||
# pins the boundary from ABOVE - it is what catches a guard written `<= 0` - and
|
||||
# the SAFETY-26 block below pins it from BELOW. Neither replaces the other, and
|
||||
# the whole defect that card exists to close is that only one of the two sides
|
||||
# was ever stated.
|
||||
ZERO = list(POS)
|
||||
ZERO[DEAR_H] = 0.0
|
||||
check("a sell hour priced at exactly ZERO is not a negative one, so the guard "
|
||||
@@ -387,6 +391,119 @@ check("a sell hour priced at exactly ZERO is not a negative one, so the guard "
|
||||
act(plan_of(ZERO), DEAR_H, thresh=_shipped, unc=NO_WEATHER).action
|
||||
== DISCHARGE)
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# SAFETY-26. THE OTHER SIDE OF THE SAME BOUNDARY, AND WHY IT HAD TO BE ADDED.
|
||||
#
|
||||
# OPT-04's reviewer mutated `if price < 0` to `if price < -0.001` and EVERY
|
||||
# SUITE STAYED GREEN, with a -0.0005 EUR/kWh sell hour discharging. The shipped
|
||||
# code was right the whole time; the CHECKS could not tell correct from
|
||||
# nearly-correct, because the only probe near the boundary sat at 0.0000. A
|
||||
# probe above the line catches `<=`. It says nothing at all about a tolerance
|
||||
# invented below the line, and a tolerance below the line IS §17's regression
|
||||
# case walking through with the suite passing.
|
||||
#
|
||||
# ⚠️ AND -0.0005 IS NOT AN EXOTIC INPUT. Belgian day-ahead prints small negative
|
||||
# hours regularly; an all-in price a twentieth of a cent below zero is an
|
||||
# ordinary day, not a fuzz value. That is what makes this a live regression
|
||||
# rather than a theoretical one.
|
||||
#
|
||||
# ⚠️ SO THE PROBE IS A SWEEP, NOT A POINT, and the sweep is the discriminating
|
||||
# part. One probe at -0.0005 closes `-0.001` and nothing else; a mutation
|
||||
# reaching for `-0.01` would survive it, which is the identical defect one
|
||||
# decimal place further out. The sweep spans four orders of magnitude down to
|
||||
# the last place the canonical six-decimal format can even express, so there is
|
||||
# no tolerance a guard can invent that some member of it does not straddle.
|
||||
# `_ROUNDING` asserts the smallest member SURVIVES the format rather than being
|
||||
# rounded to zero on the way in - a probe that silently became 0.0 would be the
|
||||
# `0.0000` check again, wearing a different name.
|
||||
TINY_NEG = (-0.05, -0.005, -0.0005, -0.00005, -0.000001)
|
||||
_ROUNDING = [round(p, 6) for p in TINY_NEG]
|
||||
check(f"every probe price survives the canonical 6-decimal rounding as a "
|
||||
f"STRICTLY negative number {_ROUNDING} - one that rounded to 0.0 would "
|
||||
f"be the check above again under another name",
|
||||
all(r < 0 for r in _ROUNDING) and len(set(_ROUNDING)) == len(TINY_NEG))
|
||||
check("...and they span four orders of magnitude, so no single invented "
|
||||
"tolerance can sit below all of them and above none",
|
||||
abs(min(TINY_NEG)) / abs(max(TINY_NEG)) > 10000)
|
||||
|
||||
|
||||
def tiny_neg_plan(price):
|
||||
"""The POS day with its DEAREST hour set to a small negative print."""
|
||||
day = list(POS)
|
||||
day[DEAR_H] = price
|
||||
return plan_of(day), day
|
||||
|
||||
|
||||
# The stimulus has to survive the rungs ABOVE §17 or it grades nothing: the
|
||||
# hour must still be the dearest, and the spread must still clear the widened
|
||||
# threshold, at every probe. Asserted first, for all of them, as its own check.
|
||||
_wellformed = [
|
||||
(p, day.index(max(day)) == DEAR_H,
|
||||
round(max(day) - min(day), 6) > required_spread(_shipped, NO_WEATHER))
|
||||
for p, (_pl, day) in ((p, tiny_neg_plan(p)) for p in TINY_NEG)]
|
||||
check(f"at every probe the negative hour is still the DEAREST one and the "
|
||||
f"spread still clears the threshold, so §17 is the only rung that can "
|
||||
f"refuse {_wellformed}",
|
||||
all(dearest and clears for _p, dearest, clears in _wellformed))
|
||||
_swept = [(p, act(tiny_neg_plan(p)[0], DEAR_H, thresh=_shipped,
|
||||
unc=NO_WEATHER).action) for p in TINY_NEG]
|
||||
check(f"SAFETY-26: a sell hour that is negative BY ANY MARGIN AT ALL refuses - "
|
||||
f"down to -0.000001 EUR/kWh, so the guard is `< 0` and not `< -epsilon` "
|
||||
f"for any epsilon {_swept}",
|
||||
all(a is None for _p, a in _swept))
|
||||
check("...and each refusal is §17's, not some other rung that happens to fire "
|
||||
"at small numbers",
|
||||
all("§17" in act(tiny_neg_plan(p)[0], DEAR_H, thresh=_shipped,
|
||||
unc=NO_WEATHER).reason for p in TINY_NEG))
|
||||
# The mirror image, one place above zero. Without this the sweep above would
|
||||
# pass for a guard that refused EVERYTHING, which grades §17 by nothing.
|
||||
_tiny_pos = [(-p, act(tiny_neg_plan(-p)[0], DEAR_H, thresh=_shipped,
|
||||
unc=NO_WEATHER).action) for p in TINY_NEG]
|
||||
check(f"...while THE SAME MAGNITUDES with the sign flipped all DISCHARGE, so "
|
||||
f"the sweep is a boundary at exactly zero rather than a refusal of small "
|
||||
f"numbers - which a guard that refused everything would also have passed "
|
||||
f"{_tiny_pos}",
|
||||
all(a == DISCHARGE for _p, a in _tiny_pos))
|
||||
|
||||
# SAFETY-26 AC4 - THE LOOK AT THE OTHER BOUNDARIES, NAMED RATHER THAN FIXED.
|
||||
#
|
||||
# The card asks whether the same one-sided pinning exists elsewhere. Every
|
||||
# price/quantity boundary in arbitrage.py and static_tariff.py was given the
|
||||
# tolerance mutation that survived on `price < 0`, in both directions where
|
||||
# both exist, under `-B` with a recursive purge. Two survived, BOTH in
|
||||
# static_tariff.py - TARIFF-03's file, and deliberately NOT touched here:
|
||||
#
|
||||
# D `spread == 0.0` (the flat-tariff rung) -> `spread <= 0.001` SURVIVES.
|
||||
# Measured, not reasoned: with it loosened, a series priced 0.20000 /
|
||||
# 0.20050 - a REAL 0.0005 spread - flips from ALLOWED to REFUSED at
|
||||
# min_spread_eur_kwh=0.0, which `arbitrage()`'s own docstring documents as
|
||||
# a supported value ("pass 0.0 to mean any positive spread, explicitly").
|
||||
# At a shipped threshold the OUTCOME is unchanged, but the operator's
|
||||
# reason becomes the false sentence "the tariff is flat - every one of the
|
||||
# 2 firm hours costs 0.2000 EUR/kWh" about a tariff that is not flat.
|
||||
# Same shape as §17's: the rung is only ever probed with an EXACTLY flat
|
||||
# series, so nothing pins the tiny-but-real side.
|
||||
#
|
||||
# F `len(firm) < 2` (§8.3's firm-pair rung) -> `< 1` SURVIVES, at +0 BYTES,
|
||||
# which is the delta TEST-15 names as the risky one. It is not live on its
|
||||
# own: a single firm hour has a spread of 0.0, so the flat rung refuses it
|
||||
# anyway - but the reason degrades from §8.3's honest "only 1 of 3 hours
|
||||
# carry a published price" to "the tariff is flat". THE FINDING THAT
|
||||
# MATTERS IS THE INTERACTION: D and F MASK EACH OTHER. Neither is
|
||||
# individually live because the other catches it, which is exactly the
|
||||
# arrangement in which a later edit to either one opens a hole nothing
|
||||
# reports.
|
||||
#
|
||||
# Everything else in arbitrage.py is pinned on BOTH sides and was verified so:
|
||||
# `price < 0` (this card), `soc <= floor_pct` and `soc >= ceiling_pct` are each
|
||||
# red under a +5 and a -5 tolerance; static_tariff's `spread < need` is red
|
||||
# under +/-0.001 and additionally bisected to 1e-6 in section 1; `cheap_now`'s
|
||||
# `price < mean` is red under a +0.001 tolerance in test_static_tariff.py.
|
||||
#
|
||||
# Not fixed here on purpose. D and F are TARIFF-03's rungs, they need probes in
|
||||
# test_static_tariff.py rather than in this file, and the card is explicit that
|
||||
# its fourth criterion is a look and not a refactor.
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
print()
|
||||
|
||||
Reference in New Issue
Block a user