void DefaultCandidateSetScorer::Score(Model *model, CandidateSet &candidates, bool training) { // N.B.: We assume there is at least one candidate! CandidateSet::iterator it = candidates.begin(); // Score first candidate, which is perforce the best candidate so far. model->ScoreCandidate(*(*it), training); CandidateSet::iterator best_it = it; CandidateSet::iterator gold_it = it; ++it; // Score any and all remaining candidates. for ( ; it != candidates.end(); ++it) { Candidate &candidate = *(*it); model->ScoreCandidate(candidate, training); if (model->score_comparator()->Compare(*model, candidate, **best_it) > 0) { best_it = it; } if (model->gold_comparator()->Compare(*model, candidate, **gold_it) > 0) { gold_it = it; } } candidates.set_best_scoring_index((*best_it)->index()); candidates.set_gold_index((*gold_it)->index()); }
CandidateSet CandidateSet::getCandidateTrees(double score) { CandidateSet res; for (CandidateSet::iterator it = begin(); it != end(); it++) { if (abs(it->first - score) < 0.1) { res.insert(*it); } } return res; }
CandidateSet CandidateSet::getBestCandidateTrees(int numTrees) { CandidateSet res; if (numTrees >= size() || numTrees == 0) numTrees = (int) size(); for (reverse_iterator rit = rbegin(); rit != rend() && numTrees > 0; rit++, numTrees--) { res.insert(*rit); } return res; }
StepCode EliminateNakedPairsStep::runOnHouse(House &cell_list) { bool modified = false; std::set<Mask> found_masks; std::set<Mask> duplicate_masks; for (const Cell *cell : cell_list) { if (cell->candidates.none()) { return {true, modified}; } if (cell->candidates.count() != 2) { continue; } const Mask mask = cell->candidates.to_ulong(); if (found_masks.count(mask)) { duplicate_masks.insert(cell->candidates.to_ulong()); } found_masks.insert(mask); } for (auto &mask : duplicate_masks) { for (Cell *cell : cell_list) { if (cell->isFixed()) { continue; } CandidateSet *candidates = &cell->candidates; if (candidates->to_ulong() == mask) { continue; } auto new_cands = CandidateSet(candidates->to_ulong() & ~mask); if (*candidates == new_cands) { continue; } if (DEBUG) { const Mask intersection = candidates->to_ulong() & mask; dbgs() << "Naked Pair " << printCandidateString(mask) << " removes " << printCandidateString(intersection) << " from " << cell->coord << "\n"; } modified = true; changed.insert(cell); *candidates = new_cands; } } return {false, modified}; }
void PerceptronModel::ComputeFeaturesToUpdate(const CandidateSet &example, unordered_set<int> & gold_features_to_update, unordered_set<int> & best_scoring_features_to_update) const { // Collect gold features that are not in best-scoring candidate. const FeatureVector<int,double> &gold_features = example.GetGold().features(); const FeatureVector<int,double> &best_scoring_features = example.GetBestScoring().features(); if (DEBUG >= 2) { cerr << "Gold index: " << example.gold_index() << "; best scoring index: " << example.best_scoring_index() << endl; cerr << "Original gold features: " << gold_features << endl << "Original best scoring features: " << best_scoring_features << endl; } gold_features.GetNonZeroFeatures(gold_features_to_update); best_scoring_features.RemoveEqualFeatures(gold_features, gold_features_to_update); if (DEBUG >= 2) { cerr << "Time:" << time_.to_string() << ": new gold features: ["; for (unordered_set<int>::const_iterator it = gold_features_to_update.begin(); it != gold_features_to_update.end(); ++it) { cerr << " " << *it; } cerr << "]" << endl; } best_scoring_features.GetNonZeroFeatures(best_scoring_features_to_update); gold_features.RemoveEqualFeatures(best_scoring_features, best_scoring_features_to_update); if (DEBUG >= 2) { cerr << "Time:" << time_.to_string() << ": new best scoring features: ["; for (unordered_set<int>::const_iterator it = best_scoring_features_to_update.begin(); it != best_scoring_features_to_update.end(); ++it) { cerr << " " << *it; } cerr << "]" << endl; } }
//Print the message followed by all the entries in the CandidateTally, in // the order specified by has_higher_priority: i is printed before j, if // has_higher_priority(i,j) returns true: sometimes alphabetically by candidate, // other times by decreasing votes for the candidate. //Use a "->" to separate the candidate name from the number of votes they // received. void print_tally(std::string message, const CandidateTally& tally, bool (*has_higher_priority)(const TallyEntry& i,const TallyEntry& j)) { CandidateSet candidateSet; for (const auto& kv : tally) { candidateSet.insert(kv.first); } TallyEntryPQ tallyPQ(*has_higher_priority); tallyPQ.enqueue_all(tally); std::cout << message << ": still in election = " << candidateSet << std::endl; for (const TallyEntry& tallyEntry : tallyPQ) { std::cout << " " << tallyEntry.first << " -> " << tallyEntry.second << std::endl; } std::cout << std::endl; }
//Prompt the user for a file, create a voter preference Map, and print it. //Determine the Set of all the candidates in the election, from this Map. //Repeatedly evaluate the ballot based on the candidates (still) in the // election, printing the vote count (tally) two ways: with the candidates // (a) shown alphabetically increasing and (b) shown with the vote count // decreasing (candidates with equal vote counts are shown alphabetically // increasing); from this tally, compute which candidates remain in the // election: all candidates receiving more than the minimum number of votes; // continue this process until there are less than 2 candidates. //Print the final result: there may 1 candidate left (the winner) or 0 left // (no winner). int main() { try { std::ifstream text_file; ics::safe_open(text_file,"Enter the name of a file with voter preferences","votepref1.txt"); std::cout << std::endl; const Preferences prefs = read_voter_preferences(text_file); print_voter_preferences(prefs); std::cout << std::endl; CandidateSet candidateSet; for (const auto& kv : prefs) { for (const std::string& candidate : kv.second) { candidateSet.insert(candidate); } } CandidateTally tally = evaluate_ballot(prefs, candidateSet); int ballot = 1; while (candidateSet.size() > 1) { std::stringstream ss; ss << ballot++; print_tally("Vote count on ballot #" + ss.str() + " with candidates in alphabetical order", tally, alphabetic_gt); print_tally("Vote count on ballot #" + ss.str() + " with candidates in numerical order", tally, numeric_gt); candidateSet = remaining_candidates(tally); tally = evaluate_ballot(prefs, candidateSet); } if (candidateSet.size() == 0) { std::cout << "No winner" << std::endl; } else { std::string winner; for (const std::string& candidate : candidateSet) { winner += candidate + " and "; } std::cout << "Winner is " << winner.substr(0, winner.length()-5) << std::endl; } } catch (ics::IcsError& e) { std::cout << e.what() << std::endl; } return 0; }
void PerceptronModel::DefaultUpdater::Update(Model *m, CandidateSet &example) { PerceptronModel *model = dynamic_cast<PerceptronModel *>(m); ++(model->num_updates_); unordered_set<int> gold_features; unordered_set<int> best_scoring_features; model->ComputeFeaturesToUpdate(example, gold_features, best_scoring_features); model->models_.UpdateGoldAndCandidateFeatureAverages(model->time_, gold_features, best_scoring_features); double step_size = model->ComputeStepSize(gold_features, best_scoring_features, example); // Finally, update perceptrons. if (DEBUG >= 2) { cerr << "Updating weights for gold features ["; for (unordered_set<int>::const_iterator it = gold_features.begin(); it != gold_features.end(); ++it) { cerr << " " << *it; } cerr << "] from\n\t" << example.GetGold() << endl; cerr << "Updating weights for best scoring features ["; for (unordered_set<int>::const_iterator it = best_scoring_features.begin(); it != best_scoring_features.end(); ++it) { cerr << " " << *it; } cerr << "] from\n\t" << example.GetBestScoring() << endl; } double positive_step = step_size; model->models_.UpdateWeights(model->time_, gold_features, example.GetGold().features(), positive_step); double negative_step = -step_size; model->models_.UpdateWeights(model->time_, best_scoring_features, example.GetBestScoring().features(), negative_step); if (DEBUG >=2) { cerr << "Raw model: " << model->models_.GetModel(true) << endl; cerr << "Avg model: " << model->models_.GetModel(false) << endl; } }
//Return the Set of candidates who are still in the election, based on the // tally of votes: compute the minimum number of votes and return a Set of // all candidates receiving more than that minimum; if all candidates // receive the same number of votes (that would be the minimum), the empty // Set is returned. CandidateSet remaining_candidates(const CandidateTally& tally) { CandidateSet result; if (!tally.empty()) { int min = -1; for (const TallyEntry& entry : tally) { if (min == -1 || entry.second < min) { min = entry.second; } } for (const TallyEntry& entry : tally) { if (entry.second > min) { result.insert(entry.first); } } } return result; }
void RandomPairCandidateSetScorer::Score(Model *model, CandidateSet &candidates, bool training) { // First, pick two candidate indices at random. size_t idx1 = GetRandomIndex(candidates.size()); size_t idx2 = GetRandomIndex(candidates.size()); Candidate &c1 = candidates.Get(idx1); Candidate &c2 = candidates.Get(idx2); // Next, just score those two candidates. model->ScoreCandidate(c1, training); model->ScoreCandidate(c2, training); // Finally, set indices of best scoring and gold amongst just those two. int score_cmp = model->score_comparator()->Compare(*model, c1, c2); candidates.set_best_scoring_index(score_cmp > 0 ? c1.index() : c2.index()); int gold_cmp = model->gold_comparator()->Compare(*model, c1, c2); candidates.set_gold_index(gold_cmp > 0 ? c1.index() : c2.index()); }
//Return the CandidateTally: a Map of candidates (as keys) and the number of // votes they received, based on the unchanging Preferences (read from the // file) and the candidates who are currently still in the election (which changes). //Every possible candidate should appear as a key in the resulting tally. //Each voter should tally one vote: for their highest-ranked candidate who is // still in the the election. CandidateTally evaluate_ballot(const Preferences& preferences, const CandidateSet& candidates) { CandidateTally tally; for (const PreferencesEntry& entry : preferences) { for (const std::string& candidate : entry.second) { if (candidates.contains(candidate)) { ++tally[candidate]; break; } } } return tally; }
void PerceptronModel::TrainOnExample(CandidateSet &example) { time_.Tick(); if (symbols_ != NULL) { example.CompileFeatures(symbols_); } bool training = true; ScoreCandidates(example, training); if (NeedToUpdate(example)) { if (DEBUG >= 2) { cerr << "Time:" << time_.to_string() << ": need to update because " << "best scoring index " << example.best_scoring_index() << " is not equal to gold index " << example.gold_index() << endl; } ++(*num_training_errors_per_epoch_.rbegin()); ++num_training_errors_; Update(example); } }
bool PerceptronModel::DefaultUpdatePredicate::NeedToUpdate(Model *model, CandidateSet &example) { return example.best_scoring_index() != example.gold_index(); }
StepCode EliminateNakedTriplesStep::runOnHouse(House &house) { bool modified = false; if (house.size() <= 3) { return {false, modified}; } std::vector<std::pair<Mask, std::vector<const Cell *>>> found_masks; for (const Cell *cell : house) { std::size_t num_candidates = cell->candidates.count(); if (num_candidates == 0) { return {true, modified}; } if (num_candidates != 2 && num_candidates != 3) { continue; } const Mask mask = cell->candidates.to_ulong(); bool found_match = false; for (unsigned i = 0; i < found_masks.size(); ++i) { const Mask m = found_masks[i].first; // ORing the two masks together will switch on all candidates found in // both masks. If it's less than three, then we're still a triple // candidate. // TODO: If the current mask is size 2 and the OR of the two is 3, then // we should create two masks: one for the two and one for the OR. // Otherwise you could get 1/2 match with 1/2/3 and 1/2/4. // For now, only accept found masks of size 3. Easy naked triples. if (bitCount(m) == 3 && bitCount(mask | m) == 3) { found_match = true; found_masks[i].second.push_back(cell); } } if (!found_match) { std::pair<Mask, std::vector<const Cell *>> pair; pair.first = mask; pair.second.push_back(cell); found_masks.push_back(pair); } } for (auto &pair : found_masks) { if (pair.second.size() != 3) { continue; } auto &matches = pair.second; const Mask mask = pair.first; for (Cell *cell : house) { if (cell->isFixed()) { continue; } if (std::find(matches.begin(), matches.end(), cell) != matches.end()) { continue; } CandidateSet *candidates = &cell->candidates; if (candidates->to_ulong() == mask) { continue; } auto new_cands = CandidateSet(candidates->to_ulong() & ~mask); if (*candidates == new_cands) { continue; } if (DEBUG) { const Mask intersection = candidates->to_ulong() & mask; dbgs() << "Naked Triple " << printCandidateString(mask) << " removes " << printCandidateString(intersection) << " from " << cell->coord << "\n"; } modified = true; changed.insert(cell); *candidates = new_cands; } } return {false, modified}; }