FA-92526 / HVAC thermostat control / Open access
Economizer enables right at the high limit with no differential · case 01
The economizer toggles on and off every sample when outdoor temperature hovers at the high limit.
ROOT CAUSE
The enable test uses outdoor <= high_limit instead of high_limit - 2.0 F.
VERIFIED REPAIR
Enable only at or below high_limit - 2.0 F; disable above high_limit.
Unsuccessful approach: Halving the differential still leaves too narrow a band.
Case contract
Rooftop economizer per sample; samples are [outdoor, return_air, cooling_call, mixed_air, occupied] in tenths F. Economizing is enabled when outdoor <= high_limit - 2.0 F and outdoor < return - diff, and disabled when outdoor > high_limit or outdoor >= return. Unoccupied: damper 0%. Occupied, enabled and cooling: if mixed air < mat_low the damper steps down 10% but not below min_pos, otherwise it steps up 20% from at least min_pos, capped at 100%. Otherwise the damper sits at min_pos. Return [enabled, damper%] per sample.
Why this case matters
Residential and light-commercial thermostats make these decisions sample by sample; each defect changes equipment calls in a way that shows up as short cycling, comfort complaints or equipment stress.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(settings, samples):
enabled = False
pos = 0
out = []
for outdoor, ret, cooling, mixed, occupied in samples:
if enabled and (outdoor > settings['high_limit'] or outdoor >= ret):
enabled = False
elif not enabled and outdoor <= settings['high_limit'] and outdoor < ret - settings['diff']:
enabled = True
if not occupied:
pos = 0
elif enabled and cooling:
if mixed < settings['mat_low']:
pos = max(settings['min_pos'], pos - 10)
else:
pos = min(100, max(settings['min_pos'], pos) + 20)
else:
pos = settings['min_pos']
out.append([enabled, pos])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[739, 710, True, 528, True],
[721, 715, True, 537, True],
[696, 760, False, 541, True],
[680, 735, True, 575, True],
[646, 726, True, 550, True],
[633, 731, False, 494, True],
[598, 704, True, 565, True],
[608, 716, False, 614, True]]],
[[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[648, 701, True, 604, True],
[640, 708, True, 484, True],
[671, 716, True, 536, True],
[649, 721, True, 616, False],
[640, 749, True, 619, True],
[651, 751, True, 599, True],
[669, 718, False, 504, True],
[638, 704, False, 542, True]]],
[[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]]],
['control: scenario 2',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[460, 757, True, 619, True],
[462, 720, False, 618, True],
[502, 734, True, 534, True],
[488, 707, False, 516, True],
[450, 735, False, 496, True],
[472, 754, True, 606, True],
[477, 741, False, 575, True],
[452, 710, True, 531, True],
[431, 759, False, 576, True]]],
[[True, 40],
[True, 20],
[True, 40],
[True, 20],
[True, 20],
[True, 40],
[True, 20],
[True, 40],
[True, 20]]]],
[['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 500},
[[637, 708, True, 552, True],
[645, 735, True, 591, True],
[634, 703, True, 564, True],
[622, 710, True, 543, True],
[634, 716, True, 534, False],
[647, 753, True, 529, True],
[619, 706, True, 541, True],
[596, 740, True, 617, True],
[590, 749, True, 524, True],
[592, 739, True, 566, False],
[581, 721, True, 554, True],
[593, 737, False, 599, True],
[572, 760, True, 524, True],
[560, 714, True, 512, True]]],
[[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 0],
[True, 50],
[True, 70],
[True, 90],
[True, 100],
[True, 0],
[True, 50],
[True, 30],
[True, 50],
[True, 70]]],
['regression: scenario 10',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[641, 705, True, 484, True],
[633, 750, True, 549, True],
[611, 743, False, 559, True],
[596, 715, True, 522, True],
[625, 713, True, 564, True],
[641, 703, True, 560, True],
[618, 717, True, 585, True],
[632, 711, False, 552, True],
[608, 758, True, 602, True]]],
[[False, 30],
[False, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 30],
[True, 50]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 0, 'min_pos': 30, 'mat_low': 500},
[[754, 754, False, 483, False],
[716, 749, True, 499, True],
[699, 701, False, 573, True],
[694, 750, True, 534, False],
[704, 716, False, 517, True],
[708, 701, False, 567, True],
[743, 735, True, 609, True],
[775, 747, False, 554, True],
[743, 731, False, 583, False],
[764, 708, False, 507, True],
[795, 743, False, 528, True],
[770, 706, True, 501, True],
[764, 752, False, 578, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]]],
[['regression: scenario 6',
[{'high_limit': 700, 'diff': 0, 'min_pos': 20, 'mat_low': 530},
[[763, 718, True, 585, True],
[759, 718, True, 579, True],
[756, 741, True, 590, True],
[742, 724, True, 569, True],
[765, 717, True, 612, True],
[763, 727, False, 522, True],
[735, 744, True, 480, True],
[715, 733, True, 519, True],
[685, 712, True, 608, True],
[656, 730, True, 484, True],
[659, 758, False, 561, True],
[625, 726, True, 500, True],
[640, 750, True, 506, True],
[640, 711, False, 549, True]]],
[[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20]]],
['regression: scenario 16',
[{'high_limit': 650, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[672, 743, True, 486, True],
[641, 721, True, 613, True],
[636, 726, True, 567, True],
[600, 723, False, 611, True],
[561, 754, True, 587, True],
[567, 709, True, 591, True],
[546, 754, False, 499, False],
[561, 729, False, 540, True]]],
[[False, 30], [False, 30], [False, 30], [True, 30], [True, 50], [True, 70], [True, 0], [True, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['regression: scenario 1',
[{'high_limit': 650, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[678, 721, True, 495, True],
[646, 726, True, 616, True],
[669, 714, True, 600, True],
[630, 728, True, 542, True],
[626, 751, True, 530, False],
[638, 723, False, 607, True],
[654, 706, True, 557, True],
[625, 704, False, 618, True]]],
[[False, 20], [False, 20], [False, 20], [True, 40], [True, 0], [True, 20], [False, 20], [True, 20]]]],
[['regression: scenario 17',
[{'high_limit': 700, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[669, 750, False, 557, False],
[688, 701, True, 577, True],
[671, 744, False, 534, True],
[651, 716, False, 567, True],
[680, 702, False, 569, True],
[640, 740, True, 559, True],
[657, 721, True, 556, True],
[650, 721, True, 516, True],
[682, 741, True, 511, True],
[700, 718, True, 582, True],
[730, 724, True, 482, True],
[724, 715, False, 517, False],
[685, 716, False, 568, True],
[683, 729, True, 588, True]]],
[[True, 0],
[True, 35],
[True, 15],
[True, 15],
[True, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 35],
[True, 55],
[False, 15],
[False, 0],
[False, 15],
[False, 15]]],
['regression: scenario 48',
[{'high_limit': 750, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[746, 701, True, 531, True],
[717, 709, True, 611, True],
[737, 722, True, 489, True],
[744, 712, True, 498, True],
[731, 701, True, 552, True],
[701, 730, True, 531, True],
[738, 741, True, 619, True],
[742, 738, True, 561, True],
[771, 757, True, 582, True],
[785, 738, False, 566, True],
[760, 739, True, 489, True],
[748, 757, True, 534, True],
[734, 758, True, 568, True],
[750, 722, False, 549, True]]],
[[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 70],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[505, 738, False, 612, True],
[535, 706, True, 498, True],
[549, 734, False, 604, True],
[558, 729, True, 521, True],
[540, 724, True, 571, True],
[530, 749, True, 592, True],
[507, 715, True, 533, False],
[468, 703, False, 516, True],
[457, 715, False, 563, True],
[444, 708, False, 513, True]]],
[[True, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 0],
[True, 30],
[True, 30],
[True, 30]]]],
[['regression: scenario 26',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[601, 732, False, 620, True],
[635, 736, True, 571, True],
[666, 716, False, 556, True],
[646, 714, False, 491, True],
[625, 747, True, 525, True],
[631, 760, True, 584, True],
[640, 723, True, 496, True],
[600, 744, False, 537, True],
[579, 717, True, 514, True],
[611, 736, False, 614, True],
[639, 745, False, 496, True],
[602, 732, False, 490, False]]],
[[True, 15],
[True, 35],
[False, 15],
[False, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 15],
[True, 35],
[True, 15],
[True, 15],
[True, 0]]],
['regression: scenario 36',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[679, 739, True, 601, True],
[689, 742, True, 510, True],
[652, 741, True, 598, True],
[631, 734, True, 539, True],
[671, 731, False, 552, False],
[637, 737, False, 532, True],
[675, 744, False, 599, True],
[666, 748, True, 508, True],
[661, 721, True, 577, True]]],
[[False, 15],
[False, 15],
[False, 15],
[False, 15],
[False, 0],
[False, 15],
[False, 15],
[False, 15],
[False, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[684, 714, False, 579, True],
[715, 719, False, 481, True],
[737, 759, True, 615, False],
[715, 756, True, 609, False],
[743, 742, True, 491, True],
[776, 713, False, 491, True],
[798, 748, False, 566, True],
[800, 751, True, 547, True],
[800, 736, True, 486, True],
[760, 727, False, 570, True]]],
[[True, 20],
[True, 20],
[True, 0],
[True, 0],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: enable band edges | [[True, 40], [True, 60], [True, 80], [True, 100], [False, 20]] | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | Failed |
| regression: scenario 4 | [[False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15], [True, 35], [True, 15]] | [[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]] | Failed |
| control: economizer cycle | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | Passed |
| control: cold mixed air | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | Passed |
| control: night cooling call | [[True, 0], [True, 40]] | [[True, 0], [True, 40]] | Passed |
| control: near return disable | [[True, 35], [True, 55], [True, 75], [False, 15]] | [[True, 35], [True, 55], [True, 75], [False, 15]] | Passed |
| control: scenario 1 | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | Passed |
| control: scenario 2 | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | Passed |
SHA-256 / 2f08e64f5b28ab6ebf727cdffc650ab83ad79b6f21ef639ca81da0da19e94bf6
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(settings, samples):
enabled = False
pos = 0
out = []
for outdoor, ret, cooling, mixed, occupied in samples:
if enabled and (outdoor > settings['high_limit'] or outdoor >= ret):
enabled = False
elif not enabled and outdoor <= settings['high_limit'] - 10 and outdoor < ret - settings['diff']:
enabled = True
if not occupied:
pos = 0
elif enabled and cooling:
if mixed < settings['mat_low']:
pos = max(settings['min_pos'], pos - 10)
else:
pos = min(100, max(settings['min_pos'], pos) + 20)
else:
pos = settings['min_pos']
out.append([enabled, pos])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[739, 710, True, 528, True],
[721, 715, True, 537, True],
[696, 760, False, 541, True],
[680, 735, True, 575, True],
[646, 726, True, 550, True],
[633, 731, False, 494, True],
[598, 704, True, 565, True],
[608, 716, False, 614, True]]],
[[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[648, 701, True, 604, True],
[640, 708, True, 484, True],
[671, 716, True, 536, True],
[649, 721, True, 616, False],
[640, 749, True, 619, True],
[651, 751, True, 599, True],
[669, 718, False, 504, True],
[638, 704, False, 542, True]]],
[[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]]],
['control: scenario 2',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[460, 757, True, 619, True],
[462, 720, False, 618, True],
[502, 734, True, 534, True],
[488, 707, False, 516, True],
[450, 735, False, 496, True],
[472, 754, True, 606, True],
[477, 741, False, 575, True],
[452, 710, True, 531, True],
[431, 759, False, 576, True]]],
[[True, 40],
[True, 20],
[True, 40],
[True, 20],
[True, 20],
[True, 40],
[True, 20],
[True, 40],
[True, 20]]]],
[['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 500},
[[637, 708, True, 552, True],
[645, 735, True, 591, True],
[634, 703, True, 564, True],
[622, 710, True, 543, True],
[634, 716, True, 534, False],
[647, 753, True, 529, True],
[619, 706, True, 541, True],
[596, 740, True, 617, True],
[590, 749, True, 524, True],
[592, 739, True, 566, False],
[581, 721, True, 554, True],
[593, 737, False, 599, True],
[572, 760, True, 524, True],
[560, 714, True, 512, True]]],
[[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 0],
[True, 50],
[True, 70],
[True, 90],
[True, 100],
[True, 0],
[True, 50],
[True, 30],
[True, 50],
[True, 70]]],
['regression: scenario 10',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[641, 705, True, 484, True],
[633, 750, True, 549, True],
[611, 743, False, 559, True],
[596, 715, True, 522, True],
[625, 713, True, 564, True],
[641, 703, True, 560, True],
[618, 717, True, 585, True],
[632, 711, False, 552, True],
[608, 758, True, 602, True]]],
[[False, 30],
[False, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 30],
[True, 50]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 0, 'min_pos': 30, 'mat_low': 500},
[[754, 754, False, 483, False],
[716, 749, True, 499, True],
[699, 701, False, 573, True],
[694, 750, True, 534, False],
[704, 716, False, 517, True],
[708, 701, False, 567, True],
[743, 735, True, 609, True],
[775, 747, False, 554, True],
[743, 731, False, 583, False],
[764, 708, False, 507, True],
[795, 743, False, 528, True],
[770, 706, True, 501, True],
[764, 752, False, 578, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]]],
[['regression: scenario 6',
[{'high_limit': 700, 'diff': 0, 'min_pos': 20, 'mat_low': 530},
[[763, 718, True, 585, True],
[759, 718, True, 579, True],
[756, 741, True, 590, True],
[742, 724, True, 569, True],
[765, 717, True, 612, True],
[763, 727, False, 522, True],
[735, 744, True, 480, True],
[715, 733, True, 519, True],
[685, 712, True, 608, True],
[656, 730, True, 484, True],
[659, 758, False, 561, True],
[625, 726, True, 500, True],
[640, 750, True, 506, True],
[640, 711, False, 549, True]]],
[[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20]]],
['regression: scenario 16',
[{'high_limit': 650, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[672, 743, True, 486, True],
[641, 721, True, 613, True],
[636, 726, True, 567, True],
[600, 723, False, 611, True],
[561, 754, True, 587, True],
[567, 709, True, 591, True],
[546, 754, False, 499, False],
[561, 729, False, 540, True]]],
[[False, 30], [False, 30], [False, 30], [True, 30], [True, 50], [True, 70], [True, 0], [True, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['regression: scenario 1',
[{'high_limit': 650, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[678, 721, True, 495, True],
[646, 726, True, 616, True],
[669, 714, True, 600, True],
[630, 728, True, 542, True],
[626, 751, True, 530, False],
[638, 723, False, 607, True],
[654, 706, True, 557, True],
[625, 704, False, 618, True]]],
[[False, 20], [False, 20], [False, 20], [True, 40], [True, 0], [True, 20], [False, 20], [True, 20]]]],
[['regression: scenario 17',
[{'high_limit': 700, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[669, 750, False, 557, False],
[688, 701, True, 577, True],
[671, 744, False, 534, True],
[651, 716, False, 567, True],
[680, 702, False, 569, True],
[640, 740, True, 559, True],
[657, 721, True, 556, True],
[650, 721, True, 516, True],
[682, 741, True, 511, True],
[700, 718, True, 582, True],
[730, 724, True, 482, True],
[724, 715, False, 517, False],
[685, 716, False, 568, True],
[683, 729, True, 588, True]]],
[[True, 0],
[True, 35],
[True, 15],
[True, 15],
[True, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 35],
[True, 55],
[False, 15],
[False, 0],
[False, 15],
[False, 15]]],
['regression: scenario 48',
[{'high_limit': 750, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[746, 701, True, 531, True],
[717, 709, True, 611, True],
[737, 722, True, 489, True],
[744, 712, True, 498, True],
[731, 701, True, 552, True],
[701, 730, True, 531, True],
[738, 741, True, 619, True],
[742, 738, True, 561, True],
[771, 757, True, 582, True],
[785, 738, False, 566, True],
[760, 739, True, 489, True],
[748, 757, True, 534, True],
[734, 758, True, 568, True],
[750, 722, False, 549, True]]],
[[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 70],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[505, 738, False, 612, True],
[535, 706, True, 498, True],
[549, 734, False, 604, True],
[558, 729, True, 521, True],
[540, 724, True, 571, True],
[530, 749, True, 592, True],
[507, 715, True, 533, False],
[468, 703, False, 516, True],
[457, 715, False, 563, True],
[444, 708, False, 513, True]]],
[[True, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 0],
[True, 30],
[True, 30],
[True, 30]]]],
[['regression: scenario 26',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[601, 732, False, 620, True],
[635, 736, True, 571, True],
[666, 716, False, 556, True],
[646, 714, False, 491, True],
[625, 747, True, 525, True],
[631, 760, True, 584, True],
[640, 723, True, 496, True],
[600, 744, False, 537, True],
[579, 717, True, 514, True],
[611, 736, False, 614, True],
[639, 745, False, 496, True],
[602, 732, False, 490, False]]],
[[True, 15],
[True, 35],
[False, 15],
[False, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 15],
[True, 35],
[True, 15],
[True, 15],
[True, 0]]],
['regression: scenario 36',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[679, 739, True, 601, True],
[689, 742, True, 510, True],
[652, 741, True, 598, True],
[631, 734, True, 539, True],
[671, 731, False, 552, False],
[637, 737, False, 532, True],
[675, 744, False, 599, True],
[666, 748, True, 508, True],
[661, 721, True, 577, True]]],
[[False, 15],
[False, 15],
[False, 15],
[False, 15],
[False, 0],
[False, 15],
[False, 15],
[False, 15],
[False, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[684, 714, False, 579, True],
[715, 719, False, 481, True],
[737, 759, True, 615, False],
[715, 756, True, 609, False],
[743, 742, True, 491, True],
[776, 713, False, 491, True],
[798, 748, False, 566, True],
[800, 751, True, 547, True],
[800, 736, True, 486, True],
[760, 727, False, 570, True]]],
[[True, 20],
[True, 20],
[True, 0],
[True, 0],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: enable band edges | [[True, 40], [True, 60], [True, 80], [True, 100], [False, 20]] | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | Failed |
| regression: scenario 4 | [[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 15], [True, 35], [True, 15]] | [[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]] | Failed |
| control: economizer cycle | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | Passed |
| control: cold mixed air | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | Passed |
| control: night cooling call | [[True, 0], [True, 40]] | [[True, 0], [True, 40]] | Passed |
| control: near return disable | [[True, 35], [True, 55], [True, 75], [False, 15]] | [[True, 35], [True, 55], [True, 75], [False, 15]] | Passed |
| control: scenario 1 | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | Passed |
| control: scenario 2 | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | Passed |
SHA-256 / db9305bf3a3b4556f0a81fdbb214cec3c5cbb46d10705845b849da4fb04119a3
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(settings, samples):
enabled = False
pos = 0
out = []
for outdoor, ret, cooling, mixed, occupied in samples:
if enabled and (outdoor > settings['high_limit'] or outdoor >= ret):
enabled = False
elif not enabled and outdoor <= settings['high_limit'] - 20 and outdoor < ret - settings['diff']:
enabled = True
if not occupied:
pos = 0
elif enabled and cooling:
if mixed < settings['mat_low']:
pos = max(settings['min_pos'], pos - 10)
else:
pos = min(100, max(settings['min_pos'], pos) + 20)
else:
pos = settings['min_pos']
out.append([enabled, pos])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[739, 710, True, 528, True],
[721, 715, True, 537, True],
[696, 760, False, 541, True],
[680, 735, True, 575, True],
[646, 726, True, 550, True],
[633, 731, False, 494, True],
[598, 704, True, 565, True],
[608, 716, False, 614, True]]],
[[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[648, 701, True, 604, True],
[640, 708, True, 484, True],
[671, 716, True, 536, True],
[649, 721, True, 616, False],
[640, 749, True, 619, True],
[651, 751, True, 599, True],
[669, 718, False, 504, True],
[638, 704, False, 542, True]]],
[[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]]],
['control: scenario 2',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[460, 757, True, 619, True],
[462, 720, False, 618, True],
[502, 734, True, 534, True],
[488, 707, False, 516, True],
[450, 735, False, 496, True],
[472, 754, True, 606, True],
[477, 741, False, 575, True],
[452, 710, True, 531, True],
[431, 759, False, 576, True]]],
[[True, 40],
[True, 20],
[True, 40],
[True, 20],
[True, 20],
[True, 40],
[True, 20],
[True, 40],
[True, 20]]]],
[['regression: scenario 4',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 500},
[[637, 708, True, 552, True],
[645, 735, True, 591, True],
[634, 703, True, 564, True],
[622, 710, True, 543, True],
[634, 716, True, 534, False],
[647, 753, True, 529, True],
[619, 706, True, 541, True],
[596, 740, True, 617, True],
[590, 749, True, 524, True],
[592, 739, True, 566, False],
[581, 721, True, 554, True],
[593, 737, False, 599, True],
[572, 760, True, 524, True],
[560, 714, True, 512, True]]],
[[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 0],
[True, 50],
[True, 70],
[True, 90],
[True, 100],
[True, 0],
[True, 50],
[True, 30],
[True, 50],
[True, 70]]],
['regression: scenario 10',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[641, 705, True, 484, True],
[633, 750, True, 549, True],
[611, 743, False, 559, True],
[596, 715, True, 522, True],
[625, 713, True, 564, True],
[641, 703, True, 560, True],
[618, 717, True, 585, True],
[632, 711, False, 552, True],
[608, 758, True, 602, True]]],
[[False, 30],
[False, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 30],
[True, 50]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 0, 'min_pos': 30, 'mat_low': 500},
[[754, 754, False, 483, False],
[716, 749, True, 499, True],
[699, 701, False, 573, True],
[694, 750, True, 534, False],
[704, 716, False, 517, True],
[708, 701, False, 567, True],
[743, 735, True, 609, True],
[775, 747, False, 554, True],
[743, 731, False, 583, False],
[764, 708, False, 507, True],
[795, 743, False, 528, True],
[770, 706, True, 501, True],
[764, 752, False, 578, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]]],
[['regression: scenario 6',
[{'high_limit': 700, 'diff': 0, 'min_pos': 20, 'mat_low': 530},
[[763, 718, True, 585, True],
[759, 718, True, 579, True],
[756, 741, True, 590, True],
[742, 724, True, 569, True],
[765, 717, True, 612, True],
[763, 727, False, 522, True],
[735, 744, True, 480, True],
[715, 733, True, 519, True],
[685, 712, True, 608, True],
[656, 730, True, 484, True],
[659, 758, False, 561, True],
[625, 726, True, 500, True],
[640, 750, True, 506, True],
[640, 711, False, 549, True]]],
[[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20]]],
['regression: scenario 16',
[{'high_limit': 650, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[672, 743, True, 486, True],
[641, 721, True, 613, True],
[636, 726, True, 567, True],
[600, 723, False, 611, True],
[561, 754, True, 587, True],
[567, 709, True, 591, True],
[546, 754, False, 499, False],
[561, 729, False, 540, True]]],
[[False, 30], [False, 30], [False, 30], [True, 30], [True, 50], [True, 70], [True, 0], [True, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['regression: scenario 1',
[{'high_limit': 650, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[678, 721, True, 495, True],
[646, 726, True, 616, True],
[669, 714, True, 600, True],
[630, 728, True, 542, True],
[626, 751, True, 530, False],
[638, 723, False, 607, True],
[654, 706, True, 557, True],
[625, 704, False, 618, True]]],
[[False, 20], [False, 20], [False, 20], [True, 40], [True, 0], [True, 20], [False, 20], [True, 20]]]],
[['regression: scenario 17',
[{'high_limit': 700, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[669, 750, False, 557, False],
[688, 701, True, 577, True],
[671, 744, False, 534, True],
[651, 716, False, 567, True],
[680, 702, False, 569, True],
[640, 740, True, 559, True],
[657, 721, True, 556, True],
[650, 721, True, 516, True],
[682, 741, True, 511, True],
[700, 718, True, 582, True],
[730, 724, True, 482, True],
[724, 715, False, 517, False],
[685, 716, False, 568, True],
[683, 729, True, 588, True]]],
[[True, 0],
[True, 35],
[True, 15],
[True, 15],
[True, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 35],
[True, 55],
[False, 15],
[False, 0],
[False, 15],
[False, 15]]],
['regression: scenario 48',
[{'high_limit': 750, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[746, 701, True, 531, True],
[717, 709, True, 611, True],
[737, 722, True, 489, True],
[744, 712, True, 498, True],
[731, 701, True, 552, True],
[701, 730, True, 531, True],
[738, 741, True, 619, True],
[742, 738, True, 561, True],
[771, 757, True, 582, True],
[785, 738, False, 566, True],
[760, 739, True, 489, True],
[748, 757, True, 534, True],
[734, 758, True, 568, True],
[750, 722, False, 549, True]]],
[[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 70],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[505, 738, False, 612, True],
[535, 706, True, 498, True],
[549, 734, False, 604, True],
[558, 729, True, 521, True],
[540, 724, True, 571, True],
[530, 749, True, 592, True],
[507, 715, True, 533, False],
[468, 703, False, 516, True],
[457, 715, False, 563, True],
[444, 708, False, 513, True]]],
[[True, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 0],
[True, 30],
[True, 30],
[True, 30]]]],
[['regression: scenario 26',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[601, 732, False, 620, True],
[635, 736, True, 571, True],
[666, 716, False, 556, True],
[646, 714, False, 491, True],
[625, 747, True, 525, True],
[631, 760, True, 584, True],
[640, 723, True, 496, True],
[600, 744, False, 537, True],
[579, 717, True, 514, True],
[611, 736, False, 614, True],
[639, 745, False, 496, True],
[602, 732, False, 490, False]]],
[[True, 15],
[True, 35],
[False, 15],
[False, 15],
[True, 35],
[True, 55],
[True, 45],
[True, 15],
[True, 35],
[True, 15],
[True, 15],
[True, 0]]],
['regression: scenario 36',
[{'high_limit': 650, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[679, 739, True, 601, True],
[689, 742, True, 510, True],
[652, 741, True, 598, True],
[631, 734, True, 539, True],
[671, 731, False, 552, False],
[637, 737, False, 532, True],
[675, 744, False, 599, True],
[666, 748, True, 508, True],
[661, 721, True, 577, True]]],
[[False, 15],
[False, 15],
[False, 15],
[False, 15],
[False, 0],
[False, 15],
[False, 15],
[False, 15],
[False, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['regression: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[684, 714, False, 579, True],
[715, 719, False, 481, True],
[737, 759, True, 615, False],
[715, 756, True, 609, False],
[743, 742, True, 491, True],
[776, 713, False, 491, True],
[798, 748, False, 566, True],
[800, 751, True, 547, True],
[800, 736, True, 486, True],
[760, 727, False, 570, True]]],
[[True, 20],
[True, 20],
[True, 0],
[True, 0],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20]]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: enable band edges | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | Passed |
| regression: scenario 4 | [[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]] | [[False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [False, 15], [True, 35], [True, 15]] | Passed |
| control: economizer cycle | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | Passed |
| control: cold mixed air | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | Passed |
| control: night cooling call | [[True, 0], [True, 40]] | [[True, 0], [True, 40]] | Passed |
| control: near return disable | [[True, 35], [True, 55], [True, 75], [False, 15]] | [[True, 35], [True, 55], [True, 75], [False, 15]] | Passed |
| control: scenario 1 | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | Passed |
| control: scenario 2 | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | [[True, 40], [True, 20], [True, 40], [True, 20], [True, 20], [True, 40], [True, 20], [True, 40], [True, 20]] | Passed |
SHA-256 / 0e158c65b9a2769e35e65948eccdf6424faa60b54b3d02bca8f76eb2381bf91b
Verification & scope
A deterministic bounded teaching model of one thermostat or HVAC controller decision evaluated per sample. Temperatures are integer tenths of a degree Fahrenheit unless stated otherwise. The contract is a stipulated toy convention, not a claim of conformance to any manufacturer, ASHRAE guideline or code. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:46.506758+00:00.
Case digest / 8ac148e55bfc57a9584860d9c238cb3bb56bc8e2ad1e9ebe2c90ebdb77bf3d67