def test_simple_strings(): criteria = [['bad', 'good'], ['expensive', 'cheap']] alts = [('bad', 'cheap'), ('good', 'expensive'), ('bad', 'expensive')] def asker(a, b): # We prefer good to bad, but when that criterion is the same, # we prefer cheap to expensive. if a[0] == b[0]: if a[1] == b[1]: return EQ elif a[1] == 'cheap': return GT else: return LT elif a[0] == 'good': return GT else: return LT for find_best in (1, 2, None): prefs = vda(criteria=criteria, alts=alts if find_best else None, asker=asker, find_best=find_best) assert prefs.maxes(among=alts) == {('good', 'expensive')} if find_best == 2: assert prefs.extreme(2, among=alts) == {('good', 'expensive'), ('bad', 'cheap')} elif not find_best: assert prefs.maxes() == {('good', 'cheap')} ranking = (('bad', 'expensive'), ('bad', 'cheap'), ('good', 'expensive'), ('good', 'cheap')) for (ai, a), (bi, b) in choose2(enumerate(ranking)): assert prefs.cmp(a, b) == Relation.cmp(ai, bi)
def test_appendixD(): '''Appendix D of Larichev and Moshkovich (1995).''' criteria = [(3, 2, 1)] * 3 # 3 is worst and 1 is best. alts = [(1, 2, 3), (2, 3, 1), (3, 1, 2)] dm_ranking = [(2, 1, 1), (1, 1, 2), (1, 2, 1), (1, 3, 1), (3, 1, 1), (1, 1, 3)] # The first element is the best, so we negate `cmp` in the # asker. def asker(a, b): return -Relation.cmp(dm_ranking.index(a), dm_ranking.index(b)) Proposal1, Proposal2, Proposal3 = alts for goal in ('find_best', 'rank_alts', 'rank_space'): prefs = vda(criteria=criteria, alts=(None if goal == 'rank_alts' else alts), asker=asker, find_best=goal == 'find_best' and 1) assert prefs.maxes(among=alts) == {Proposal2} if goal == 'rank_alts': for v1, v2 in choose2(dm_ranking): assert prefs.cmp(v1, v2) == -Relation.cmp( dm_ranking.index(v1), dm_ranking.index(v2)) if goal != 'find_best': assert prefs.cmp(Proposal2, Proposal1) == GT assert prefs.cmp(Proposal2, Proposal3) == GT assert prefs.cmp(Proposal3, Proposal1) == GT
def test_one_criterion(): '''When there's only one criterion, we should never need to ask questions, because the rule of dominance suffices to infer all preferences.''' for criterion_length in range(2, 10): prefs = artiruno.vda(criteria=[tuple(range(criterion_length))], asker=asker_stub) for i, j in choose2(range(criterion_length)): assert prefs.cmp((i, ), (j, )) == LT
def test_extreme_n(): x = PreorderedSet(l + str(i) for l in "wxyz" for i in range(3)) for a, b in choose2(x.elements): x.learn(a, b, Relation.cmp(a[0], b[0])) assert (x.maxes() == x.extreme(2) == x.extreme(3) == {"z0", "z1", "z2"}) assert (x.extreme(4) == x.extreme(5) == x.extreme(6) == {"z0", "z1", "z2", "y0", "y1", "y2"}) def xb(n): return x.extreme(n, bottom = True) assert (x.mins() == xb(2) == xb(3) == {"w0", "w1", "w2"}) assert (xb(4) == xb(5) == xb(6) == {"w0", "w1", "w2", "x0", "x1", "x2"})
def out(criteria, run_slow_tests): if criteria in criteria_slow and not run_slow_tests: pytest.skip() questions_asked = 0 expect_questions_asked = (inspect.signature( f).parameters['n_questions'].default)[criteria_all.index(criteria)] criteria = tuple(tuple(range(n)) for n in criteria) item_space = tuple(itertools.product(*criteria)) for trial in range(trials): R = random.Random((criteria, trial)) alts = R.sample(item_space, min(len(item_space), R.randint(2, 8))) find_best = R.choice([True, None]) if find_best is True: find_best = R.choice([i for i in [1, 2, 3] if i < len(alts)]) asker = f(criteria, R) def counting_asker(a, b): nonlocal questions_asked questions_asked += 1 return asker(a, b) prefs = vda(criteria, alts, counting_asker, find_best, max_dev=2 * len(criteria)) if find_best: assert prefs.extreme(find_best, among=alts) == { a for a in alts for cmps in [Counter(asker(a, b) for b in alts)] if cmps[IC] == 0 and cmps[LT] < find_best } else: for a, b in choose2(alts): assert prefs.cmp(a, b) == asker(a, b) assert questions_asked == expect_questions_asked