8e6ee31596
ci / test (push) Successful in 6m18s
History-free snapshot of the private development repository at cbe45c5 (2026-09-09). Code, docs and public catalogue only; no plant or job knowledge layers, no client or job names (tools/scrub.py clean). The intro deck is left out of this snapshot because its slide images could not be checked for job drawings.
885 lines
37 KiB
Python
885 lines
37 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""The reinforcing-suggestion matcher: set cover over opaque feature records.
|
|
|
|
Every panel here is invented. Marks are `PNL...`, which is the project's
|
|
placeholder convention -- no real panel mark, job or client name appears in
|
|
this file, including in the case rebuilt from the worked example, where the
|
|
three real marks are replaced by PNL501 (target) and PNL502 / PNL503 (donors).
|
|
|
|
Two kinds of test live here. Most use bare rectangles and the module's own
|
|
reference `rect_distance`, because the point of the seam is that the selection
|
|
logic can be exercised with no feature schema at all. A handful inject a
|
|
hand-written `covers` that returns canned numbers, because the ranking rules
|
|
are about the NUMBERS and it is clearer to state them directly than to derive
|
|
them from geometry.
|
|
"""
|
|
# pylint: disable=import-error,invalid-name,broad-except
|
|
import pytest
|
|
|
|
from vibe import reinforcematch as rm
|
|
|
|
|
|
# ---------------------------------------------------------------- fixtures
|
|
|
|
def canned(table):
|
|
"""A `covers` built from {(donor_feature, target_feature): distance}.
|
|
|
|
Anything not in the table is not covered. Feature records are plain
|
|
strings; the matcher never looks at them, which is the whole point.
|
|
"""
|
|
def covers(donor_feature, target_feature):
|
|
return table.get((donor_feature, target_feature))
|
|
return covers
|
|
|
|
|
|
# Rebuilt from the worked example, with invented marks. Rectangles are
|
|
# (x0, x1, y0, y1) in absolute feet from the start of the piece -- absolute,
|
|
# never a fraction of length, which is the thing the measurement settled.
|
|
# The three pieces are three different lengths and the big openings still land
|
|
# at the identical feet-from-start rectangle.
|
|
|
|
PNL501 = [ # target, 27.625 ft long, six features
|
|
(24.250, 27.625, 6.250, 11.500), # 0 right-end blockout
|
|
(6.500, 10.583, 0.354, 4.479), # 1 big opening
|
|
(14.344, 18.427, 0.354, 4.479), # 2 big opening
|
|
(0.000, 0.875, 1.125, 3.000), # 3 left notch
|
|
(24.042, 25.875, 1.125, 3.000), # 4 small right notch
|
|
(0.000, 0.667, 11.292, 11.500), # 5 left top-corner recess
|
|
]
|
|
|
|
PNL502 = [ # donor, 24.917 ft: the two big openings
|
|
(6.500, 10.583, 0.354, 4.479),
|
|
(14.344, 18.427, 0.354, 4.479),
|
|
(0.000, 0.875, 1.125, 3.000),
|
|
(24.042, 24.917, 1.125, 3.000),
|
|
(0.000, 0.667, 11.292, 11.500),
|
|
(24.250, 24.917, 11.292, 11.500),
|
|
]
|
|
|
|
PNL503 = [ # donor, 27.208 ft: the blockout end
|
|
(24.250, 27.208, 6.917, 11.500),
|
|
(0.000, 0.875, 1.125, 3.000),
|
|
(24.042, 25.875, 1.125, 3.000),
|
|
(0.000, 0.667, 11.292, 11.500),
|
|
]
|
|
|
|
|
|
# ------------------------------------------------- the reference comparison
|
|
|
|
def test_rect_distance_is_zero_for_an_identical_rectangle():
|
|
r = (6.5, 10.583, 0.354, 4.479)
|
|
assert rm.rect_distance(r, r) == 0.0
|
|
|
|
|
|
def test_rect_distance_grows_with_the_corner_offsets():
|
|
target = (24.250, 27.625, 6.250, 11.500)
|
|
donor = (24.250, 27.208, 6.917, 11.500)
|
|
got = rm.rect_distance(donor, target)
|
|
assert got == pytest.approx(0.417 + 0.667, abs=0.001)
|
|
|
|
|
|
def test_rect_distance_refuses_a_feature_in_a_different_band():
|
|
"""A single-axis check calls these the same feature. They are not.
|
|
|
|
Both sit near the right end of the piece and share x-range, but one is a
|
|
full-height blockout and the other a shallow top-corner recess.
|
|
"""
|
|
blockout = (24.250, 27.625, 6.250, 11.500)
|
|
recess = (24.250, 24.917, 11.292, 11.500)
|
|
assert rm.rect_distance(recess, blockout) is None
|
|
|
|
|
|
def test_rect_distance_is_not_proportional():
|
|
"""Same absolute rectangle on two panels of different length is exact.
|
|
|
|
Nothing here divides by a length, so there is nothing for the panels'
|
|
different lengths to change.
|
|
"""
|
|
assert rm.rect_distance(PNL502[0], PNL501[1]) == 0.0
|
|
assert rm.rect_distance(PNL502[1], PNL501[2]) == 0.0
|
|
|
|
|
|
def test_rect_distance_returns_none_for_unreadable_input():
|
|
assert rm.rect_distance(None, (0, 1, 0, 1)) is None
|
|
assert rm.rect_distance(('a', 'b', 'c', 'd'), (0, 1, 0, 1)) is None
|
|
|
|
|
|
# --------------------------------------------------- could not judge vs none
|
|
|
|
def test_no_target_features_is_could_not_judge():
|
|
"""None, not an empty result. There is nothing to cover, so no judgement
|
|
was made -- and that must not read like 'searched, found nothing'."""
|
|
assert rm.suggest([], [('PNL502', PNL502)], rm.rect_distance) is None
|
|
assert rm.suggest(None, [('PNL502', PNL502)], rm.rect_distance) is None
|
|
|
|
|
|
def test_no_donors_reads_differently_from_no_coverage():
|
|
nothing_offered = rm.suggest(PNL501, [], rm.rect_distance)
|
|
assert nothing_offered.status == rm.NO_DONORS
|
|
assert nothing_offered.proposals == ()
|
|
assert nothing_offered.donor_count == 0
|
|
assert nothing_offered.donors_needed is None
|
|
assert 'nothing was judged' in nothing_offered.summary()
|
|
|
|
useless = [('PNL900', [(90.0, 91.0, 0.0, 1.0)])]
|
|
judged = rm.suggest(PNL501, useless, rm.rect_distance)
|
|
assert judged.status == rm.NO_COVERAGE
|
|
assert judged.proposals == ()
|
|
assert judged.donor_count == 1
|
|
assert judged.uncovered == (0, 1, 2, 3, 4, 5)
|
|
assert 'not one of them covers' in judged.summary()
|
|
|
|
assert nothing_offered.summary() != judged.summary()
|
|
|
|
|
|
def test_a_covers_that_blows_up_is_not_swallowed():
|
|
"""An exception in the comparison must not become 'covers nothing'."""
|
|
def boom(donor_feature, target_feature):
|
|
raise ZeroDivisionError('bad geometry')
|
|
with pytest.raises(ZeroDivisionError):
|
|
rm.suggest(PNL501, [('PNL502', PNL502)], boom)
|
|
|
|
|
|
def test_contract_violations_are_raised_not_absorbed():
|
|
with pytest.raises(ValueError):
|
|
rm.suggest(['t'], [('D', ['d'])], canned({('d', 't'): -1.0}))
|
|
with pytest.raises(ValueError):
|
|
rm.suggest(['t'], [('D', ['d'])], canned({('d', 't'): 'close'}))
|
|
with pytest.raises(ValueError):
|
|
rm.suggest(['t'], [('D', ['d'])], covers=None)
|
|
|
|
|
|
# ---------------------------------------------------------- single-donor cover
|
|
|
|
def test_one_donor_covering_everything_exactly():
|
|
target = ['a', 'b', 'c']
|
|
donors = [('PNL601', ['a', 'b', 'c'])]
|
|
table = dict(((f, f), 0.0) for f in target)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 1
|
|
assert got.best.donors == ('PNL601',)
|
|
assert got.best.is_exact
|
|
assert got.best.exact_count == 3
|
|
assert got.best.total_distance == 0.0
|
|
assert got.uncovered == ()
|
|
|
|
|
|
def test_a_single_exact_donor_outranks_a_single_loose_one():
|
|
target = ['a', 'b']
|
|
donors = [('PNL_LOOSE', ['x', 'y']), ('PNL_EXACT', ['a', 'b'])]
|
|
table = {('x', 'a'): 1.0, ('y', 'b'): 1.0,
|
|
('a', 'a'): 0.0, ('b', 'b'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.donors_needed == 1
|
|
assert got.cover_count == 2
|
|
assert [p.donors[0] for p in got.proposals] == ['PNL_EXACT', 'PNL_LOOSE']
|
|
|
|
|
|
# ------------------------------------------------------------ two-donor cover
|
|
|
|
def test_two_donors_are_needed_when_neither_alone_will_do():
|
|
target = ['a', 'b', 'c', 'd']
|
|
donors = [('PNL701', ['a', 'b']), ('PNL702', ['c', 'd'])]
|
|
table = {('a', 'a'): 0.0, ('b', 'b'): 0.0,
|
|
('c', 'c'): 0.0, ('d', 'd'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 2
|
|
assert got.best.donors == ('PNL701', 'PNL702')
|
|
|
|
|
|
def test_fewest_donors_wins_even_when_more_donors_would_be_more_exact():
|
|
"""Fewest donors is the objective; exactness only breaks ties.
|
|
|
|
PNL802+PNL803 would be exact on both features. PNL801 alone is loose on
|
|
both. One donor still wins -- that is the owner's stated objective and the
|
|
matcher does not get to relitigate it.
|
|
"""
|
|
target = ['a', 'b']
|
|
donors = [('PNL801', ['w']), ('PNL802', ['a']), ('PNL803', ['b'])]
|
|
table = {('w', 'a'): 3.0, ('w', 'b'): 3.0,
|
|
('a', 'a'): 0.0, ('b', 'b'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.donors_needed == 1
|
|
assert got.best.donors == ('PNL801',)
|
|
|
|
|
|
def test_overlap_between_donors_is_not_penalised():
|
|
"""Both donors carry feature 'a'. That is expected, not a conflict."""
|
|
target = ['a', 'b', 'c']
|
|
donors = [('PNL811', ['a', 'b']), ('PNL812', ['a', 'c'])]
|
|
table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 2
|
|
assert got.best.exact_count == 3
|
|
|
|
|
|
# ---------------------------------------------------------- partial coverage
|
|
|
|
def test_a_feature_nothing_covers_still_returns_the_cover_of_the_rest():
|
|
"""'Nothing found' and 'here is everything but feature 3' are different
|
|
answers, and only the second tells the engineer what to detail by hand."""
|
|
target = ['a', 'b', 'c', 'orphan']
|
|
donors = [('PNL901', ['a', 'b']), ('PNL902', ['c'])]
|
|
table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.PARTIAL
|
|
assert got.uncovered == (3,)
|
|
assert got.best.donors == ('PNL901', 'PNL902')
|
|
assert got.best.covered_count == 3
|
|
assert got.best.uncovered == (3,)
|
|
assert got.best.assignment[3] is None
|
|
assert 'no donor covers at all' in got.summary()
|
|
|
|
|
|
def test_partial_and_complete_summaries_do_not_read_alike():
|
|
target = ['a', 'orphan']
|
|
complete = rm.suggest(['a'], [('PNL911', ['a'])],
|
|
canned({('a', 'a'): 0.0}))
|
|
partial = rm.suggest(target, [('PNL911', ['a'])],
|
|
canned({('a', 'a'): 0.0}))
|
|
assert complete.status == rm.COMPLETE
|
|
assert partial.status == rm.PARTIAL
|
|
assert complete.summary() != partial.summary()
|
|
|
|
|
|
# ------------------------------------------------------------------ ranking
|
|
|
|
def test_exactness_breaks_a_tie_between_equal_sized_covers():
|
|
target = ['a', 'b']
|
|
donors = [('PNL_NEAR', ['n1', 'n2']), ('PNL_SAME', ['a', 'b'])]
|
|
table = {('n1', 'a'): 0.25, ('n2', 'b'): 0.25,
|
|
('a', 'a'): 0.0, ('b', 'b'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert [p.donors[0] for p in got.proposals] == ['PNL_SAME', 'PNL_NEAR']
|
|
|
|
|
|
def test_exactness_beats_evenness_and_a_sum_would_have_tied_them():
|
|
"""The documented consequence of ranking on the sorted distance vector.
|
|
|
|
Both covers total 2.0, so a sum calls them equal. The owner's ruling is
|
|
that the exact shapes go in front, so the cover with four exact features
|
|
wins even though its fifth is far off.
|
|
"""
|
|
target = ['a', 'b', 'c', 'd', 'e']
|
|
donors = [
|
|
('PNL_EVEN', ['v0', 'v1', 'v2', 'v3', 'v4']),
|
|
('PNL_SHARP', ['w0', 'w1', 'w2', 'w3', 'w4']),
|
|
]
|
|
table = {}
|
|
for i, f in enumerate(target):
|
|
table[('v{0}'.format(i), f)] = 0.4
|
|
table[('w{0}'.format(i), f)] = 0.0 if i < 4 else 2.0
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.proposals[0].total_distance == pytest.approx(2.0)
|
|
assert got.proposals[1].total_distance == pytest.approx(2.0)
|
|
assert got.proposals[0].donors == ('PNL_SHARP',)
|
|
assert got.proposals[0].exact_count == 4
|
|
assert got.proposals[1].exact_count == 0
|
|
|
|
|
|
def test_the_best_donor_feature_within_a_donor_wins():
|
|
"""A donor carrying two features that both cover one target feature
|
|
contributes its closer one, not whichever happens to be looked at first."""
|
|
target = ['a']
|
|
donors = [('PNL_TWO', ['far', 'near'])]
|
|
got = rm.suggest(target, donors,
|
|
canned({('far', 'a'): 5.0, ('near', 'a'): 0.5}))
|
|
assert got.best.assignment[0][2] == 0.5
|
|
assert got.best.donor_for(0) == ('PNL_TWO', 1, 0.5)
|
|
|
|
|
|
def test_max_proposals_trims_but_the_count_still_reports_the_truth():
|
|
target = ['a']
|
|
donors = [('PNL{0}'.format(i), ['a']) for i in range(8)]
|
|
got = rm.suggest(target, donors, canned({('a', 'a'): 0.0}),
|
|
max_proposals=3)
|
|
assert len(got.proposals) == 3
|
|
assert got.cover_count == 8
|
|
|
|
|
|
# ------------------------------------------------------------- determinism
|
|
|
|
def test_dead_even_ties_break_by_input_order():
|
|
"""Three indistinguishable donors. Input order decides, so the caller
|
|
controls it -- e.g. by listing same-elevation donors first."""
|
|
target = ['a']
|
|
table = {('a', 'a'): 0.0}
|
|
first = rm.suggest(target, [('PNL_A', ['a']), ('PNL_B', ['a']),
|
|
('PNL_C', ['a'])], canned(table))
|
|
assert [p.donors[0] for p in first.proposals] == ['PNL_A', 'PNL_B',
|
|
'PNL_C']
|
|
reordered = rm.suggest(target, [('PNL_C', ['a']), ('PNL_A', ['a']),
|
|
('PNL_B', ['a'])], canned(table))
|
|
assert [p.donors[0] for p in reordered.proposals] == ['PNL_C', 'PNL_A',
|
|
'PNL_B']
|
|
|
|
|
|
def test_the_same_inputs_give_the_same_answer_every_run():
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503)]
|
|
shape = None
|
|
for _ in range(25):
|
|
got = rm.suggest(PNL501, donors, rm.rect_distance)
|
|
this = (got.status, got.donors_needed, got.cover_count,
|
|
tuple([(p.donors, p.distances, p.assignment)
|
|
for p in got.proposals]))
|
|
if shape is None:
|
|
shape = this
|
|
assert this == shape
|
|
|
|
|
|
def test_determinism_survives_a_donor_list_full_of_equivalents():
|
|
target = ['a', 'b', 'c']
|
|
table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.5}
|
|
donors = [('PNL{0}'.format(i), ['a', 'b', 'c']) for i in range(12)]
|
|
runs = set()
|
|
for _ in range(10):
|
|
got = rm.suggest(target, donors, canned(table))
|
|
runs.add(tuple([p.donors for p in got.proposals]))
|
|
assert len(runs) == 1
|
|
|
|
|
|
# --------------------------------------- the worked example, invented names
|
|
|
|
def test_the_worked_example_splits_into_exactly_two_donors():
|
|
"""The shape of the owner's own example, rebuilt with synthetic marks.
|
|
|
|
PNL502 is the only donor with the two big central openings; PNL503 is the
|
|
only one with the right-end blockout. Neither alone will do, and the answer
|
|
is the pair -- which is the claim the whole feature rests on.
|
|
"""
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503)]
|
|
got = rm.suggest(PNL501, donors, rm.rect_distance)
|
|
assert got.status == rm.COMPLETE
|
|
assert got.uncovered == ()
|
|
assert got.donors_needed == 2
|
|
assert got.best.donors == ('PNL502', 'PNL503')
|
|
|
|
|
|
def test_the_worked_example_is_exact_everywhere_but_the_blockout():
|
|
"""Five of six features match exactly; the blockout is close, not equal.
|
|
|
|
That asymmetry is the measurement, and it is what the ranking is built to
|
|
express -- the exact features go in front, the approximate one carries a
|
|
number rather than a verdict.
|
|
"""
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503)]
|
|
best = rm.suggest(PNL501, donors, rm.rect_distance).best
|
|
assert best.exact_count == 5
|
|
assert best.covered_count == 6
|
|
assert not best.is_exact
|
|
# feature 0, the blockout, is the only inexact one, and it comes from the
|
|
# blockout donor.
|
|
assert best.donor_for(0)[0] == 'PNL503'
|
|
assert best.donor_for(0)[2] == pytest.approx(1.084, abs=0.001)
|
|
for i in (1, 2, 3, 4, 5):
|
|
assert best.donor_for(i)[2] == 0.0
|
|
|
|
|
|
def test_the_worked_example_big_openings_come_from_the_opening_donor():
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503)]
|
|
best = rm.suggest(PNL501, donors, rm.rect_distance).best
|
|
assert best.donor_for(1)[0] == 'PNL502'
|
|
assert best.donor_for(2)[0] == 'PNL502'
|
|
|
|
|
|
def test_a_donor_offering_only_the_common_end_hardware_does_not_get_chosen():
|
|
"""The end notches sit identically on every piece, so they discriminate
|
|
between nothing -- a donor carrying only them adds nothing to the cover."""
|
|
common_only = [PNL501[3], PNL501[4], PNL501[5]]
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503),
|
|
('PNL504', common_only)]
|
|
got = rm.suggest(PNL501, donors, rm.rect_distance)
|
|
assert got.donors_needed == 2
|
|
for proposal in got.proposals:
|
|
assert 'PNL504' not in proposal.donors
|
|
|
|
|
|
def test_a_third_donor_that_makes_a_smaller_cover_possible_takes_over():
|
|
"""One donor that carries the whole target beats the two-donor split."""
|
|
whole = list(PNL501)
|
|
donors = [('PNL502', PNL502), ('PNL503', PNL503), ('PNL505', whole)]
|
|
got = rm.suggest(PNL501, donors, rm.rect_distance)
|
|
assert got.donors_needed == 1
|
|
assert got.best.donors == ('PNL505',)
|
|
assert got.best.is_exact
|
|
|
|
|
|
# ------------------------------------------------------ greedy's known shape
|
|
|
|
def test_restarting_from_every_donor_beats_the_first_bite_trap():
|
|
"""The failure documented above `_greedy_covers`, run.
|
|
|
|
Nine features; PNL_DIAG is tied for the biggest single bite and is listed
|
|
first, so a single greedy pass takes it and then needs three more. The
|
|
restarts find the three-donor answer.
|
|
"""
|
|
target = [str(i) for i in range(9)]
|
|
diag = ['d2', 'd5', 'd8']
|
|
a, b, c = ['a0', 'a1', 'a2'], ['b3', 'b4', 'b5'], ['c6', 'c7', 'c8']
|
|
table = {('d2', '2'): 0.0, ('d5', '5'): 0.0, ('d8', '8'): 0.0}
|
|
for group, base in ((a, 0), (b, 3), (c, 6)):
|
|
for offset, name in enumerate(group):
|
|
table[(name, str(base + offset))] = 0.0
|
|
donors = [('PNL_DIAG', diag), ('PNL_A', a), ('PNL_B', b), ('PNL_C', c)]
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 3
|
|
assert got.best.donors == ('PNL_A', 'PNL_B', 'PNL_C')
|
|
|
|
|
|
def test_a_donor_that_covers_nothing_is_simply_ignored():
|
|
target = ['a', 'b']
|
|
donors = [('PNL_EMPTY', []), ('PNL_NONE', ['z']), ('PNL_GOOD', ['a', 'b'])]
|
|
got = rm.suggest(target, donors, canned({('a', 'a'): 0.0,
|
|
('b', 'b'): 0.0}))
|
|
assert got.donor_count == 3
|
|
assert got.donors_needed == 1
|
|
assert got.best.donors == ('PNL_GOOD',)
|
|
|
|
|
|
def test_a_donor_with_no_features_does_not_crash_the_run():
|
|
got = rm.suggest(['a'], [('PNL_EMPTY', None)], canned({}))
|
|
assert got.status == rm.NO_COVERAGE
|
|
|
|
|
|
# --------------------------------------------------- the matcher stays blind
|
|
|
|
def test_the_matcher_never_touches_a_feature_record():
|
|
"""Feature records that raise on ANY attribute access still work.
|
|
|
|
This is the seam, asserted. If the matcher ever grows a peek at a field,
|
|
this test is what fails.
|
|
"""
|
|
class Opaque(object):
|
|
def __init__(self, tag):
|
|
object.__setattr__(self, '_tag', tag)
|
|
|
|
def __getattr__(self, name):
|
|
raise AssertionError(
|
|
'the matcher read field {0!r} off a feature record'.format(
|
|
name))
|
|
|
|
def covers(donor_feature, target_feature):
|
|
d = object.__getattribute__(donor_feature, '_tag')
|
|
t = object.__getattribute__(target_feature, '_tag')
|
|
return 0.0 if d == t else None
|
|
|
|
target = [Opaque('a'), Opaque('b')]
|
|
donors = [('PNL_X', [Opaque('a')]), ('PNL_Y', [Opaque('b')])]
|
|
got = rm.suggest(target, donors, covers)
|
|
assert got.status == rm.COMPLETE
|
|
assert got.best.donors == ('PNL_X', 'PNL_Y')
|
|
|
|
|
|
def test_donor_keys_may_be_anything_hashable():
|
|
target = ['a']
|
|
donors = [((123456, 'assembly'), ['a'])]
|
|
got = rm.suggest(target, donors, canned({('a', 'a'): 0.0}))
|
|
assert got.best.donors == ((123456, 'assembly'),)
|
|
|
|
|
|
# ------------------------------------------------------- MASTER as tie-break
|
|
#
|
|
# Owner's ruling, 2026-08-30: a donor typed MASTER in the model has been
|
|
# REVIEWED, and reviewed donors are preferred. Asked how that interacts with
|
|
# exactness -- a loosely-fitting MASTER against a perfectly exact non-MASTER
|
|
# -- he ruled 拟合更好的排在前面: fit wins. MASTER breaks ties and nothing more.
|
|
#
|
|
# So there are exactly two things to prove: that it DOES decide a dead-even
|
|
# tie, and that it NEVER moves anything the distance vector has separated.
|
|
|
|
|
|
def test_master_breaks_a_tie_the_distances_cannot():
|
|
"""Two covers, identical distance vectors. The reviewed one goes first.
|
|
|
|
Without the signal these two are dead level and only input order
|
|
separates them -- and PNL801 is listed first, so a matcher that ignored
|
|
MASTER would put PNL801 on top. That is what makes this test load-bearing
|
|
rather than decorative.
|
|
"""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL801', ['a']), ('PNL802', ['b'])]
|
|
|
|
plain = rm.suggest(['t'], donors, covers)
|
|
assert plain.proposals[0].donors == ('PNL801',)
|
|
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL802'])
|
|
assert ranked.proposals[0].donors == ('PNL802',)
|
|
assert ranked.proposals[0].master_count == 1
|
|
assert ranked.proposals[1].master_count == 0
|
|
|
|
|
|
def test_master_never_beats_a_better_fit():
|
|
"""The ruling, as an executable statement.
|
|
|
|
PNL811 is a MASTER and fits loosely (0.5 off). PNL812 is not reviewed at
|
|
all and is exact. Fit wins, every time, and the reviewed donor comes
|
|
second -- it is not promoted past a cover the geometry has already put
|
|
in front of it.
|
|
"""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0})
|
|
donors = [('PNL811', ['a']), ('PNL812', ['b'])]
|
|
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL811'])
|
|
assert ranked.proposals[0].donors == ('PNL812',)
|
|
assert ranked.proposals[0].master_count == 0
|
|
assert ranked.proposals[1].donors == ('PNL811',)
|
|
|
|
|
|
def test_master_never_beats_a_better_fit_even_a_hair_better():
|
|
"""The margin does not matter. 0.001 ft of fit still outranks a review."""
|
|
covers = canned({('a', 't'): 0.001, ('b', 't'): 0.0})
|
|
donors = [('PNL821', ['a']), ('PNL822', ['b'])]
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL821'])
|
|
assert ranked.proposals[0].donors == ('PNL822',)
|
|
|
|
|
|
def test_master_never_beats_fewer_donors():
|
|
"""Donor count is the objective and MASTER sits below that too.
|
|
|
|
Only covers of the smallest size are built at all, so a two-donor set of
|
|
masters is never even offered against a one-donor non-master cover.
|
|
"""
|
|
covers = canned({('a', 't0'): 0.0, ('a', 't1'): 0.0,
|
|
('b', 't0'): 0.0, ('c', 't1'): 0.0})
|
|
donors = [('PNL831', ['a']), ('PNL832', ['b']), ('PNL833', ['c'])]
|
|
ranked = rm.suggest(['t0', 't1'], donors, covers,
|
|
masters=['PNL832', 'PNL833'])
|
|
assert ranked.donors_needed == 1
|
|
assert ranked.proposals[0].donors == ('PNL831',)
|
|
|
|
|
|
def test_more_masters_ranks_above_fewer_when_everything_else_is_level():
|
|
"""A cover carrying a reviewed donor outranks one carrying none.
|
|
|
|
Four donors, two features, every distance 0.5, so the distance vectors of
|
|
all three covers found are identical and only the tie-breaks separate
|
|
them. PNL844 is the only master IN A COVER, and its cover is not the one
|
|
input order would have chosen -- (PNL841, PNL842) is. So this fails if
|
|
the master count is dropped from the key or put below input order.
|
|
|
|
(Only three covers exist rather than all four pairings because the search
|
|
is greedy-with-restarts and seeds each walk from one donor; that is the
|
|
matcher's documented approximation and is not what is under test here.)
|
|
"""
|
|
covers = canned({('a', 't0'): 0.5, ('b', 't1'): 0.5,
|
|
('c', 't0'): 0.5, ('d', 't1'): 0.5})
|
|
donors = [('PNL841', ['a']), ('PNL842', ['b']),
|
|
('PNL843', ['c']), ('PNL844', ['d'])]
|
|
plain = rm.suggest(['t0', 't1'], donors, covers)
|
|
assert plain.proposals[0].donors == ('PNL841', 'PNL842')
|
|
|
|
ranked = rm.suggest(['t0', 't1'], donors, covers,
|
|
masters=['PNL843', 'PNL844'])
|
|
assert ranked.proposals[0].donors == ('PNL841', 'PNL844')
|
|
assert ranked.proposals[0].master_count == 1
|
|
assert ranked.proposals[-1].master_count == 0
|
|
|
|
|
|
def test_master_is_never_a_filter():
|
|
"""A pool with one MASTER still offers every other cover it found."""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5, ('c', 't'): 0.5})
|
|
donors = [('PNL851', ['a']), ('PNL852', ['b']), ('PNL853', ['c'])]
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL852'])
|
|
assert len(ranked.proposals) == 3
|
|
assert set(p.donors[0] for p in ranked.proposals) == {
|
|
'PNL851', 'PNL852', 'PNL853'}
|
|
|
|
|
|
def test_a_master_key_that_is_not_in_the_pool_is_ignored():
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL861', ['a']), ('PNL862', ['b'])]
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL999'])
|
|
assert ranked.proposals[0].donors == ('PNL861',)
|
|
assert all(p.master_count == 0 for p in ranked.proposals)
|
|
|
|
|
|
def test_no_masters_ranks_exactly_as_before():
|
|
"""The signal off is the old matcher, byte for byte in its ordering."""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL871', ['a']), ('PNL872', ['b'])]
|
|
a = rm.suggest(['t'], donors, covers)
|
|
b = rm.suggest(['t'], donors, covers, masters=[])
|
|
c = rm.suggest(['t'], donors, covers, masters=None)
|
|
assert ([p.donors for p in a.proposals] == [p.donors for p in b.proposals]
|
|
== [p.donors for p in c.proposals])
|
|
|
|
|
|
def test_a_proposal_reports_which_of_its_donors_are_reviewed():
|
|
covers = canned({('a', 't0'): 0.0, ('b', 't1'): 0.0})
|
|
donors = [('PNL881', ['a']), ('PNL882', ['b'])]
|
|
ranked = rm.suggest(['t0', 't1'], donors, covers, masters=['PNL882'])
|
|
best = ranked.proposals[0]
|
|
assert best.masters == ('PNL882',)
|
|
assert best.is_master('PNL882') and not best.is_master('PNL881')
|
|
|
|
|
|
# ---------------------------------------------------- SAME ELEVATION as tie-break
|
|
#
|
|
# Owner, 2026-09-02, on WP425: five donors tied on every criterion above --
|
|
# 1 donor, 5/5 covered, 4 exact, 0 reviewed, 0.04 in total distance, all five
|
|
# identical -- and the tie fell through to mark order, so WP133 printed
|
|
# first though WP234 shares WP425's own building elevation:
|
|
# "WP234明明在同一个elevation,为什么排名没有提前?" Approved as briefed
|
|
# ("同意,就这么做"): elevation is an AFFINITY, ranked LAST -- below MASTER,
|
|
# above input order -- never a filter. The proof obligations mirror the
|
|
# MASTER section exactly: it DOES decide a dead-even tie, and it NEVER moves
|
|
# anything the distance vector or MASTER have already separated.
|
|
|
|
|
|
def test_same_elevation_breaks_a_tie_the_distances_and_master_cannot():
|
|
"""Two covers, identical distance vectors and no MASTER either. The
|
|
donor sharing the target's elevation goes first.
|
|
|
|
Without the signal these two are dead level and only input order
|
|
separates them -- and PNL901 is listed first, so a matcher that ignored
|
|
elevation would put PNL901 on top. That is what makes this test
|
|
load-bearing rather than decorative.
|
|
"""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL901', ['a']), ('PNL902', ['b'])]
|
|
|
|
plain = rm.suggest(['t'], donors, covers)
|
|
assert plain.proposals[0].donors == ('PNL901',)
|
|
|
|
ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL902'])
|
|
assert ranked.proposals[0].donors == ('PNL902',)
|
|
assert ranked.proposals[0].elevation_count == 1
|
|
assert ranked.proposals[1].elevation_count == 0
|
|
|
|
|
|
def test_same_elevation_never_beats_a_better_fit():
|
|
"""The ruling, as an executable statement.
|
|
|
|
PNL911 shares the target's elevation but fits loosely (0.5 off). PNL912
|
|
is on a different elevation and is exact. Fit wins, every time, and the
|
|
same-elevation donor comes second -- it is not promoted past a cover the
|
|
geometry has already put in front of it.
|
|
"""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0})
|
|
donors = [('PNL911', ['a']), ('PNL912', ['b'])]
|
|
|
|
ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL911'])
|
|
assert ranked.proposals[0].donors == ('PNL912',)
|
|
assert ranked.proposals[0].elevation_count == 0
|
|
assert ranked.proposals[1].donors == ('PNL911',)
|
|
|
|
|
|
def test_same_elevation_never_beats_fewer_donors():
|
|
"""Donor count is the objective and elevation sits below that too."""
|
|
covers = canned({('a', 't0'): 0.0, ('a', 't1'): 0.0,
|
|
('b', 't0'): 0.0, ('c', 't1'): 0.0})
|
|
donors = [('PNL921', ['a']), ('PNL922', ['b']), ('PNL923', ['c'])]
|
|
ranked = rm.suggest(['t0', 't1'], donors, covers,
|
|
same_elevation=['PNL922', 'PNL923'])
|
|
assert ranked.donors_needed == 1
|
|
assert ranked.proposals[0].donors == ('PNL921',)
|
|
|
|
|
|
def test_same_elevation_sits_below_master():
|
|
"""MASTER decides first; elevation only breaks what MASTER leaves tied.
|
|
|
|
PNL931 is reviewed but on a different elevation; PNL932 shares the
|
|
target's elevation but is not reviewed. Both fit identically (0.5 off).
|
|
MASTER wins -- exactly the priority `_proposal_sort_key` states.
|
|
"""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL931', ['a']), ('PNL932', ['b'])]
|
|
ranked = rm.suggest(['t'], donors, covers, masters=['PNL931'],
|
|
same_elevation=['PNL932'])
|
|
assert ranked.proposals[0].donors == ('PNL931',)
|
|
assert ranked.proposals[0].master_count == 1
|
|
assert ranked.proposals[0].elevation_count == 0
|
|
|
|
|
|
def test_same_elevation_is_never_a_filter():
|
|
"""A pool with one same-elevation donor still offers every cover found."""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5, ('c', 't'): 0.5})
|
|
donors = [('PNL941', ['a']), ('PNL942', ['b']), ('PNL943', ['c'])]
|
|
ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL942'])
|
|
assert len(ranked.proposals) == 3
|
|
assert set(p.donors[0] for p in ranked.proposals) == {
|
|
'PNL941', 'PNL942', 'PNL943'}
|
|
|
|
|
|
def test_a_same_elevation_key_that_is_not_in_the_pool_is_ignored():
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL951', ['a']), ('PNL952', ['b'])]
|
|
ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL999'])
|
|
assert ranked.proposals[0].donors == ('PNL951',)
|
|
assert all(p.elevation_count == 0 for p in ranked.proposals)
|
|
|
|
|
|
def test_no_same_elevation_ranks_exactly_as_before():
|
|
"""The signal off is the old matcher, byte for byte in its ordering."""
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5})
|
|
donors = [('PNL961', ['a']), ('PNL962', ['b'])]
|
|
a = rm.suggest(['t'], donors, covers)
|
|
b = rm.suggest(['t'], donors, covers, same_elevation=[])
|
|
c = rm.suggest(['t'], donors, covers, same_elevation=None)
|
|
assert ([p.donors for p in a.proposals] == [p.donors for p in b.proposals]
|
|
== [p.donors for p in c.proposals])
|
|
|
|
|
|
def test_a_proposal_reports_which_of_its_donors_share_elevation():
|
|
covers = canned({('a', 't0'): 0.0, ('b', 't1'): 0.0})
|
|
donors = [('PNL971', ['a']), ('PNL972', ['b'])]
|
|
ranked = rm.suggest(['t0', 't1'], donors, covers,
|
|
same_elevation=['PNL972'])
|
|
best = ranked.proposals[0]
|
|
assert best.same_elevation == ('PNL972',)
|
|
|
|
|
|
# ------------------------------------------------------------- the donor cap
|
|
#
|
|
# Owner's ruling, 2026-08-30: "一个方案,只要找到足够覆盖全部特征的母版,就够了。
|
|
# 我期待是 1-4 个,最多不超过 4 个。" Four donors in one proposal, hard.
|
|
#
|
|
# The OVERFLOW rule -- what happens when four cannot cover everything -- was
|
|
# NOT ruled and is inferred: return the best four and name what they miss,
|
|
# never relax to a fifth. These tests are what pins that reading, so if the
|
|
# owner overrules it they are the place to start.
|
|
|
|
|
|
def _one_each(n):
|
|
"""n target features, n donors, each donor carrying exactly one of them.
|
|
|
|
The worst case for a cap: the only way to cover everything is to take
|
|
every donor, so any cap below n has to bite.
|
|
"""
|
|
target = [str(i) for i in range(n)]
|
|
donors = [('PNL{0}'.format(600 + i), [str(i)]) for i in range(n)]
|
|
table = dict(((str(i), str(i)), 0.0) for i in range(n))
|
|
return target, donors, table
|
|
|
|
|
|
def test_a_proposal_never_names_more_than_four_donors():
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.donor_cap == 4
|
|
for proposal in got.proposals:
|
|
assert len(proposal.donors) <= 4
|
|
|
|
|
|
def test_the_cap_does_not_relax_to_a_fifth_donor():
|
|
"""The inferred boundary, stated as a test. Eight features, eight donors
|
|
with one feature each: five donors would cover more and the search may
|
|
not take them."""
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.donors_needed == 4
|
|
assert len(got.best.donors) == 4
|
|
assert got.best.covered_count == 4
|
|
|
|
|
|
def test_the_cap_names_what_it_could_not_reach():
|
|
"""A partial answer that says what it missed is useful; one that quietly
|
|
covers half the panel is not."""
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.CAPPED
|
|
assert len(got.capped_out) == 4
|
|
assert set(got.capped_out) == set(got.best.uncovered)
|
|
assert 'not within' in got.summary() or 'not by any' in got.summary()
|
|
|
|
|
|
def test_capped_out_is_never_confused_with_nobody_has_it():
|
|
"""Two different findings with opposite remedies. A feature the cap left
|
|
out HAS a donor -- go and open it. A feature nobody has must be detailed
|
|
from scratch. They are reported in separate fields and never merged."""
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.uncovered == () # somebody has every one of them
|
|
assert got.capped_out # the cap is why they are missing
|
|
assert got.status == rm.CAPPED
|
|
assert got.status != rm.PARTIAL
|
|
|
|
|
|
def test_the_cap_and_a_feature_nobody_has_are_reported_apart():
|
|
"""Both can be true at once. The status reports the harder fact, and the
|
|
two sets stay disjoint so the report can print two tables."""
|
|
target, donors, table = _one_each(8)
|
|
target = target + ['orphan']
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.PARTIAL # nobody has 'orphan'
|
|
assert got.uncovered == (8,)
|
|
assert len(got.capped_out) == 4
|
|
assert not set(got.capped_out) & set(got.uncovered)
|
|
|
|
|
|
def test_four_donors_that_do_cover_everything_are_a_complete_answer():
|
|
"""The cap only bites when it has to. At exactly four, nothing changes."""
|
|
target, donors, table = _one_each(4)
|
|
got = rm.suggest(target, donors, canned(table))
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 4
|
|
assert got.capped_out == ()
|
|
|
|
|
|
def test_the_cap_is_not_the_proposal_limit():
|
|
"""THE TWO AXES. `max_donors` bounds the size of one answer;
|
|
`max_proposals` bounds how many answers come back. Conflating them gives
|
|
either one giant proposal or four tiny ones."""
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table), max_proposals=2)
|
|
assert len(got.proposals) == 2 # the proposal limit bit
|
|
assert got.cover_count > 2 # and there were more of them
|
|
for proposal in got.proposals:
|
|
assert len(proposal.donors) == 4 # the donor cap bit separately
|
|
|
|
|
|
def test_under_the_cap_covering_more_beats_using_fewer_donors():
|
|
"""Fewest donors is the objective only among answers that cover the same
|
|
ground. Once the cap can stop a walk short, a two-donor set covering two
|
|
features must not outrank a four-donor set covering six."""
|
|
target = ['a', 'b', 'c', 'd', 'e', 'f']
|
|
donors = [
|
|
('PNL_SMALL_1', ['a']),
|
|
('PNL_SMALL_2', ['b']),
|
|
('PNL_BIG_1', ['c', 'd']),
|
|
('PNL_BIG_2', ['e', 'f']),
|
|
]
|
|
table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0,
|
|
('d', 'd'): 0.0, ('e', 'e'): 0.0, ('f', 'f'): 0.0}
|
|
got = rm.suggest(target, donors, canned(table), max_donors=2)
|
|
assert got.best.covered_count == 4
|
|
assert got.best.donors == ('PNL_BIG_1', 'PNL_BIG_2')
|
|
assert got.status == rm.CAPPED
|
|
|
|
|
|
def test_the_cap_can_be_lifted_and_the_old_answer_comes_back():
|
|
"""`max_donors=None` is the uncapped matcher, unchanged. Kept so the
|
|
ruling stays a parameter rather than a rewrite."""
|
|
target, donors, table = _one_each(8)
|
|
got = rm.suggest(target, donors, canned(table), max_donors=None)
|
|
assert got.status == rm.COMPLETE
|
|
assert got.donors_needed == 8
|
|
assert got.capped_out == ()
|
|
|
|
|
|
def test_a_better_fit_still_wins_under_the_cap():
|
|
"""THE LOAD-BEARING RULE, re-checked with the cap in place: a better fit
|
|
outranks everything below it, MASTER included. Both covers here are of
|
|
legal size, so the cap has nothing to say and must not interfere."""
|
|
target = ['t']
|
|
donors = [('PNL811', ['a']), ('PNL812', ['b'])]
|
|
covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0})
|
|
got = rm.suggest(target, donors, covers, masters=['PNL811'])
|
|
assert got.best.donors == ('PNL812',) # the exact one, not the MASTER
|
|
assert got.best.exact_count == 1
|
|
|
|
|
|
def test_the_cap_does_not_reorder_covers_of_equal_reach():
|
|
"""Among covers that reach equally far the ranking is the old one, in
|
|
order: fewest donors, then the distance vector, then MASTER, then input
|
|
order. The cap adds a term above all of that and it is constant here."""
|
|
target = ['t0', 't1']
|
|
donors = [('PNL821', ['a', 'b']), ('PNL822', ['c', 'd'])]
|
|
covers = canned({('a', 't0'): 1.0, ('b', 't1'): 1.0,
|
|
('c', 't0'): 0.0, ('d', 't1'): 0.0})
|
|
got = rm.suggest(target, donors, covers)
|
|
assert [p.donors[0] for p in got.proposals] == ['PNL822', 'PNL821']
|