# -*- coding: utf-8 -*-
"""
Created on 12 jul. 2021, 13:55
Copyright François Durand 2014-2021
fradurand@gmail.com
This file is part of SVVAMP.
SVVAMP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
SVVAMP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SVVAMP. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from svvamp.rules.rule import Rule
from svvamp.rules.rule_irv import RuleIRV
from svvamp.preferences.profile import Profile
from svvamp.utils.util_cache import cached_property
from svvamp.utils.pseudo_bool import equal_true
from svvamp.utils.misc import preferences_ut_to_matrix_duels_ut
[docs]
class RuleWoodall(Rule):
"""Woodall Rule.
Options
-------
>>> RuleWoodall.print_options_parameters()
cm_option: ['fast', 'slow', 'very_slow', 'exact']. Default: 'fast'.
icm_option: ['exact']. Default: 'exact'.
iia_subset_maximum_size: is_number. Default: 2.
im_option: ['lazy', 'exact']. Default: 'lazy'.
tm_option: ['exact']. Default: 'exact'.
um_option: ['lazy', 'exact']. Default: 'lazy'.
Notes
-----
Each voter must provide a strict total order. Among the candidates of the Smith set
(in the sense of :attr:`smith_set_rk`), elect the one that is eliminated latest by :class:`RuleIRV`.
* :meth:`is_cm_`:
* :attr:`cm_option` = ``'fast'``: Rely on :class:`RuleIRV`'s fast algorithm. Polynomial heuristic. Can prove
CM but unable to decide non-CM (except in rare obvious cases).
* :attr:`cm_option` = ``'slow'``: Rely on :class:`RuleExhaustiveBallot`'s exact algorithm. Non-polynomial
heuristic (:math:`2^{n_c}`). Quite efficient to prove CM or non-CM.
* :attr:`cm_option` = ``'very_slow'``: Rely on :class:`RuleIRV`'s exact algorithm. Non-polynomial
heuristic (:math:`n_c!`). Very efficient to prove CM or non-CM.
* :attr:`cm_option` = ``'exact'``: Non-polynomial algorithm from superclass :class:`Rule`.
Each algorithm above exploits the faster ones. For example, if :attr:`cm_option` = ``'very_slow'``,
SVVAMP tries the fast algorithm first, then the slow one, then the 'very slow' one. As soon as it reaches
a decision, computation stops.
Woodall does not :attr:`meets_condorcet_c_ut_abs_ctb`:
>>> profile = Profile(preferences_ut=[
... [ 0. , 0.5, -0.5],
... [ 0.5, -0.5, 1. ],
... ], preferences_rk=[
... [1, 0, 2],
... [2, 0, 1],
... ])
>>> RuleWoodall()(profile).w_
1
>>> profile.condorcet_winner_ut_abs_ctb
0
Woodall does not :attr:`meets_condorcet_c_ut_rel`:
>>> profile = Profile(preferences_ut=[
... [ 1. , 1. , 1. ],
... [ 0.5, -0.5, 1. ],
... ], preferences_rk=[
... [0, 1, 2],
... [2, 0, 1],
... ])
>>> RuleWoodall()(profile).w_
0
>>> profile.condorcet_winner_ut_rel
2
References
----------
'Four Condorcet-Hare Hybrid Methods for Single-Winner Elections', James Green-Armytage, 2011.
Examples
--------
>>> profile = Profile(preferences_ut=[
... [ 0. , -0.5, -1. ],
... [ 1. , -1. , 0.5],
... [ 0.5, 0.5, -0.5],
... [ 0.5, 0. , 1. ],
... [-1. , -1. , 1. ],
... ], preferences_rk=[
... [0, 1, 2],
... [0, 2, 1],
... [1, 0, 2],
... [2, 0, 1],
... [2, 1, 0],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.demo_results_(log_depth=0) # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE>
************************
* *
* Election Results *
* *
************************
<BLANKLINE>
***************
* Results *
***************
profile_.preferences_ut (reminder) =
[[ 0. -0.5 -1. ]
[ 1. -1. 0.5]
[ 0.5 0.5 -0.5]
[ 0.5 0. 1. ]
[-1. -1. 1. ]]
profile_.preferences_rk (reminder) =
[[0 1 2]
[0 2 1]
[1 0 2]
[2 0 1]
[2 1 0]]
ballots =
[[0 1 2]
[0 2 1]
[1 0 2]
[2 0 1]
[2 1 0]]
scores =
[[1 0 0]
[2 0 1]]
candidates_by_scores_best_to_worst
[0, 2, 1]
scores_best_to_worst
[[1 0 0]
[2 1 0]]
w = 0
score_w = [1 2]
total_utility_w = 1.0
<BLANKLINE>
*********************************
* Condorcet efficiency (rk) *
*********************************
w (reminder) = 0
<BLANKLINE>
condorcet_winner_rk_ctb = 0
w_is_condorcet_winner_rk_ctb = True
w_is_not_condorcet_winner_rk_ctb = False
w_missed_condorcet_winner_rk_ctb = False
<BLANKLINE>
condorcet_winner_rk = 0
w_is_condorcet_winner_rk = True
w_is_not_condorcet_winner_rk = False
w_missed_condorcet_winner_rk = False
<BLANKLINE>
***************************************
* Condorcet efficiency (relative) *
***************************************
w (reminder) = 0
<BLANKLINE>
condorcet_winner_ut_rel_ctb = 0
w_is_condorcet_winner_ut_rel_ctb = True
w_is_not_condorcet_winner_ut_rel_ctb = False
w_missed_condorcet_winner_ut_rel_ctb = False
<BLANKLINE>
condorcet_winner_ut_rel = 0
w_is_condorcet_winner_ut_rel = True
w_is_not_condorcet_winner_ut_rel = False
w_missed_condorcet_winner_ut_rel = False
<BLANKLINE>
***************************************
* Condorcet efficiency (absolute) *
***************************************
w (reminder) = 0
<BLANKLINE>
condorcet_admissible_candidates =
[ True False False]
w_is_condorcet_admissible = True
w_is_not_condorcet_admissible = False
w_missed_condorcet_admissible = False
<BLANKLINE>
weak_condorcet_winners =
[ True False False]
w_is_weak_condorcet_winner = True
w_is_not_weak_condorcet_winner = False
w_missed_weak_condorcet_winner = False
<BLANKLINE>
condorcet_winner_ut_abs_ctb = 0
w_is_condorcet_winner_ut_abs_ctb = True
w_is_not_condorcet_winner_ut_abs_ctb = False
w_missed_condorcet_winner_ut_abs_ctb = False
<BLANKLINE>
condorcet_winner_ut_abs = 0
w_is_condorcet_winner_ut_abs = True
w_is_not_condorcet_winner_ut_abs = False
w_missed_condorcet_winner_ut_abs = False
<BLANKLINE>
resistant_condorcet_winner = nan
w_is_resistant_condorcet_winner = False
w_is_not_resistant_condorcet_winner = True
w_missed_resistant_condorcet_winner = False
>>> rule.demo_manipulation_(log_depth=0) # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE>
*****************************
* *
* Election Manipulation *
* *
*****************************
<BLANKLINE>
*********************************************
* Basic properties of the voting system *
*********************************************
with_two_candidates_reduces_to_plurality = True
is_based_on_rk = True
is_based_on_ut_minus1_1 = False
meets_iia = False
<BLANKLINE>
****************************************************
* Manipulation properties of the voting system *
****************************************************
Condorcet_c_ut_rel_ctb (False) ==> Condorcet_c_ut_rel (False)
|| ||
|| Condorcet_c_rk_ctb (False) ==> Condorcet_c_rk (True) ||
|| || || || || ||
V V || || V V
Condorcet_c_ut_abs_ctb (False) ==> Condorcet_ut_abs_c (True)
|| || || ||
|| V V ||
|| maj_fav_c_rk_ctb (True) ==> maj_fav_c_rk (True) ||
|| || || ||
V V V V
majority_favorite_c_ut_ctb (True) ==> majority_favorite_c_ut (True)
|| ||
V V
IgnMC_c_ctb (True) ==> IgnMC_c (True)
|| ||
V V
InfMC_c_ctb (True) ==> InfMC_c (True)
<BLANKLINE>
*****************************************************
* Independence of Irrelevant Alternatives (IIA) *
*****************************************************
w (reminder) = 0
is_iia = True
log_iia: iia_subset_maximum_size = 2.0
example_winner_iia = nan
example_subset_iia = nan
<BLANKLINE>
**********************
* c-Manipulators *
**********************
w (reminder) = 0
preferences_ut (reminder) =
[[ 0. -0.5 -1. ]
[ 1. -1. 0.5]
[ 0.5 0.5 -0.5]
[ 0.5 0. 1. ]
[-1. -1. 1. ]]
v_wants_to_help_c =
[[False False False]
[False False False]
[False False False]
[False False True]
[False False True]]
<BLANKLINE>
************************************
* Individual Manipulation (IM) *
************************************
is_im = nan
log_im: im_option = lazy
candidates_im =
[ 0. 0. nan]
<BLANKLINE>
*********************************
* Trivial Manipulation (TM) *
*********************************
is_tm = False
log_tm: tm_option = exact
candidates_tm =
[0. 0. 0.]
<BLANKLINE>
********************************
* Unison Manipulation (UM) *
********************************
is_um = nan
log_um: um_option = lazy
candidates_um =
[ 0. 0. nan]
<BLANKLINE>
*********************************************
* Ignorant-Coalition Manipulation (ICM) *
*********************************************
is_icm = False
log_icm: icm_option = exact
candidates_icm =
[0. 0. 0.]
necessary_coalition_size_icm =
[0. 6. 4.]
sufficient_coalition_size_icm =
[0. 6. 4.]
<BLANKLINE>
***********************************
* Coalition Manipulation (CM) *
***********************************
is_cm = nan
log_cm: cm_option = fast, um_option = lazy, tm_option = exact
candidates_cm =
[ 0. 0. nan]
necessary_coalition_size_cm =
[0. 1. 2.]
sufficient_coalition_size_cm =
[0. 2. 4.]
"""
full_name = 'Woodall'
abbreviation = 'Woo'
options_parameters = Rule.options_parameters.copy()
options_parameters.update({
'cm_option': {'allowed': ['fast', 'slow', 'very_slow', 'exact'], 'default': 'fast'},
'tm_option': {'allowed': ['exact'], 'default': 'exact'},
'icm_option': {'allowed': ['exact'], 'default': 'exact'}
})
def __init__(self, **kwargs):
super().__init__(
with_two_candidates_reduces_to_plurality=True, is_based_on_rk=True,
precheck_icm=False,
log_identity="WOODALL", **kwargs
)
def __call__(self, profile):
"""
>>> my_profile = Profile(preferences_rk=[[0, 1, 2], [0, 1, 2]])
>>> rule = RuleWoodall(cm_option='slow')(my_profile)
>>> rule.irv_.cm_option
'slow'
>>> rule = RuleWoodall(cm_option='exact')(my_profile)
>>> rule.irv_.cm_option
'exact'
"""
self.delete_cache(suffix='_')
self.profile_ = profile
# Grab the IRV ballot of the profile (or create it)
irv_options = {}
if self.cm_option == 'fast':
irv_options['cm_option'] = 'fast'
elif self.cm_option == 'slow':
irv_options['cm_option'] = 'slow'
else: # self.cm_option in {'very_slow', 'exact'}:
irv_options['cm_option'] = 'exact'
self.irv_ = RuleIRV(**irv_options)(self.profile_)
return self
# %% Counting the ballots
@cached_property
def w_(self):
"""
>>> profile = Profile(preferences_ut=[
... [3, 0, 1, 2],
... [0, 1, 2, 3],
... [0, 3, 2, 1],
... [0, 3, 2, 1],
... [3, 0, 2, 1],
... [1, 0, 2, 3],
... ], preferences_rk=[
... [0, 3, 2, 1],
... [3, 2, 1, 0],
... [1, 2, 3, 0],
... [1, 2, 3, 0],
... [0, 2, 3, 1],
... [3, 2, 0, 1],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.w_
3
"""
self.mylog("Count ballots", 1)
if self.profile_.exists_condorcet_winner_rk:
return self.profile_.condorcet_winner_rk
elif self.irv_.w_ in self.profile_.smith_set_rk:
# Included in the following case, but faster
return self.irv_.w_
else:
return int(next(c for c in self.irv_.candidates_by_scores_best_to_worst_
if c in self.profile_.smith_set_rk))
@cached_property
def scores_(self):
self.mylog("Compute scores", 1)
smith_set = self.profile_.smith_set_rk
scores_smith = [(1 if c in smith_set else 0) for c in range(self.profile_.n_c)]
scores_irv = sorted(range(self.profile_.n_c), key=self.irv_.elimination_path_.__getitem__)
return np.array([scores_smith, scores_irv])
@cached_property
def candidates_by_scores_best_to_worst_(self):
self.mylog("Compute candidates_by_scores_best_to_worst", 1)
return sorted(
range(self.profile_.n_c),
key=lambda c: list(self.scores_[:, c]),
reverse=True
)
# TO DO: implement v_might_im_for_c_
# %% Manipulation criteria of the voting system
@cached_property
def meets_majority_favorite_c_rk_ctb(self):
return True
@cached_property
def meets_condorcet_c_rk(self):
return True
# %% Individual manipulation (IM)
# TODO: should be implemented.
# %% Trivial Manipulation (TM)
# Use the general methods from class Rule.
# %% Unison manipulation (UM)
def _um_preliminary_checks_c_(self, c):
"""
>>> profile = Profile(preferences_rk=[
... [1, 0, 2],
... [1, 0, 2],
... [2, 1, 0],
... [2, 1, 0],
... ])
>>> rule = RuleWoodall(um_option='lazy', cm_option="exact")(profile)
>>> rule.is_um_
False
"""
if self.um_option not in {'fast', 'lazy'} or self.cm_option not in {'fast', 'lazy'}:
if (
self.w_ == self.profile_.condorcet_winner_rk_ctb
and not self.profile_.c_might_be_there_when_cw_is_eliminated_irv_style[c]
):
# Impossible to manipulate with n_m manipulators
self._candidates_um[c] = False
# %% Ignorant-Coalition Manipulation (ICM)
# Use the general methods from class Rule.
# %% Coalition Manipulation (CM)
def _cm_preliminary_checks_c_subclass_(self, c, optimize_bounds):
"""CM: preliminary checks for challenger ``c``.
Let us consider the case where `w` is the Condorcet winner (rk ctb) (otherwise it is easy to manipulate, at
least if utility preferences are strict). We must have `c` in the (manipulated) Smith Set, hence we must have
`w` too. Hence we must manipulate IRV so that `w` is eliminated before `c`. In other words, at some point,
`w` must be eliminated, IRV-style, at a moment where `c` is still present in the election.
Examples
--------
>>> profile = Profile(preferences_rk=[
... [2, 1, 0],
... [2, 1, 0],
... [0, 1, 2],
... [2, 0, 1],
... ])
>>> rule = RuleWoodall(cm_option='exact')(profile)
>>> rule.is_cm_c_with_bounds_(1)
(False, 3.0, 3.0)
"""
if self.cm_option not in {'fast', 'lazy'}:
if self.w_ == self.profile_.condorcet_winner_rk_ctb:
self._update_necessary(
self._necessary_coalition_size_cm, c,
self.profile_.necessary_coalition_size_to_break_irv_immunity[c],
'CM: Preliminary checks: IRV-Immunity => \n necessary_coalition_size_cm[c] = '
)
@cached_property
def losing_candidates_(self):
"""If ``irv_.w_ does not win, then we put her first. Other losers are sorted as usual. (scores in
``matrix_duels_ut``).
Examples
--------
>>> profile = Profile(preferences_rk=[
... [3, 2, 0, 1],
... [0, 2, 3, 1],
... [1, 3, 2, 0],
... ])
>>> rule = RuleWoodall()(profile)
>>> list(rule.losing_candidates_)
[0, 1, 2]
"""
self.mylog("Compute ordered list of losing candidates", 1)
if self.w_ == self.irv_.w_:
# As usual
losing_candidates = np.concatenate((
np.array(range(0, self.w_), dtype=int), np.array(range(self.w_ + 1, self.profile_.n_c), dtype=int)))
losing_candidates = losing_candidates[np.argsort(
- self.profile_.matrix_duels_ut[losing_candidates, self.w_], kind='mergesort')]
else:
# Put irv_.w_ first.
losing_candidates = np.array(
[c for c in range(self.profile_.n_c) if c != self.w_ and c != self.irv_.w_]).astype(int)
losing_candidates = losing_candidates[np.argsort(
- self.profile_.matrix_duels_ut[losing_candidates, self.w_], kind='mergesort')]
losing_candidates = np.concatenate(([self.irv_.w_], losing_candidates))
return [int(c) for c in losing_candidates]
def _cm_aux_(self, c, ballots_m, preferences_rk_s):
profile_test = Profile(preferences_rk=np.concatenate((preferences_rk_s, ballots_m)), sort_voters=False)
if profile_test.n_v != self.profile_.n_v: # pragma: no cover
raise AssertionError('Uh-oh!')
winner_test = self.__class__()(profile_test).w_
return winner_test == c
def _cm_main_work_c_(self, c, optimize_bounds):
"""
>>> profile = Profile(preferences_rk=[
... [1, 0, 2],
... [1, 0, 2],
... [2, 0, 1],
... [0, 1, 2],
... ])
>>> rule = RuleWoodall(cm_option='exact')(profile)
>>> rule.candidates_cm_
array([0., 0., 0.])
>>> profile = Profile(preferences_rk=[
... [1, 2, 0],
... [0, 2, 1],
... [1, 2, 0],
... [1, 0, 2],
... ])
>>> rule = RuleWoodall(cm_option='slow', um_option = 'lazy')(profile)
>>> rule.necessary_coalition_size_cm_
array([3., 0., 3.])
>>> profile = Profile(preferences_ut=[
... [-0.5, 0. , -0.5, -0.5],
... [-1. , 1. , 1. , 0.5],
... [ 0. , 1. , -1. , 1. ],
... [ 0. , 0.5, 0.5, 1. ],
... [-1. , 0. , 0.5, 0. ],
... ], preferences_rk=[
... [1, 2, 3, 0],
... [1, 2, 3, 0],
... [3, 1, 0, 2],
... [3, 2, 1, 0],
... [2, 1, 3, 0],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.candidates_cm_
array([ 0., 0., nan, 0.])
>>> profile = Profile(preferences_ut=[
... [-0.5, 0. , 0.5, 1. ],
... [-0.5, -0.5, 0. , -1. ],
... [ 0. , 0. , 1. , -1. ],
... [ 1. , 1. , -1. , -1. ],
... [ 1. , -0.5, 1. , 1. ],
... [ 0.5, 0. , 0.5, 0.5],
... ], preferences_rk=[
... [3, 2, 1, 0],
... [2, 1, 0, 3],
... [2, 1, 0, 3],
... [1, 0, 3, 2],
... [3, 0, 2, 1],
... [0, 2, 3, 1],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.is_cm_c_with_bounds_(1)
(True, 0.0, 1.0)
>>> profile = Profile(preferences_ut=[
... [ 0.5, 0. , 0. ],
... [ 0.5, 1. , 1. ],
... [-1. , -1. , -0.5],
... ], preferences_rk=[
... [0, 2, 1],
... [1, 2, 0],
... [2, 0, 1],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.is_cm_c_with_bounds_(0)
(True, 0.0, 1.0)
>>> profile = Profile(preferences_ut=[
... [ 0. , 0.5, -0.5, 0. ],
... [-0.5, -0.5, -0.5, 1. ],
... [ 1. , -1. , 0. , -0.5],
... [ 0. , 0. , -0.5, 0. ],
... [-0.5, -0.5, 1. , 0. ],
... ], preferences_rk=[
... [1, 3, 0, 2],
... [3, 2, 0, 1],
... [0, 2, 3, 1],
... [0, 1, 3, 2],
... [2, 3, 1, 0],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.is_cm_c_(1)
nan
>>> profile = Profile(preferences_ut=[
... [ 1. , 0. , 0.5, -0.5],
... [-1. , 1. , 1. , 0.5],
... [ 0. , 1. , -1. , -0.5],
... [ 0.5, 1. , 0. , 1. ],
... [ 1. , -1. , -0.5, 1. ],
... [ 0. , 0.5, 0.5, 0.5],
... ], preferences_rk=[
... [0, 2, 1, 3],
... [2, 1, 3, 0],
... [1, 0, 3, 2],
... [3, 1, 0, 2],
... [3, 0, 2, 1],
... [3, 1, 2, 0],
... ])
>>> rule = RuleWoodall()(profile)
>>> rule.necessary_coalition_size_cm_
array([0., 0., 0., 1.])
"""
n_m = self.profile_.matrix_duels_ut[c, self.w_]
n_s = self.profile_.n_v - n_m
candidates = np.array(range(self.profile_.n_c))
preferences_borda_s = self.profile_.preferences_borda_rk[np.logical_not(self.v_wants_to_help_c_[:, c]), :]
preferences_rk_s = self.profile_.preferences_rk[np.logical_not(self.v_wants_to_help_c_[:, c]), :]
matrix_duels_vtb_temp = (preferences_ut_to_matrix_duels_ut(preferences_borda_s))
self.mylogm("CM: matrix_duels_vtb_temp =", matrix_duels_vtb_temp, 3)
# More preliminary checks. It's more convenient to put them in that method, because we need
# ``preferences_borda_s`` and ``matrix_duels_vtb_temp``.
d_neq_c = (np.array(range(self.profile_.n_c)) != c)
# Prevent another cond. Look at the weakest duel for ``d``, she has ``matrix_duels_vtb_temp[d, e]``. We simply
# need that:
# ``matrix_duels_vtb_temp[d, e] <= (n_s + n_m) / 2``
# ``2 * max_d(min_e(matrix_duels_vtb_temp[d, e])) - n_s <= n_m``
n_manip_prevent_cond = 0
for d in candidates[d_neq_c]:
e_neq_d = (np.array(range(self.profile_.n_c)) != d)
n_prevent_d = np.maximum(2 * np.min(matrix_duels_vtb_temp[d, e_neq_d]) - n_s, 0)
n_manip_prevent_cond = max(n_manip_prevent_cond, n_prevent_d)
self.mylogv("CM: n_manip_prevent_cond =", n_manip_prevent_cond, 3)
self._update_necessary(self._necessary_coalition_size_cm, c, n_manip_prevent_cond,
'CM: Update necessary_coalition_size_cm[c] = n_manip_prevent_cond =')
if not optimize_bounds and self._necessary_coalition_size_cm[c] > n_m:
return True
# Let us work
if self.w_ == self.irv_.w_:
self.mylog('CM: c != self.irv_.w == self.w', 3)
if self.cm_option == "fast":
self.irv_.cm_option = "fast"
elif self.cm_option == "slow":
self.irv_.cm_option = "slow"
else:
self.irv_.cm_option = "exact"
irv_is_cm_c = self.irv_.is_cm_c_(c)
if equal_true(irv_is_cm_c):
# Use IRV without bounds
self.mylog('CM: Use IRV without bounds')
suggested_path_one = self.irv_.example_path_cm_c_(c)
self.mylogv("CM: suggested_path =", suggested_path_one, 3)
ballots_m = self.irv_.example_ballots_cm_c_(c)
manipulation_found = self._cm_aux_(c, ballots_m, preferences_rk_s)
self.mylogv("CM: manipulation_found =", manipulation_found, 3)
if manipulation_found:
self._update_sufficient(self._sufficient_coalition_size_cm, c, n_m,
'CM: Update sufficient_coalition_size_cm[c] = n_m =')
# We will not do better with any algorithm (even the brute force algo).
return False
# Use IRV with bounds
self.irv_.is_cm_c_with_bounds_(c)
self.mylog('CM: Use IRV with bounds')
suggested_path_two = self.irv_.example_path_cm_c_(c)
self.mylogv("CM: suggested_path =", suggested_path_two, 3)
if np.array_equal(suggested_path_one, suggested_path_two):
self.mylog('CM: Same suggested path as before, skip computation')
else: # pragma: no cover
# TO DO: Investigate whether this case can actually happen.
self._reached_uncovered_code()
ballots_m = self.irv_.example_ballots_cm_c_(c)
manipulation_found = self._cm_aux_(c, ballots_m, preferences_rk_s)
self.mylogv("CM: manipulation_found =", manipulation_found, 3)
if manipulation_found:
self._update_sufficient(self._sufficient_coalition_size_cm, c, n_m,
'CM: Update sufficient_coalition_size_cm[c] = n_m =')
# We will not do better with any algorithm (even the brute force algo).
return False
else: # self.w_ != self.irv_.w_:
if c == self.irv_.w_:
self.mylog('CM: c == self.irv_.w != self._w', 3)
suggested_path = self.irv_.elimination_path_
self.mylogv("CM: suggested_path =", suggested_path, 3)
ballots_m = self.irv_.example_ballots_cm_w_against_(w_other_rule=self.w_)
manipulation_found = self._cm_aux_(c, ballots_m, preferences_rk_s)
self.mylogv("CM: manipulation_found =", manipulation_found, 3)
if manipulation_found:
self._update_sufficient(self._sufficient_coalition_size_cm, c, n_m,
'CM: Update sufficient_coalition_size_cm[c] = n_m =')
# We will not do better with any algorithm (even the brute force algo).
return
else:
self.mylog('CM: c, self.irv_.w_ and self.w_ are all distinct', 3)
if self.cm_option == 'exact':
return self._cm_main_work_c_exact_(c, optimize_bounds)