1+ #include < cassert>
2+ #include < iostream>
3+ #include < stdexcept>
4+
5+ #include " disqset.h"
6+ #include " max_elements/smith.h"
7+
8+ // Recursively test the indirect disqualification condition for
9+ // chain_members.rbegin() ~> leaf:
10+ // Call the members last and leaf.
11+ // Go through each subelection, given by the membership set S:
12+ // If root is not in S, but both last and leaf are in it:
13+ // If fp{last} <= 1/|S|, then return (last doesn't disqualify
14+ // leaf).
15+ // If the whole chain_members list, *and* leaf is in S,
16+ // If the sum of first prefs of the chain_members list, does
17+ // not exceed k/|S|, then return (last doesn't
18+ // disqualify leaf through the root), where k is the
19+ // length of that list.
20+ // If we have gone through every applicable subelection without
21+ // aborting, then:
22+ // Set disqualified[leaf], and add leaf to the chain.
23+ // Recurse for every non-chain member.
24+ // Remove leaf from the chain and return.
25+
26+ // The chain must start with the root.
27+
28+ void idisqualif_set::explore_paths (
29+ const std::vector<bool > & hopefuls,
30+ const subelections & se,
31+ std::vector<size_t > & chain_members,
32+ std::vector<bool > & chain_members_bool,
33+ size_t leaf, std::vector<bool > & disqualified) const {
34+
35+ if (chain_members.empty ()) {
36+ throw std::invalid_argument (
37+ " explore_paths: Chain must contain root!" );
38+ }
39+
40+ size_t root = chain_members[0 ],
41+ last = *chain_members.rbegin ();
42+ size_t num_subelections = se.hopeful_power_set .size ();
43+
44+ // TODO: Check that this also verifies pairwise.
45+
46+ for (size_t se_idx = 0 ; se_idx < num_subelections; ++se_idx) {
47+ // If leaf isn't in it, then it's not interesting.
48+ if (!se.hopeful_power_set [se_idx][leaf]) {
49+ continue ;
50+ }
51+
52+ /* if (se.num_remaining_candidates[se_idx] == 2) {
53+ std::cout << "debug, pairwise included" << std::endl;
54+ }*/
55+
56+ // If last and leaf are in it but root isn't, check
57+ // ordinary disqualification.
58+
59+ if (se.hopeful_power_set [se_idx][last] &&
60+ !se.hopeful_power_set [se_idx][root]) {
61+ // fp(last)_S <= numvoters/|S|, or for numerical stability,
62+ // |S| * fp(last)_S <= numvoters
63+ if (se.num_remaining_candidates [se_idx] *
64+ se.first_pref_scores [se_idx][last] <=
65+ se.num_remaining_voters [se_idx]) {
66+ // last doesn't disqualify leaf.
67+ return ;
68+ }
69+ }
70+
71+ // Skip if not everybody in the current chain is in the
72+ // subelection.
73+ bool all_members = true ;
74+ double combined_first_prefs = 0 ;
75+ for (size_t chain_cand : chain_members) {
76+ all_members &= se.hopeful_power_set [se_idx][chain_cand];
77+ combined_first_prefs += se.first_pref_scores [se_idx][chain_cand];
78+
79+ if (!all_members) {
80+ continue ;
81+ }
82+ }
83+ if (!all_members) {
84+ continue ;
85+ }
86+
87+ // If we fail to disqualify leaf through the root, abort.
88+ // combined_first_prefs <= numvoters * chain_members.size() / |S|
89+ // i.e. |S| * cfs <= numvoters * chain_members.size()
90+ if (se.num_remaining_candidates [se_idx] *
91+ combined_first_prefs <=
92+ se.num_remaining_voters [se_idx] * chain_members.size ()) {
93+ // last doesn't disqualify leaf.
94+ return ;
95+ }
96+ }
97+
98+ // Last does disqualify leaf!
99+
100+ // Set disqualification and add the leaf to the chain.
101+ disqualified[leaf] = true ;
102+ chain_members.push_back (leaf);
103+ assert (!chain_members_bool[leaf]);
104+ chain_members_bool[leaf] = true ;
105+
106+ // Recurse into every hopeful non-chain member.
107+ for (size_t candidate_leaf = 0 ; candidate_leaf < hopefuls.size ();
108+ ++candidate_leaf) {
109+
110+ if (!hopefuls[candidate_leaf] ||
111+ chain_members_bool[candidate_leaf]) {
112+ continue ;
113+ }
114+
115+ explore_paths (hopefuls, se, chain_members,
116+ chain_members_bool, candidate_leaf,
117+ disqualified);
118+ }
119+
120+ // Remove the leaf member from the chain so that whoever is
121+ // calling us can try another branch.
122+ chain_members.pop_back ();
123+ assert (chain_members_bool[leaf]);
124+ chain_members_bool[leaf] = false ;
125+ }
126+
127+ std::vector<std::vector<bool > > idisqualif_set::explore_all_paths (
128+ const election_t & papers,
129+ const std::vector<bool > & hopefuls) const {
130+
131+ size_t numcands = hopefuls.size ();
132+
133+ subelections se;
134+ se.count_subelections (papers, hopefuls, true );
135+
136+ std::vector<std::vector<bool > > disqualification_matrix (
137+ numcands, std::vector<bool >(numcands, false ));
138+
139+ for (size_t root = 0 ; root < numcands; ++root) {
140+ if (!hopefuls[root]) {
141+ continue ;
142+ }
143+ for (size_t leaf = 0 ; leaf < numcands; ++leaf) {
144+ if (!hopefuls[leaf] || root == leaf) {
145+ continue ;
146+ }
147+
148+ std::vector<size_t > chain_members = {root};
149+ std::vector<bool > chain_members_bool (numcands, false );
150+ chain_members_bool[root] = true ;
151+
152+ explore_paths (hopefuls, se, chain_members,
153+ chain_members_bool, leaf,
154+ disqualification_matrix[root]);
155+ }
156+ }
157+
158+ return disqualification_matrix;
159+ }
160+
161+ // Standard DFS search to detect a cycle.
162+
163+ bool idisqualif_set::has_cycle (
164+ const std::vector<std::vector<bool > > & disqualifications,
165+ std::vector<bool > & visited,
166+ size_t cur_cand) const {
167+
168+ size_t numcands = disqualifications.size ();
169+
170+ visited[cur_cand] = true ;
171+
172+ for (size_t next_candidate = 0 ; next_candidate < numcands;
173+ ++next_candidate) {
174+ // Skip if there's no edge to next_candidate.
175+ if (!disqualifications[cur_cand][next_candidate]) {
176+ continue ;
177+ }
178+
179+ if (visited[next_candidate]) {
180+ return true ;
181+ } else {
182+ return has_cycle (disqualifications,
183+ visited, next_candidate);
184+ }
185+ }
186+
187+ return false ;
188+ }
189+
190+ bool idisqualif_set::has_cycle (
191+ const std::vector<std::vector<bool > > &
192+ disqualifications) const {
193+
194+ size_t numcands = disqualifications.size ();
195+
196+ for (size_t candidate = 0 ; candidate < numcands; ++candidate) {
197+ std::vector<bool > visited (numcands, false );
198+
199+ if (has_cycle (disqualifications, visited, candidate)) {
200+ return true ;
201+ }
202+ }
203+
204+ return false ;
205+ }
206+
207+ void idisqualif_set::print (const std::vector<std::vector<bool > > &
208+ disqualifications) const {
209+
210+ size_t numcands = disqualifications.size ();
211+
212+ for (size_t i = 0 ; i < numcands; ++i) {
213+ for (size_t j = 0 ; j < numcands; ++j) {
214+ if (disqualifications[i][j]) {
215+ std::cout << (char )(' A' + i) << " disqualifies "
216+ << (char )(' A' +j) << " \n " ;
217+ }
218+ }
219+ }
220+ }
221+
222+ std::pair<ordering, bool > idisqualif_set::elect_inner (
223+ const election_t & papers,
224+ const std::vector<bool > & hopefuls,
225+ int num_candidates, cache_map * cache, bool winner_only) const {
226+
227+ std::vector<std::vector<bool > > disq_matrix = explore_all_paths (
228+ papers, hopefuls);
229+
230+ if (has_cycle (disq_matrix)) {
231+ print (disq_matrix);
232+ throw std::runtime_error (" idisqualif_set: Yowza! Cycle detected!" );
233+ }
234+
235+ size_t numcands = hopefuls.size (); // handle sign-compare warning
236+
237+ // Turn the set vector into an ordering and return.
238+ ordering inner_set;
239+
240+ for (size_t cand = 0 ; cand < numcands; ++cand) {
241+ if (!hopefuls[cand]) {
242+ continue ;
243+ }
244+ size_t defeated_by = 0 ;
245+
246+ for (size_t i = 0 ; i < numcands; ++i) {
247+ if (!hopefuls[i] || i == cand) {
248+ continue ;
249+ }
250+ if (disq_matrix[i][cand]) {
251+ ++defeated_by;
252+ }
253+ }
254+
255+ inner_set.insert (candscore (cand, numcands-defeated_by));
256+ }
257+
258+ return std::pair<ordering, bool >(inner_set, false );
259+
260+ /*
261+ // TODO, don't make it count, just do a blank matrix.
262+ condmat matrix(papers, num_candidates, CM_PAIRWISE_OPP);
263+
264+ for (size_t incumbent = 0; incumbent < numcands; ++incumbent) {
265+ for (size_t challenger = 0; challenger < numcands; ++challenger) {
266+ matrix.set(incumbent, challenger, 0);
267+ if (!hopefuls[incumbent]) { continue; }
268+ if (!hopefuls[challenger] || incumbent == challenger) { continue; }
269+
270+ if (disq_matrix[incumbent][challenger]) {
271+ matrix.set(incumbent, challenger, 1);
272+ if (disq_matrix[challenger][incumbent]) {
273+ throw std::runtime_error("Yowza! Cycle detected!");
274+ }
275+ }
276+ }
277+ }
278+
279+ return smith_set().pair_elect(matrix, hopefuls,
280+ cache, winner_only);
281+ */
282+ }
0 commit comments