diff --git a/elsim/studies/__init__.py b/elsim/studies/__init__.py index 918e04a6..5f7f5dad 100644 --- a/elsim/studies/__init__.py +++ b/elsim/studies/__init__.py @@ -13,7 +13,11 @@ """ from .backends import JoblibBackend, SerialBackend -from .condorcet_metrics import merrill_1984_comparison_methods, tally_condorcet_agreement +from .condorcet_metrics import ( + approval_at_optimal, + merrill_1984_comparison_methods, + tally_condorcet_agreement, +) from .parameters import expand_product, expand_rows, expand_zip from .runner import merge_counters, run_batched from .social_utility import ( @@ -31,6 +35,7 @@ "merge_counters", "run_batched", "merrill_1984_comparison_methods", + "approval_at_optimal", "tally_condorcet_agreement", "spatial_random_reference_utility_updates", "random_society_utility_updates", diff --git a/elsim/studies/condorcet_metrics.py b/elsim/studies/condorcet_metrics.py index 6916a5b9..344dc54f 100644 --- a/elsim/studies/condorcet_metrics.py +++ b/elsim/studies/condorcet_metrics.py @@ -19,7 +19,14 @@ RatedMethod = Callable[..., Optional[int]] -def _approval_at_optimal(utilities: np.ndarray, tiebreaker: str) -> Optional[int]: # noqa: UP045 +def approval_at_optimal(utilities: np.ndarray, tiebreaker: str = "random") -> Optional[int]: # noqa: UP045 + """ + Rated-method helper: build an optimal approval ballot, then apply :func:`elsim.methods.approval`. + + Intended for use in a ``rated_methods`` mapping passed to + :func:`tally_condorcet_agreement` or the spatial sweep helpers, so scripts + stay declarative without repeating lambdas. + """ return approval(approval_optimal(utilities), tiebreaker) @@ -44,7 +51,7 @@ def merrill_1984_comparison_methods() -> tuple[dict[str, RankedMethod], dict[str } rated_methods: dict[str, RatedMethod] = { "SU max": utility_winner, - "Approval": _approval_at_optimal, + "Approval": approval_at_optimal, } return ranked_methods, rated_methods @@ -76,7 +83,7 @@ def tally_condorcet_agreement( Returns ------- collections.Counter - Includes key ``\"CW\"`` when a Condorcet winner exists, plus one key per + Includes key ``"CW"`` when a Condorcet winner exists, plus one key per supplied method name when that method's winner matches the Condorcet winner. """ diff --git a/examples/merrill_1984_table_1_fig_1.py b/examples/merrill_1984_table_1_fig_1.py index 2604ac1a..bf258a65 100644 --- a/examples/merrill_1984_table_1_fig_1.py +++ b/examples/merrill_1984_table_1_fig_1.py @@ -32,14 +32,19 @@ from tabulate import tabulate from elsim.elections import random_utilities +from elsim.methods import black, borda, coombs, fptp, irv, runoff, utility_winner from elsim.strategies import honest_rankings -from elsim.studies import merrill_1984_comparison_methods, tally_condorcet_agreement +from elsim.studies import approval_at_optimal, tally_condorcet_agreement n_elections = 10_000 # Roughly 15 seconds on a 2019 6-core i7-9750H n_voters = 25 n_cands_list = (2, 3, 4, 5, 7, 10) -ranked_methods, rated_methods = merrill_1984_comparison_methods() +ranked_methods = {'Plurality': fptp, 'Runoff': runoff, 'Hare': irv, + 'Borda': borda, 'Coombs': coombs, 'Black': black} + +rated_methods = {'SU max': utility_winner, + 'Approval': approval_at_optimal} condorcet_winner_count = {key: Counter() for key in ( ranked_methods.keys() | rated_methods.keys() | {'CW'})} diff --git a/examples/merrill_1984_table_2.py b/examples/merrill_1984_table_2.py index c684900d..4aa6873a 100644 --- a/examples/merrill_1984_table_2.py +++ b/examples/merrill_1984_table_2.py @@ -38,14 +38,19 @@ from tabulate import tabulate from elsim.elections import normal_electorate, normed_dist_utilities +from elsim.methods import black, borda, coombs, fptp, irv, runoff, utility_winner from elsim.strategies import honest_rankings -from elsim.studies import expand_rows, merrill_1984_comparison_methods, tally_condorcet_agreement +from elsim.studies import approval_at_optimal, expand_rows, tally_condorcet_agreement n_elections = 10_000 # Roughly 60 seconds on a 2019 6-core i7-9750H n_voters = 201 n_cands = 5 -ranked_methods, rated_methods = merrill_1984_comparison_methods() +ranked_methods = {'Plurality': fptp, 'Runoff': runoff, 'Hare': irv, + 'Borda': borda, 'Coombs': coombs, 'Black': black} + +rated_methods = {'SU max': utility_winner, + 'Approval': approval_at_optimal} # disp, corr, D condition_rows = ((1.0, 0.5, 2), diff --git a/examples/merrill_1984_table_4.py b/examples/merrill_1984_table_4.py index a3f5bec2..44965279 100644 --- a/examples/merrill_1984_table_4.py +++ b/examples/merrill_1984_table_4.py @@ -36,10 +36,11 @@ from tabulate import tabulate from elsim.elections import normal_electorate, normed_dist_utilities +from elsim.methods import black, borda, coombs, fptp, irv, runoff, utility_winner from elsim.strategies import honest_rankings from elsim.studies import ( + approval_at_optimal, expand_rows, - merrill_1984_comparison_methods, spatial_random_reference_utility_updates, ) @@ -47,7 +48,11 @@ n_voters = 201 n_cands = 5 -ranked_methods, rated_methods = merrill_1984_comparison_methods() +ranked_methods = {'Plurality': fptp, 'Runoff': runoff, 'Hare': irv, + 'Borda': borda, 'Coombs': coombs, 'Black': black} + +rated_methods = {'SU max': utility_winner, + 'Approval': approval_at_optimal} # disp, corr, D condition_rows = ((1.0, 0.5, 2),