Source code for trivoting.election.preflib

from __future__ import annotations

from preflibtools.instances import CategoricalInstance, get_parsed_instance
from trivoting.election import TrichotomousMultiProfile, FrozenTrichotomousBallot

from trivoting.election.alternative import Alternative


[docs] def cat_preferences_to_frozen_trichotomous_ballot( pref: tuple[tuple[int]], alt_map: dict[int, Alternative] ) -> FrozenTrichotomousBallot: """ Converts a categorical preference from PrefLib into a trichotomous ballot. The first category in the preference is treated as the set of approved alternatives. If a second category is present, it is treated as neutral and ignored in the ballot. If a third category is present, it is treated as the set of disapproved alternatives. Parameters ---------- pref : tuple[tuple[int]] The categorical preferences, typically a tuple of up to 3 ranked groups of alternative IDs. alt_map : dict[int, Alternative] A mapping from PrefLib integer IDs to Alternative objects. Returns ------- FrozenTrichotomousBallot The corresponding trichotomous ballot. Raises ------ ValueError If the number of categories is not between 1 and 3. """ if len(pref) == 0 or len(pref) > 3: raise ValueError( "Only categorical preferences between 1 and 3 categories can be converted to" f"a trichotomous ballot. Pref {pref} has {len(pref)} categories." ) if len(pref) < 2: return FrozenTrichotomousBallot(approved=(alt_map[j] for j in pref[0])) return FrozenTrichotomousBallot( approved=(alt_map[j] for j in pref[0]), disapproved=(alt_map[j] for j in pref[-1]), )
[docs] def cat_instance_to_trichotomous_profile( cat_instance: CategoricalInstance, ) -> TrichotomousMultiProfile: """ Converts a PrefLib CategoricalInstance into a trichotomous profile. The PrefLib instance should have 1, 2 or 3 categories. If there is a single categories, it is assumed to represent the approved alternatives. If there are 2 categories, it is assumed that they represent the approved and neutral alternatives. In case of 3 categories, the categories are assumed to represent approved, neutral and disapproved alternatives, in that order. Each ballot in the categorical instance is converted using `cat_preferences_to_trichotomous_ballot`. Parameters ---------- cat_instance : CategoricalInstance A parsed categorical instance from PrefLib. Returns ------- TrichotomousMultiProfile A multi-profile composed of trichotomous ballots. """ if cat_instance.num_categories == 0 or cat_instance.num_categories > 3: raise ValueError( "Only categorical preferences between 1 and 3 categories can be converted to" f"a trichotomous profile. Categorical instance {cat_instance} has " f"{cat_instance.num_categories} categories." ) alt_map = {j: Alternative(str(j)) for j in cat_instance.alternatives_name} profile = TrichotomousMultiProfile(alternatives=alt_map.values()) for p, m in cat_instance.multiplicity.items(): ballot = cat_preferences_to_frozen_trichotomous_ballot(p, alt_map) profile[ballot] = m return profile
[docs] def parse_preflib(file_path: str) -> TrichotomousMultiProfile: """ Parses a PrefLib file and returns the corresponding trichotomous profile. The file is parsed using `preflibtools.get_parsed_instance`, and only categorical instances with 1–3 categories are supported. Parameters ---------- file_path : str The file path to a PrefLib categorical instance. Returns ------- TrichotomousMultiProfile A trichotomous multi-profile built from the given file. """ instance = get_parsed_instance(file_path, autocorrect=True) if isinstance(instance, CategoricalInstance): return cat_instance_to_trichotomous_profile(instance) raise ValueError( f"PrefLib instances of type {type(instance)} cannot be converted to trichotomous profiles." )