[Aptitude-svn-commit] r4230 - in branches/aptitude-0.3/aptitude: .
src/generic/problemresolver
Daniel Burrows
dburrows at costa.debian.org
Sat Sep 24 15:43:59 UTC 2005
Author: dburrows
Date: Sat Sep 24 15:43:55 2005
New Revision: 4230
Modified:
branches/aptitude-0.3/aptitude/ChangeLog
branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
Log:
Fix the resolver indentation.
Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog (original)
+++ branches/aptitude-0.3/aptitude/ChangeLog Sat Sep 24 15:43:55 2005
@@ -1,3 +1,9 @@
+2005-09-24 Daniel Burrows <dburrows at debian.org>
+
+ * src/generic/problemresolver/problemresolver.h:
+
+ Fix the resolver indentation (finally).
+
2005-09-24 Rubén Porras <nahoo at inicia.es>
* Update spanish translation
Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h (original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h Sat Sep 24 15:43:55 2005
@@ -289,1678 +289,1678 @@
}
};
- // Information regarding the weight given to various parameters;
- // packaged up in a struct so it can be easily used by the solution
- // constructors.
- solution_weights weights;
-
- /** Solutions whose score is smaller than this value will be
- * discarded rather than being enqueued.
- */
- int minimum_score;
-
- /** The "maximum" number of successors to generate for any given
- * node. Note, however, that a full set of successors will always
- * be generated for each broken dependency, so this is not exact.
- *
- * The theoretical justification is that in order to cover all
- * solutions, it's sufficient to generate all successors for a
- * single broken dependency. This avoids the problem that when a
- * large number of packages are broken, or when a single package
- * version with a large number of reverse dependencies is broken,
- * you can end up searching way too many successor nodes, even if
- * all the successors are discarded outright.
- *
- * Unfortunately, only generating one successor has its own
- * problem: solutions might be generated out-of-order (i.e., worse
- * solutions before better). This variable allows you to seek a
- * "happy medium" by generating a reasonable number of successors,
- * but not too many. If a single package breaks a large number of
- * other packages, then any attempt to fix those packages should
- * generate the correct successor (which will then pop to the top
- * of the queue and likely stay there), while if a large number of
- * unrelated packages are broken, it doesn't matter which successor
- * goes first.
- *
- * Note that in practice, turning this up is very likely to result
- * in dreadful performance; the option to do so may very well be
- * removed in the future.
- */
- unsigned int max_successors;
-
- /** The universe in which we are solving problems. */
- const PackageUniverse universe;
-
- /** If \b true, we have exhausted the list of solutions. */
- bool finished:1;
-
- /** If \b true, it is possible that some deferred solutions are
- * no longer "forbidden".
- */
- bool deferred_dirty:1;
-
- /** If \b true, debugging messages will be sent to stdout. */
- bool debug:1;
-
- /** If \b true, so-called "stupid" pairs of actions will be
- * eliminated from solutions. (see paper)
- */
- bool remove_stupid:1;
-
-
- // Multithreading support variables.
- //
- // These variables ensure (as a sanity-check) that only one thread
- // executes the solver function at once, and allow the executing
- // instance to be cleanly terminated. They are managed by the
- // instance_tracker class (see above).
-
- /** If \b true, a thread is currently executing in the solver. */
- bool solver_executing : 1;
-
- /** If \b true, the currently executing thread should stop at the
- * next opportunity.
- */
- bool solver_cancelled : 1;
-
- /** Mutex guarding the solver_executing and stop_solver variables. */
- threads::mutex execution_mutex;
-
-
-
- queue_counts counts;
-
- /** Mutex guarding the cache of resolver status information. */
- threads::mutex counts_mutex;
-
-
-
- /** The working queue: */
- std::priority_queue<solution, std::vector<solution>, solution_goodness_compare> open;
-
- /** Stores already-seen solutions: */
- std::set<solution, solution_contents_compare> closed;
-
- /** Stores solutions that were ignored because of user constraints
- * (but could be reinstated later). Disjoint with closed.
- */
- std::set<solution, solution_contents_compare> deferred;
-
- typedef dense_mapset<package, action, ExtractPackageId> conflictset;
-
- /** Stores conflicts: sets of installations that have been
- * determined to be mutually incompatible.
- */
- conflictset conflicts;
-
- /** The initial set of broken dependencies. Kept here for use in
- * the stupid-elimination algorithm.
- */
- imm::set<dep> initial_broken;
-
- /** Stores versions that have been rejected by the user; distinct
- * from the per-solution reject sets that track changes on a single
- * inference path.
- */
- std::set<version> user_rejected;
-
- /** Stores versions that have been mandated by the user; we should
- * always pick these versions if the chance arises.
- */
- std::set<version> user_mandated;
-
- /** Stores dependencies that have been "hardened" by the user (that
- * aren't allowed to default).
- */
- std::set<dep> user_hardened;
-
- /** Stores generated solutions: */
- std::vector<solution> generated_solutions;
-
-
- typedef std::set<std::pair<version, version> > stupid_table;
-
- std::ostream &dump_conflict(std::ostream &out, const imm::map<package, action> &conflict) const;
- std::ostream &dump_conflict(std::ostream &out, const action &act) const;
-
-
- /** \param conflictorpair a (package, action) pair contained in a conflict.
- * \param apair a (package, action) pair from a solution or a conflict.
- *
- * \return \b true if the given conflictor matches a. This relation
- * is transitive, so if c1 matches c2 and c2 matches a,
- * then c1 matches a.
- */
- static bool conflictor_matches(const std::pair<package, action> &conflictorpair,
- const std::pair<package, action> &apair)
- {
- const action &conflictor = conflictorpair.second;
- const action &a = apair.second;
-
- if(a.ver != conflictor.ver)
- return false;
-
- if(!conflictor.from_dep_source)
- return true;
- else
- return a.from_dep_source && a.d == conflictor.d;
- }
-
- /** \return \b true if each element of pc2 is matched by an element
- * in pc1.
- */
- static bool conflict_matches(const typename imm::map<package, action> &c,
- const typename imm::map<package, action> &acts)
- {
- typename imm::map<package, action>::const_iterator ci = c.begin();
- typename imm::map<package, action>::const_iterator ai = acts.begin();
-
- while(ci != c.end() &&
- ai != acts.end())
- {
- if(ai->first < ci->first)
- ++ai;
- else if(ci->first < ai->first)
- return false;
- else if(!(conflictor_matches(ci->second, ai->second)))
- return false;
- else
- {
- ++ci;
- ++ai;
- }
- }
-
- return (ci == c.end());
- }
-
- /** Test whether the given partial conflict subsumes an existing
- * conflict.
- *
- * \param m the conflict or action to match
- *
- * \return a conflict matched by m, or conflicts.end() if no such
- * conflict exists.
- */
- typename conflictset::const_iterator
- find_matching_conflict(const imm::map<package, action> &m) const
- {
- return conflicts.find_submap(m, &conflictor_matches);
- }
-
- /** Test whether the given partial conflict subsumes an existing
- * conflict.
- *
- * \param m the conflict or action to match
- * \param actpair a single element that should be present in the
- * returned conflict. For instance, if you have a
- * solution that you know is conflict-free and you
- * perform a single action, passing this action
- * as actpair may speed up the search for a
- * conflict.
- *
- * \return a conflict matched by m, or conflicts.end() if no such
- * conflict exists.
- */
- typename conflictset::const_iterator
- find_matching_conflict(const imm::map<package, action> &m,
- const std::pair<package, action> &actpair) const
- {
- return conflicts.find_submap_containing(m, actpair, &conflictor_matches);
- }
-
- /** Test whether the given solution contains a conflict. */
- bool contains_conflict(const solution &s) const
- {
- typename conflictset::const_iterator
- found = find_matching_conflict(s.get_actions());
- bool rval = (found != conflicts.end());
-
- if(debug && rval)
- {
- std::cout << "The conflict ";
- dump_conflict(std::cout, *found);
- std::cout << " is triggered by the solution ";
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
- return rval;
- }
-
- /** Add the given conflict to the set of conflicts. Tests if the
- * conflict is matched by an existing conflict (in which case it's
- * redundant and will be dropped), then tests whether any existing
- * conflicts will be made redundant by this conflict.
- */
- void add_conflict(const imm::map<package, action> &conflict)
- {
- typename conflictset::const_iterator
- found = find_matching_conflict(conflict);
-
- if(found != conflicts.end())
- {
- if(debug)
- {
- std::cout << "Dropping conflict ";
- dump_conflict(std::cout, conflict);
- std::cout << " because it is redundant with ";
- dump_conflict(std::cout, *found);
- std::cout << std::endl;
- }
- }
- else
- // TODO: drop conflicts of which this is a subset. Needs work
- // at the setset level.
- conflicts.insert(conflict);
- }
-
- #if 0
- /** Test whether the given solution contains a conflict when the
- * given action is taken.
- *
- * \return such a conflict if it exists; otherwise conflicts.end().
- */
- typename std::set<std::map<package, act_conflict> >::const_iterator
- will_conflict(const solution &s,
- const act_conflict &a) const
- {
- // For now I'm being lazy and actually generating a full mapping.
- // However, this could be done much more efficiently if optimizing
- // this code is important.
- std::map<package, act_conflict> m;
-
- populate_partial_conflict(s, m);
-
- package p = a.ver.get_package();
-
- m[p] = a;
-
- typename std::set<std::map<package, act_conflict> >::const_iterator
- result = subsumes_any_conflict(m);
-
- if(debug && result != conflicts.end())
- {
- std::cout << "Adding " << p.get_name() << " "
- << a.ver.get_name();
-
- if(a.from_dep_source)
- std::cout << " due to " << a.d;
-
- std::cout << " will trigger conflict ";
- dump(std::cout, *result);
- std::cout << std::endl;
- }
-
- return result;
- }
-
- /** Generate a table listing the "stupid" pairs of actions in a
- * solution. If (a,b) is in the table, then b solves the
- * dependency that triggered a's installation.
- */
- void initialize_stupid_pairs(const solution &s,
- stupid_table &table)
- {
- for(typename std::map<package, action>::const_iterator i = s.get_actions().begin();
- i != s.get_actions().end(); ++i)
- {
- const dep &d = i->second.d;
-
- for(typename dep::solver_iterator j = d.solvers_begin();
- !j.end(); ++j)
- {
- // Don't claim that (v,v) is a stupid pair.
- if(*j == i->second.ver)
- continue;
-
- typename std::map<package, action>::const_iterator found
- = s.get_actions().find((*j).get_package());
-
- // Check whether j will be installed; if so, insert it.
- if(found != s.get_actions().end() &&
- found->second.ver == *j)
- table.insert(std::pair<version, version>(i->second.ver, *j));
- }
- }
- }
-
- /** A solution wrapper suitable for use with broken_under() that
- * drops one mapping from the solution.
- */
- class drop_package
- {
- solution s;
- package drop;
- public:
- drop_package(const solution &_s, const package &_drop)
- :s(_s), drop(_drop)
- {
- }
-
- version version_of(const package &p) const
- {
- if(p == drop)
- return p.current_version();
- else
- return s.version_of(p);
- }
- };
-
- /** An action iterator wrapper that replaces the action on a given
- * package with the given action.
- */
- class swap_action
- {
- typename std::map<package, action>::const_iterator i;
- package replace;
- action with;
- public:
- swap_action(const typename std::map<package, action>::const_iterator &_i,
- const package &_replace, const action &_with)
- : i(_i), replace(_replace), with(_with)
- {
- }
-
- bool operator==(const swap_action &other) const
- {
- return i == other.i && replace == other.replace && with.ver == other.with.ver;
- }
-
- bool operator!=(const swap_action &other) const
- {
- return i != other.i || replace != other.replace || with.ver != other.with.ver;
- }
-
- swap_action &operator++()
- {
- ++i;
- return *this;
- }
-
- const action &operator*() const
- {
- if(i->first == replace)
- return with;
- else
- return i->second;
- }
-
- const action *operator->() const
- {
- if(i->first == replace)
- return &with;
- else
- return &i->second;
- }
- };
-
- /** Check whether dropping a version from a solution breaks
- * dependencies.
- *
- * \param s the "starting" solution; should not have broken deps
- * \param v the version to drop
- * \param d output parameter; if there is a broken dependency,
- * it will be placed in this location.
- *
- * \return \b true if a broken dependency exists.
- */
- bool broken_by_drop(const solution &s,
- const version &v,
- dep &d)
- {
- // Don't flag an error if a dep that's SUPPOSED to be unresolved
- // gets broken.
- const std::set<dep> &ignore_deps = s.get_unresolved_soft_deps();
-
- drop_package dropped(s, v.get_package());
-
- version curr = v.get_package().current_version();
- for(typename version::revdep_iterator i = curr.revdeps_begin();
- !i.end(); ++i)
- if(ignore_deps.find(*i) == ignore_deps.end() &&
- (*i).broken_under(dropped))
- {
- d = *i;
- return true;
- }
-
- for(typename version::revdep_iterator i = curr.revdeps_begin();
- !i.end(); ++i)
- if(ignore_deps.find(*i) == ignore_deps.end() &&
- (*i).broken_under(dropped))
- {
- d = *i;
- return true;
- }
-
- for(typename version::dep_iterator i = curr.deps_begin();
- !i.end(); ++i)
- if(ignore_deps.find(*i) == ignore_deps.end() &&
- (*i).broken_under(dropped))
- {
- d = *i;
- return true;
- }
-
- return false;
- }
-
- /** Represents an empty solution. */
- class null_solution
- {
- public:
- version version_of(const package &p) const
- {
- return p.current_version();
- }
- };
-
- /** Represents a partial solution based directly (by reference) on a
- * reference to a map.
- */
- class map_solution
- {
- const std::map<package, action> &actions;
- public:
- map_solution(const std::map<package, action> &_actions)
- : actions(_actions)
- {
- }
-
- version version_of(const package &p) const
- {
- typename std::map<package, action>::const_iterator found = actions.find(p);
-
- if(found == actions.end())
- return p.current_version();
- else
- return found->second.ver;
- }
- };
-
- /** Represents a possible successor to the given solution, created
- * by installing a single 'new' version.
- */
- template<typename SolutionType>
- class partial_solution
- {
- /** The solution this is based on. */
- const SolutionType &s;
- /** More stuff to install */
- std::map<package, version> installations;
- public:
- /** Initialize a new solution.
- */
- partial_solution (const SolutionType &_s)
- :s(_s)
- {
- }
-
- /** Takes a range of versions and installs them in order. */
- template<typename InputIterator>
- void install(const InputIterator &begin, const InputIterator &end)
- {
- for(InputIterator i = begin; i != end; ++i)
- install(i->get_package(), *i);
- }
-
- void install(const package &p, const version &v)
- {
- assert(installations.find(p) == installations.end());
-
- installations[p]=v;
- }
-
- /** \return the currently installed version of the given
- * package.
- */
- version version_of(const package &pkg) const
- {
- typename std::map<package, version>::const_iterator found=installations.find(pkg);
-
- if(found!=installations.end())
- return found->second;
- else
- return s.version_of(pkg);
- }
-
- /** Test whether the given version is installed in this solution.
- */
- bool ver_installed(const version &test_ver) const
- {
- return version_of(test_ver.get_package()) == test_ver;
- }
- };
-
- /** Wrap a solver list by generating pairs where the second value
- * is a bound constant value.
- */
- class forbid_iter_builder
- {
- typename dep::solver_iterator i;
- dep d;
- public:
- forbid_iter_builder(const typename dep::solver_iterator &_i,
- const dep &_d)
- :i(_i), d(_d)
- {
- }
-
- typename std::pair<version, dep> operator*() const
- {
- return std::pair<version, dep>(*i, d);
- }
-
- forbid_iter_builder &operator++()
- {
- ++i;
- return *this;
- }
-
- bool end() const
- {
- return i.end();
- }
- };
-
- /** Wrap an iterator over pairs to an iterator over the second
- * element of the pair.
- */
- template<typename Iter>
- class project_iter_2nd_impl
- {
- Iter i;
- public:
- project_iter_2nd_impl(const Iter &_i)
- :i(_i)
- {
- }
-
- typename Iter::value_type::second_type &operator*() const
- {
- return i->second;
- }
-
- typename Iter::value_type::second_type *operator->() const
- {
- return &i->second;
- }
-
- project_iter_2nd_impl &operator++()
- {
- ++i;
- return *this;
- }
-
- bool operator==(const project_iter_2nd_impl &other) const
- {
- return i == other.i;
- }
-
- bool operator!=(const project_iter_2nd_impl &other) const
- {
- return i != other.i;
- }
- };
-
- template<typename Iter>
- project_iter_2nd_impl<Iter> project_iter_2nd(const Iter &i)
- {
- return project_iter_2nd_impl<Iter>(i);
- }
-
- /** Wrap an action iterator to select its version element. */
- template<class Iter>
- class project_ver_impl
- {
- Iter i;
- public:
- project_ver_impl(const Iter &_i)
- :i(_i)
- {
- }
-
- bool operator==(const project_ver_impl &other)
- {
- return i == other.i;
- }
-
- bool operator!=(const project_ver_impl &other)
- {
- return i != other.i;
- }
-
- project_ver_impl &operator++()
- {
- ++i;
- return *this;
- }
-
- const version &operator*() const
- {
- return i->ver;
- }
-
- const version *operator->() const
- {
- return &i->ver;
- }
- };
-
- template<typename Iter>
- project_ver_impl<Iter> project_ver(const Iter &i)
- {
- return project_ver_impl<Iter>(i);
- }
-
-
- /** Convert a [begin,end) pair to a single APT-style iterator.
- */
- template<class iter>
- class apt_iter_wrapper_impl
- {
- iter curr, the_end;
- public:
- apt_iter_wrapper_impl(const iter &_begin, const iter &_end)
- :curr(_begin), the_end(_end)
- {
- }
-
- bool end() const
- {
- return curr==the_end;
- }
-
- apt_iter_wrapper_impl &operator++()
- {
- ++curr;
- return *this;
- }
-
- const typename std::iterator_traits<iter>::reference operator*() const
- {
- return *curr;
- }
-
- const typename std::iterator_traits<iter>::reference operator->() const
- {
- return &*curr;
- }
- };
-
- template<class Iter>
- apt_iter_wrapper_impl<Iter> apt_iter_wrapper(const Iter &begin,
- const Iter &end)
- {
- return apt_iter_wrapper_impl<Iter>(begin, end);
- }
-
-
- /** Given a list of actions, remove "obviously" unnecessary actions;
- * that is, actions which are not inspired by any broken
- * dependency. Corresponds to ResolveFrom, but is a bit more
- * extreme in its approach. As usual, no effort is made to avoid
- * stupid pairs.
- *
- * \param actions the set of actions to apply
- *
- * \param unresolved_soft_deps a set of broken dependencies which
- * are permitted (indeed, required?)
- * to be broken at the end of the process.
- *
- * \param forbidden_iter an iterator over the list of versions that
- * should be forbidden in the new solution.
- *
- * \return a solution containing only the actions which appear to
- * be necessary.
- */
- template<typename forbid_iter>
- solution filter_unnecessary_actions(const std::vector<action> &actions,
- const std::set<dep> &unresolved_soft_deps,
- const forbid_iter &forbidden_iter)
- {
- // Versions from which to choose.
- std::set<version> avail_versions(project_ver(actions.begin()),
- project_ver(actions.end()));
-
- // Currently broken deps.
- std::set<dep> broken_deps = initial_broken;
-
- // The solution being built.
- std::map<package, action> output_actions;
-
- // Score of this solution
- int action_score = 0;
-
- while(!broken_deps.empty())
- {
- assert(!avail_versions.empty());
-
- // Pick an "arbitrary" broken dependency.
- dep d = *broken_deps.begin();
- broken_deps.erase(broken_deps.begin());
-
- // If it's to be ignored, just drop it on the floor.
- if(unresolved_soft_deps.find(d) != unresolved_soft_deps.end())
- continue;
-
- // Now, pick an "arbitrary" resolver of this dep.
- //
- // (maybe pick the best-scored one that's available instead?)
- bool found_one = false;
- version solver;
- for(typename dep::solver_iterator si = d.solvers_begin();
- !si.end(); ++si)
- {
- typename std::set<version>::const_iterator found
- = avail_versions.find(*si);
-
- if(found != avail_versions.end())
- {
- found_one = true;
- solver = *si;
- break;
- }
- }
-
- // I have a proof that a solver should exist, but that doesn't
- // mean it does ;-)
- assert(found_one);
- assert(output_actions.find(solver.get_package()) == output_actions.end());
- assert(solver != solver.get_package().current_version());
-
- if(debug)
- std::cout << "Filter: resolving " << d << " with "
- << solver.get_package().get_name() << ":"
- << solver.get_name() << std::endl;
-
- action act(solver, d, output_actions.size());
-
- // Update the set of broken dependencies. FIXME: it should be
- // possible to do this in-place, maybe using immutable sets?
- std::set<dep> new_broken;
- update_broken(partial_solution<map_solution>(output_actions),
- project_ver(&act),
- project_ver((&act)+1),
- unresolved_soft_deps,
- broken_deps,
- new_broken);
-
- // Expensive copy.
- broken_deps = new_broken;
-
- // Finally, update the old solution with the new action.
- output_actions[solver.get_package()] = act;
- action_score += step_score;
- action_score += version_scores[solver.get_id()];
- action_score -= version_scores[solver.get_package().current_version().get_id()];
- }
-
- // By definition we have a solution now.
- int score = action_score + full_solution_score + unfixed_soft_score * unresolved_soft_deps.size();
-
- return solution(project_iter_2nd(output_actions.begin()),
- project_iter_2nd(output_actions.end()),
- unresolved_soft_deps,
- broken_deps,
- forbidden_iter,
- score,
- action_score);
- }
-
- /** Eliminate stupid pairs from the given solution.
- *
- * \param s the solution from which stupid pairs should be removed.
- *
- * \return the new solution
- */
- solution eliminate_stupid(const solution &s)
- {
- assert(s.get_broken().empty());
-
- stupid_table stupid_pairs;
-
- initialize_stupid_pairs(s, stupid_pairs);
-
- solution rval = s;
-
- bool contained_stupid = !stupid_pairs.empty();
-
- while(!stupid_pairs.empty())
- {
- if(debug)
- {
- std::cout << "Eliminating stupid pairs from ";
- rval.dump(std::cout);
- std::cout << std::endl;
- }
-
- // The pair to eliminate; picked (sorta) arbitrarily.
- const typename std::pair<version, version> &victim = *stupid_pairs.begin();
- stupid_pairs.erase(stupid_pairs.begin());
-
- // This is where we discard pairs that are no longer relevant
- // because one of the elements of the pair has been dropped.
- if(rval.get_actions().find(victim.first.get_package()) == rval.get_actions().end() ||
- rval.get_actions().find(victim.second.get_package()) == rval.get_actions().end())
- {
- if(debug)
- std::cout << "Dropping invalidated stupid pair("
- << victim.first.get_package().get_name()
- << ":" << victim.first.get_name() << ","
- << victim.second.get_package().get_name()
- << ":" << victim.second.get_name() << ")"
- << std::endl;
-
- continue;
- }
-
- if(debug)
- std::cout << "Examining stupid pair ("
- << victim.first.get_package().get_name()
- << ":" << victim.first.get_name() << ","
- << victim.second.get_package().get_name()
- << ":" << victim.second.get_name() << ")" << std::endl;
-
- // Suppose we drop the second element in favor of the first.
- // Will that produce a valid solution?
- dep first_broken;
-
- // If something was broken, then we need to use the fallback
- // position: just change the dependency given as the "reason"
- // for the first action.
- if(broken_by_drop(rval, victim.first, first_broken))
- {
- if(debug)
- std::cout << "Unable to drop "
- << victim.first.get_package().get_name()
- << ":" << victim.first.get_name()
- << ", changing justification to "
- << first_broken << std::endl;
-
- typename std::map<package, action>::const_iterator found = rval.get_actions().find(victim.first.get_package());
- assert(found != rval.get_actions().end());
-
- action repl_action(victim.first, first_broken,
- found->second.id);
-
- // Note that the score is perfectly correct, as it depends
- // only on the set of versions in rval (which doesn't
- // change). The set of forbidden versions is in some
- // sense "wrong", but if we have a final solution this
- // shouldn't be a problem.
-
- swap_action swbegin(rval.get_actions().begin(),
- victim.first.get_package(),
- repl_action);
- swap_action swend(rval.get_actions().end(),
- victim.first.get_package(),
- repl_action);
-
- rval = solution(swbegin, swend,
- rval.get_unresolved_soft_deps(),
- rval.get_broken(),
- apt_iter_wrapper(rval.get_forbidden_versions().begin(),
- rval.get_forbidden_versions().end()),
- rval.get_score(), rval.get_action_score());
- }
- else
- {
- if(debug)
- std::cout << "Dropping "
- << victim.first.get_package().get_name()
- << ":" << victim.first.get_name()
- << " and filtering unnecessary installations."
- << std::endl;
- // Ok, it's safe.
- //
- // Generate a node by dropping the second element and
- // changing the version of the first:
- std::vector<action> actions;
-
- for(typename std::map<package, action>::const_iterator i =
- s.get_actions().begin(); i != s.get_actions().end(); ++i)
- {
- if(i->second.ver == victim.first)
- actions.push_back(action(victim.second, i->second.d,
- i->second.id));
- else if(i->second.ver != victim.second)
- actions.push_back(i->second);
- }
-
- rval = filter_unnecessary_actions(actions,
- rval.get_unresolved_soft_deps(),
- apt_iter_wrapper(rval.get_forbidden_versions().begin(),
- rval.get_forbidden_versions().end()));
- }
- }
-
- if(debug)
- {
- if(contained_stupid)
- {
- std::cout << "Done eliminating stupid pairs, result is ";
- rval.dump(std::cout);
- std::cout << std::endl;
- }
- else
- {
- std::cout << "No stupid pairs in ";
- rval.dump(std::cout);
- std::cout << std::endl;
- }
- }
-
- assert(rval.get_broken().empty());
-
- return rval;
- }
- #endif
-
- solution eliminate_stupid(const solution &s) const
- {
- if(debug)
- std::cout << "Would eliminate stupid, but stupid elimination is disabled." << std::endl;
-
- return s;
- }
-
- /** Calculate whether the solution is rejected based on
- * user_rejected by testing whether the intersection of the
- * solution domain and the rejected set is nonempty.
- */
- bool contains_rejected(const solution &s) const
- {
- for(typename std::set<version>::const_iterator uri
- = user_rejected.begin(); uri != user_rejected.end(); ++uri)
- {
- typename imm::map<package, action>::node found = s.get_actions().lookup(uri->get_package());
-
- if(found.isValid() && found.getVal().second.ver == *uri)
- {
- if(debug)
- std::cout << "Rejected version " << found.getVal().first.get_name()
- << " " << found.getVal().second.ver.get_name()
- << " detected." << std::endl;
-
- return true;
- }
- }
-
- return false;
- }
-
- bool breaks_hardened(const solution &s) const
- {
- typename std::set<dep>::const_iterator uh_iter
- = user_hardened.begin();
-
- if(uh_iter == user_hardened.end())
- return false;
-
- typename imm::set<dep>::const_iterator su_iter
- = s.get_unresolved_soft_deps().begin();
-
- while(uh_iter != user_hardened.end() &&
- su_iter != s.get_unresolved_soft_deps().end())
- {
- if(*uh_iter == *su_iter)
- {
- if(debug)
- std::cout << "Broken hardened dependency " << *uh_iter
- << " detected." << std::endl;
-
- return true;
- }
- else if(*uh_iter < *su_iter)
- ++uh_iter;
- else
- ++su_iter;
- }
-
- return false;
- }
-
- /** \return \b true if the given solution passed up an opportunity
- * to include an 'mandated' version.
- */
- bool avoids_mandated(const solution &s) const
- {
- // NB: The current algorithm is not terribly efficient.
- for(typename std::set<version>::const_iterator ai = user_mandated.begin();
- ai != user_mandated.end(); ++ai)
- {
- version v = s.version_of(ai->get_package());
- // If it's already being installed, then we're fine.
- if(v == *ai)
- continue;
-
- // Check (very slowly) whether we made a decision where we had
- // the opportunity to use this version (and of course didn't).
- for(typename imm::map<package, action>::const_iterator si = s.get_actions().begin();
- si != s.get_actions().end(); ++si)
- if(si->second.d.solved_by(*ai) &&
- user_mandated.find(si->second.ver) == user_mandated.end())
- {
- if(debug)
- {
- std::cout << ai->get_package().get_name() << " version " << ai->get_name() << " is avoided (when resolving " << si->second.d << ") by the solution:" << std::endl;
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
- return true;
- }
- }
-
- return false;
- }
-
- /** \return \b true if the resolution of the given dependency might
- * be affected by a user constraint.
- */
- bool impinges_user_constraint(const dep &d) const
- {
- if(user_hardened.find(d) != user_hardened.end())
- return true;
-
- for(typename dep::solver_iterator si = d.solvers_begin();
- !si.end(); ++si)
- {
- if(user_rejected.find(*si) != user_rejected.end())
- return true;
-
- for(typename std::set<version>::iterator mi = user_mandated.begin();
- mi != user_mandated.end(); ++mi)
- {
- if(mi->get_package() == (*si).get_package())
- return true;
- }
- }
-
- return false;
- }
-
-
- /** \return \b true if the given solution should be deferred. */
- bool should_defer(const solution &s) const
- {
- return contains_rejected(s) || breaks_hardened(s) ||
- avoids_mandated(s);
- }
-
- /** Place any solutions that were deferred and are no longer
- * rejected back on the open queue.
- */
- void reexamine_deferred()
- {
- // NB: the STL guarantees that erasing elements from a set does
- // not invalidate existing iterators. Hence this very careful
- // iteration:
-
- typename std::set<solution, solution_contents_compare>::const_iterator
- i = deferred.begin(), j = i;
-
- while(i != deferred.end())
- {
- ++j;
-
- if(!should_defer(*i))
- {
- open.push(*i);
- deferred.erase(i);
- }
-
- i = j;
- }
-
- // Note that we might have to rescind the "finished" state: the
- // actions above can actually cause new solutions to be available
- // for processing!
- if(finished && !open.empty())
- finished = false;
-
- deferred_dirty = false;
- }
-
- /** \return \b true if the given solution is "irrelevant": that is,
- * either it was already generated and placed in the closed queue,
- * or it includes an already-generated solution as a proper subset.
- */
- bool irrelevant(const solution &s)
- {
- if(closed.find(s) != closed.end())
- return true;
-
- if(contains_conflict(s))
- return true;
-
- // The efficiency of this step hinges on the *assumption* that the
- // number of solutions that will be generated is small relative to
- // the number of potential solutions.
- for(typename std::vector<solution>::const_iterator i=generated_solutions.begin();
- i!=generated_solutions.end(); ++i)
- if(std::includes(s.get_actions().begin(), s.get_actions().end(),
- i->get_actions().begin(), i->get_actions().end()))
- return true;
-
- if(s.get_score() < minimum_score)
- {
- if(debug)
- std::cout << "Not generating solution (infinite badness " << s.get_score() << "<" << minimum_score << ")" << std::endl;
- return true;
- }
-
- return false;
- }
-
- /** Tries to enqueue the given package.
- *
- * \return \b true if the solution was not irrelevant.
- */
- bool try_enqueue(const solution &s)
- {
- if(irrelevant(s))
- {
- if(debug)
- {
- std::cout << "Dropping irrelevant solution ";
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
- return false;
- }
- else if(should_defer(s))
- {
- if(debug)
- {
- std::cout << "Deferring rejected solution ";
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
- deferred.insert(s);
- return false;
- }
- else
- {
- if(debug)
- {
- std::cout << "Enqueuing ";
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
- open.push(s);
-
- return true;
- }
- }
-
- /** Try to enqueue the given collection of packages. */
- void try_enqueue(const std::vector<solution> &sols)
- {
- for(typename std::vector<solution>::const_iterator
- succi = sols.begin(); succi != sols.end(); ++succi)
- try_enqueue(*succi);
- }
-
- /** Internal routine to check for the legality of a 'move' and
- * generate a conflictor if it's not legal.
- */
- bool is_legal(const solution &s,
- const version &v,
- action &out_act) const
- {
- package p = v.get_package();
- version cur = p.current_version();
- version inst = s.version_of(p);
-
- if(inst != cur)
- {
- if(debug)
- std::cout << "Discarding " << p.get_name() << " "
- << v.get_name() << ": monotonicity violation"
- << std::endl;
-
- out_act.ver = inst;
- out_act.from_dep_source = false;
- return false;
- }
- else
- {
- assert(v != cur);
-
- typename imm::map<version, dep>::node found
- = s.get_forbidden_versions().lookup(v);
-
- if(!found.isValid())
- return true;
- else
- {
- const dep &found_d = found.getVal().second;
-
- if(debug)
- std::cout << "Discarding " << p.get_name() << " "
- << v.get_name() << ": forbidden by the resolution of "
- << found_d << std::endl;
-
- out_act.ver = s.version_of(found_d.get_source().get_package());
- out_act.d = found_d;
- out_act.from_dep_source = true;
-
- return false;
- }
- }
- }
-
- /** Internal routine to insert a new conflictor into a conflict set.
- * Handles the case in which the conflictor subsumes an existing
- * element of the set.
- */
- void insert_conflictor(imm::map<package, action> &conflict,
- const action &act) const
- {
- package p = act.ver.get_package();
- typename imm::map<package, action>::node found
- = conflict.lookup(p);
-
- if(!found.isValid())
- conflict.put(p, act);
- else
- {
- const action &found_act = found.getVal().second;
-
- action a2 = act;
-
- assert(found_act.ver == act.ver);
- if(a2.from_dep_source)
- {
- if(found_act.from_dep_source)
- assert(a2.d == found_act.d);
-
- else
- a2.from_dep_source = false;
- }
- conflict.put(p, a2);
- }
- }
-
- /** Generate a solution and push it onto an encapsulated vector of
- * solutions.
- */
- class real_generator
- {
- std::vector<solution> ⌖
- public:
- real_generator(std::vector<solution> &_target)
- :target(_target)
- {
- }
-
- template<typename a_iter, class u_iter>
- void make_successor(const solution &s,
- const a_iter &abegin, const a_iter &aend,
- const u_iter &ubegin, const u_iter &uend,
- const PackageUniverse &universe,
- const solution_weights &weights) const
- {
- target.push_back(solution::successor(s,
- abegin, aend,
- ubegin, uend,
- universe, weights));
- }
- };
-
- /** Don't actually generate solutions; instead, just count how many
- * *would* be generated. Each time a solution would be generated,
- * the integer referenced by this object is incremented (it is
- * never set to 0; if you need it to be initialized to 0, do that
- * yourself).
- */
- class null_generator
- {
- int &count;
- public:
- null_generator(int &_count)
- :count(_count)
- {
- }
-
- template<typename a_iter, class u_iter>
- void make_successor(const solution &s,
- const a_iter &abegin, const a_iter &aend,
- const u_iter &ubegin, const u_iter &uend,
- const PackageUniverse &universe,
- const solution_weights &weights) const
- {
- ++count;
- }
-
- int get_count() const
- {
- return count;
- }
- };
-
- /** Convenience routine for the below: try to generate a successor
- * by installing a single package version. NB: assumes that the
- * solution's actions have dense identifiers (i.e., less than
- * s.get_actions().size()).
- *
- * \param s the solution for which a successor should be generated
- * \param v the version to install
- * \param d the dependency for which the successor is being generated.
- * \param from_dep_source if \b true, this successor is the result
- * of an action on the source of a dependency
- * \param conflict a map to which conflictors for this version, if
- * any, will be added.
- *
- * \param generator an object supporting the make_successor() routine,
- * as real_generator and null_generator above.
- */
- template<typename SolutionGenerator>
- void generate_single_successor(const solution &s,
- const dep &d,
- const version &v,
- bool from_dep_source,
- imm::map<package, action> &conflict,
- const SolutionGenerator &generator) const
- {
- action conflictor;
-
- if(debug)
- {
- std::cout << "Trying to resolve " << d << " by installing "
- << v.get_package().get_name() << " "
- << v.get_name();
- if(from_dep_source)
- std::cout << " from the dependency source";
-
- std::cout << std::endl;
- }
-
- const int newid = s.get_actions().size();
-
- if(!is_legal(s, v, conflictor))
- insert_conflictor(conflict, conflictor);
- else
- {
- action act(v, d, from_dep_source, newid);
-
- // Speculatively form the new set of actions; doing this
- // rather than forming the whole solution allows us to avoid
- // several rather expensive steps in the successor routine
- // (most notably the formation of the new broken packages
- // set).
- imm::map<package, action> new_acts = s.get_actions();
- new_acts.put(v.get_package(), act);
-
- typename conflictset::const_iterator
- found = find_matching_conflict(new_acts,
- std::pair<package, action>(v.get_package(), act));
-
- if(found == conflicts.end())
- generator.make_successor(s, &act, &act+1,
- (dep *) 0, (dep *) 0,
- universe, weights);
- else
- {
- if(debug)
- {
- std::cout << "Discarding " << v.get_package().get_name()
- << " " << v.get_name() << " due to conflict ";
- dump_conflict(std::cout, *found);
- std::cout << std::endl;
- }
-
- imm::map<package, action> m = *found;
-
- for(typename imm::map<package, action>::const_iterator ci
- = m.begin(); ci != m.end(); ++ci)
- {
- // Discard the version that we were trying to install,
- // so that the caller can use this to form a more
- // general conflict if all its resolutions fail.
- if(ci->first != v.get_package())
- insert_conflictor(conflict, ci->second);
- else
- assert(ci->second.ver == v);
- }
- }
- }
- }
-
- /** Build the successors of a solution node for a particular
- * dependency.
- *
- * \param conflict a map which will be initialized with a conflict
- * explaining the non-appearance of some solvers (if there are no
- * solvers, this will be a full conflict explaining the lack of
- * solvers).
- *
- * \param out a vector onto which the successors should be pushed.
- */
- template<typename SolutionGenerator>
- void generate_successors(const solution &s,
- const dep &d,
- imm::map<package, action> &conflict,
- const SolutionGenerator &generator) const
- {
- version source = d.get_source();
- typename imm::map<package, action>::node
- source_found = s.get_actions().lookup(source.get_package());
-
- // Try moving the source, if it is legal to do so
- if(source_found.isValid())
- insert_conflictor(conflict, action(source, d, false, -1));
- else
- {
- assert(source == source.get_package().current_version());
-
- for(typename package::version_iterator vi = source.get_package().versions_begin();
- !vi.end(); ++vi)
- if(*vi != source)
- generate_single_successor(s, d, *vi, true, conflict, generator);
- }
-
- // Now try installing each target of the dependency.
- for(typename dep::solver_iterator si = d.solvers_begin();
- !si.end(); ++si)
- generate_single_successor(s, d, *si, false, conflict, generator);
-
- // Finally, maybe we can leave this dependency unresolved.
- if(d.is_soft())
- generator.make_successor(s, (action *) 0, (action *) 0,
- &d, &d+1, universe, weights);
- }
-
- /** Processes the given solution by enqueuing its successor nodes
- * (if any are available). Note that for clarity, we now generate
- * *all* successors before examining *any*.
- */
- void process_solution(const solution &s)
- {
- // Any forcings are immediately applied to 'curr', so that
- // forcings are performed ASAP.
- solution curr = s;
-
- assert(!s.get_broken().empty());
-
- // This loop attempts to generate all the successors of a
- // solution. However, if a "forced" dependency arises, it
- // re-verifies all the dependencies of the solution.
- bool done = false;
- while(!done)
- {
- if(debug)
- {
- std::cout << "Processing ";
- curr.dump(std::cout);
- std::cout << std::endl;
- }
-
- done = true;
-
- // Remember the solution whose broken dependencies we're
- // iterating over.
- solution starting_solution = curr;
-
- for(typename imm::set<dep>::const_iterator bi=starting_solution.get_broken().begin();
- bi!=starting_solution.get_broken().end(); ++bi)
-
- {
- // Check for the case where this dependency has been
- // fortuitously solved by forcing another broken
- // dependency.
- if(starting_solution != curr && !(*bi).broken_under(curr))
- continue;
-
- // Assert against impossible conditions (if this happens
- // something is broken elsewhere).
- if(starting_solution == curr && !(*bi).broken_under(curr))
- {
- std::cerr << "Unexpectedly non-broken dependency "
- << *bi << "!" << std::endl;
-
- version source = (*bi).get_source();
-
- if(curr.version_of(source.get_package()) != source)
- std::cerr << " (" << source.get_package().get_name()
- << " " << source.get_name()
- << " is not installed)" << std::endl;
-
- for(typename dep::solver_iterator si = (*bi).solvers_begin();
- !si.end(); ++si)
- if(curr.version_of((*si).get_package()) == *si)
- std::cerr << " (" << source.get_package().get_name()
- << " " << source.get_name()
- << " is not installed)" << std::endl;
-
- abort();
- }
-
- imm::map<package, action> conflict;
-
-
- int num_successors = 0;
- generate_successors(curr, *bi, conflict,
- null_generator(num_successors));
-
-
-
- if(num_successors == 0)
- {
- if(debug)
- {
- std::cout << "Discarding solution; unresolvable dependency "
- << *bi << " with conflict ";
-
- dump_conflict(std::cout, conflict);
-
- std::cout << std::endl;
- }
-
- add_conflict(conflict);
-
- return;
- }
- else if(num_successors == 1)
- {
- if(debug)
- std::cout << "Forced resolution of " << *bi << std::endl;
-
- std::vector<solution> v;
- real_generator g(v);
- // NB: this may do redundant work adding to 'conflict'.
- // Use a generator object to avoid that?
- generate_successors(curr, *bi, conflict,
- real_generator(v));
-
- assert(v.size() == 1);
-
- curr = v.back();
- done = false;
- }
- }
- }
-
- // In the course of forcing dependencies, we might have actually
- // arrived at a solution, in which case we should just enqueue it
- // and stop (a full solution has no successors to generate).
- if(curr.is_full_solution())
- {
- try_enqueue(curr);
- return;
- }
-
- unsigned int nsols = 0;
-
- // First try to enqueue stuff related to a dependency that the
- // user constrained; then just go for a free-for-all.
- for(typename imm::set<dep>::const_iterator bi=curr.get_broken().begin();
- bi!=curr.get_broken().end() && (nsols == 0 ||
- nsols < max_successors); ++bi)
-
- if(impinges_user_constraint(*bi))
- {
- // Is it possible to take this out somehow?
- imm::map<package, action> conflict;
-
- if(debug)
- std::cout << "Generating successors for " << *bi
- << std::endl;
-
- std::vector<solution> v;
- generate_successors(curr, *bi, conflict, real_generator(v));
- try_enqueue(v);
- nsols += v.size();
- }
-
- for(typename imm::set<dep>::const_iterator bi=curr.get_broken().begin();
- bi!=curr.get_broken().end() && (nsols == 0 ||
- nsols < max_successors); ++bi)
- {
- // Is it possible to take this out somehow?
- imm::map<package, action> conflict;
-
- if(debug)
- std::cout << "Generating successors for " << *bi
- << std::endl;
-
- std::vector<solution> v;
- generate_successors(curr, *bi, conflict, real_generator(v));
- try_enqueue(v);
- nsols += v.size();
- }
- }
- public:
-
- /** Construct a new generic_problem_resolver.
- *
- * \param _score_score the score per "step" of a (partial) solution. Typically negative.
- * \param _broken_score the score to add per broken dependency of a (partial) solution. Typically negative.
- * \param _unfixed_soft_score the score to add per soft dependency LEFT UNFIXED. Typically negative.
- * \param infinity a score value that will be considered to be "infinite". Solutions
- * with less than -infinity points will be immediately discarded.
- * \param _full_solution_score a bonus for goal nodes (things
- * that solve all dependencies)
- * \param _universe the universe in which we are working.
- */
- generic_problem_resolver(int _step_score, int _broken_score,
- int _unfixed_soft_score,
- int infinity, unsigned int _max_successors,
- int _full_solution_score,
- const PackageUniverse &_universe)
- :weights(_step_score, _broken_score, _unfixed_soft_score,
- _full_solution_score, _universe.get_version_count()),
- minimum_score(-infinity), max_successors(_max_successors),
- universe(_universe), finished(false), deferred_dirty(false),
- debug(false), remove_stupid(true),
- solver_executing(false), solver_cancelled(false),
- conflicts(_universe.get_package_count())
+ // Information regarding the weight given to various parameters;
+ // packaged up in a struct so it can be easily used by the solution
+ // constructors.
+ solution_weights weights;
+
+ /** Solutions whose score is smaller than this value will be
+ * discarded rather than being enqueued.
+ */
+ int minimum_score;
+
+ /** The "maximum" number of successors to generate for any given
+ * node. Note, however, that a full set of successors will always
+ * be generated for each broken dependency, so this is not exact.
+ *
+ * The theoretical justification is that in order to cover all
+ * solutions, it's sufficient to generate all successors for a
+ * single broken dependency. This avoids the problem that when a
+ * large number of packages are broken, or when a single package
+ * version with a large number of reverse dependencies is broken,
+ * you can end up searching way too many successor nodes, even if
+ * all the successors are discarded outright.
+ *
+ * Unfortunately, only generating one successor has its own
+ * problem: solutions might be generated out-of-order (i.e., worse
+ * solutions before better). This variable allows you to seek a
+ * "happy medium" by generating a reasonable number of successors,
+ * but not too many. If a single package breaks a large number of
+ * other packages, then any attempt to fix those packages should
+ * generate the correct successor (which will then pop to the top
+ * of the queue and likely stay there), while if a large number of
+ * unrelated packages are broken, it doesn't matter which successor
+ * goes first.
+ *
+ * Note that in practice, turning this up is very likely to result
+ * in dreadful performance; the option to do so may very well be
+ * removed in the future.
+ */
+ unsigned int max_successors;
+
+ /** The universe in which we are solving problems. */
+ const PackageUniverse universe;
+
+ /** If \b true, we have exhausted the list of solutions. */
+ bool finished:1;
+
+ /** If \b true, it is possible that some deferred solutions are
+ * no longer "forbidden".
+ */
+ bool deferred_dirty:1;
+
+ /** If \b true, debugging messages will be sent to stdout. */
+ bool debug:1;
+
+ /** If \b true, so-called "stupid" pairs of actions will be
+ * eliminated from solutions. (see paper)
+ */
+ bool remove_stupid:1;
+
+
+ // Multithreading support variables.
+ //
+ // These variables ensure (as a sanity-check) that only one thread
+ // executes the solver function at once, and allow the executing
+ // instance to be cleanly terminated. They are managed by the
+ // instance_tracker class (see above).
+
+ /** If \b true, a thread is currently executing in the solver. */
+ bool solver_executing : 1;
+
+ /** If \b true, the currently executing thread should stop at the
+ * next opportunity.
+ */
+ bool solver_cancelled : 1;
+
+ /** Mutex guarding the solver_executing and stop_solver variables. */
+ threads::mutex execution_mutex;
+
+
+
+ queue_counts counts;
+
+ /** Mutex guarding the cache of resolver status information. */
+ threads::mutex counts_mutex;
+
+
+
+ /** The working queue: */
+ std::priority_queue<solution, std::vector<solution>, solution_goodness_compare> open;
+
+ /** Stores already-seen solutions: */
+ std::set<solution, solution_contents_compare> closed;
+
+ /** Stores solutions that were ignored because of user constraints
+ * (but could be reinstated later). Disjoint with closed.
+ */
+ std::set<solution, solution_contents_compare> deferred;
+
+ typedef dense_mapset<package, action, ExtractPackageId> conflictset;
+
+ /** Stores conflicts: sets of installations that have been
+ * determined to be mutually incompatible.
+ */
+ conflictset conflicts;
+
+ /** The initial set of broken dependencies. Kept here for use in
+ * the stupid-elimination algorithm.
+ */
+ imm::set<dep> initial_broken;
+
+ /** Stores versions that have been rejected by the user; distinct
+ * from the per-solution reject sets that track changes on a single
+ * inference path.
+ */
+ std::set<version> user_rejected;
+
+ /** Stores versions that have been mandated by the user; we should
+ * always pick these versions if the chance arises.
+ */
+ std::set<version> user_mandated;
+
+ /** Stores dependencies that have been "hardened" by the user (that
+ * aren't allowed to default).
+ */
+ std::set<dep> user_hardened;
+
+ /** Stores generated solutions: */
+ std::vector<solution> generated_solutions;
+
+
+ typedef std::set<std::pair<version, version> > stupid_table;
+
+ std::ostream &dump_conflict(std::ostream &out, const imm::map<package, action> &conflict) const;
+ std::ostream &dump_conflict(std::ostream &out, const action &act) const;
+
+
+ /** \param conflictorpair a (package, action) pair contained in a conflict.
+ * \param apair a (package, action) pair from a solution or a conflict.
+ *
+ * \return \b true if the given conflictor matches a. This relation
+ * is transitive, so if c1 matches c2 and c2 matches a,
+ * then c1 matches a.
+ */
+ static bool conflictor_matches(const std::pair<package, action> &conflictorpair,
+ const std::pair<package, action> &apair)
+ {
+ const action &conflictor = conflictorpair.second;
+ const action &a = apair.second;
+
+ if(a.ver != conflictor.ver)
+ return false;
+
+ if(!conflictor.from_dep_source)
+ return true;
+ else
+ return a.from_dep_source && a.d == conflictor.d;
+ }
+
+ /** \return \b true if each element of pc2 is matched by an element
+ * in pc1.
+ */
+ static bool conflict_matches(const typename imm::map<package, action> &c,
+ const typename imm::map<package, action> &acts)
+ {
+ typename imm::map<package, action>::const_iterator ci = c.begin();
+ typename imm::map<package, action>::const_iterator ai = acts.begin();
+
+ while(ci != c.end() &&
+ ai != acts.end())
+ {
+ if(ai->first < ci->first)
+ ++ai;
+ else if(ci->first < ai->first)
+ return false;
+ else if(!(conflictor_matches(ci->second, ai->second)))
+ return false;
+ else
+ {
+ ++ci;
+ ++ai;
+ }
+ }
+
+ return (ci == c.end());
+ }
+
+ /** Test whether the given partial conflict subsumes an existing
+ * conflict.
+ *
+ * \param m the conflict or action to match
+ *
+ * \return a conflict matched by m, or conflicts.end() if no such
+ * conflict exists.
+ */
+ typename conflictset::const_iterator
+ find_matching_conflict(const imm::map<package, action> &m) const
+ {
+ return conflicts.find_submap(m, &conflictor_matches);
+ }
+
+ /** Test whether the given partial conflict subsumes an existing
+ * conflict.
+ *
+ * \param m the conflict or action to match
+ * \param actpair a single element that should be present in the
+ * returned conflict. For instance, if you have a
+ * solution that you know is conflict-free and you
+ * perform a single action, passing this action
+ * as actpair may speed up the search for a
+ * conflict.
+ *
+ * \return a conflict matched by m, or conflicts.end() if no such
+ * conflict exists.
+ */
+ typename conflictset::const_iterator
+ find_matching_conflict(const imm::map<package, action> &m,
+ const std::pair<package, action> &actpair) const
+ {
+ return conflicts.find_submap_containing(m, actpair, &conflictor_matches);
+ }
+
+ /** Test whether the given solution contains a conflict. */
+ bool contains_conflict(const solution &s) const
+ {
+ typename conflictset::const_iterator
+ found = find_matching_conflict(s.get_actions());
+ bool rval = (found != conflicts.end());
+
+ if(debug && rval)
+ {
+ std::cout << "The conflict ";
+ dump_conflict(std::cout, *found);
+ std::cout << " is triggered by the solution ";
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ return rval;
+ }
+
+ /** Add the given conflict to the set of conflicts. Tests if the
+ * conflict is matched by an existing conflict (in which case it's
+ * redundant and will be dropped), then tests whether any existing
+ * conflicts will be made redundant by this conflict.
+ */
+ void add_conflict(const imm::map<package, action> &conflict)
+ {
+ typename conflictset::const_iterator
+ found = find_matching_conflict(conflict);
+
+ if(found != conflicts.end())
+ {
+ if(debug)
+ {
+ std::cout << "Dropping conflict ";
+ dump_conflict(std::cout, conflict);
+ std::cout << " because it is redundant with ";
+ dump_conflict(std::cout, *found);
+ std::cout << std::endl;
+ }
+ }
+ else
+ // TODO: drop conflicts of which this is a subset. Needs work
+ // at the setset level.
+ conflicts.insert(conflict);
+ }
+
+#if 0
+ /** Test whether the given solution contains a conflict when the
+ * given action is taken.
+ *
+ * \return such a conflict if it exists; otherwise conflicts.end().
+ */
+ typename std::set<std::map<package, act_conflict> >::const_iterator
+ will_conflict(const solution &s,
+ const act_conflict &a) const
+ {
+ // For now I'm being lazy and actually generating a full mapping.
+ // However, this could be done much more efficiently if optimizing
+ // this code is important.
+ std::map<package, act_conflict> m;
+
+ populate_partial_conflict(s, m);
+
+ package p = a.ver.get_package();
+
+ m[p] = a;
+
+ typename std::set<std::map<package, act_conflict> >::const_iterator
+ result = subsumes_any_conflict(m);
+
+ if(debug && result != conflicts.end())
+ {
+ std::cout << "Adding " << p.get_name() << " "
+ << a.ver.get_name();
+
+ if(a.from_dep_source)
+ std::cout << " due to " << a.d;
+
+ std::cout << " will trigger conflict ";
+ dump(std::cout, *result);
+ std::cout << std::endl;
+ }
+
+ return result;
+ }
+
+ /** Generate a table listing the "stupid" pairs of actions in a
+ * solution. If (a,b) is in the table, then b solves the
+ * dependency that triggered a's installation.
+ */
+ void initialize_stupid_pairs(const solution &s,
+ stupid_table &table)
+ {
+ for(typename std::map<package, action>::const_iterator i = s.get_actions().begin();
+ i != s.get_actions().end(); ++i)
+ {
+ const dep &d = i->second.d;
+
+ for(typename dep::solver_iterator j = d.solvers_begin();
+ !j.end(); ++j)
+ {
+ // Don't claim that (v,v) is a stupid pair.
+ if(*j == i->second.ver)
+ continue;
+
+ typename std::map<package, action>::const_iterator found
+ = s.get_actions().find((*j).get_package());
+
+ // Check whether j will be installed; if so, insert it.
+ if(found != s.get_actions().end() &&
+ found->second.ver == *j)
+ table.insert(std::pair<version, version>(i->second.ver, *j));
+ }
+ }
+ }
+
+ /** A solution wrapper suitable for use with broken_under() that
+ * drops one mapping from the solution.
+ */
+ class drop_package
+ {
+ solution s;
+ package drop;
+ public:
+ drop_package(const solution &_s, const package &_drop)
+ :s(_s), drop(_drop)
+ {
+ }
+
+ version version_of(const package &p) const
+ {
+ if(p == drop)
+ return p.current_version();
+ else
+ return s.version_of(p);
+ }
+ };
+
+ /** An action iterator wrapper that replaces the action on a given
+ * package with the given action.
+ */
+ class swap_action
+ {
+ typename std::map<package, action>::const_iterator i;
+ package replace;
+ action with;
+ public:
+ swap_action(const typename std::map<package, action>::const_iterator &_i,
+ const package &_replace, const action &_with)
+ : i(_i), replace(_replace), with(_with)
+ {
+ }
+
+ bool operator==(const swap_action &other) const
+ {
+ return i == other.i && replace == other.replace && with.ver == other.with.ver;
+ }
+
+ bool operator!=(const swap_action &other) const
+ {
+ return i != other.i || replace != other.replace || with.ver != other.with.ver;
+ }
+
+ swap_action &operator++()
+ {
+ ++i;
+ return *this;
+ }
+
+ const action &operator*() const
+ {
+ if(i->first == replace)
+ return with;
+ else
+ return i->second;
+ }
+
+ const action *operator->() const
+ {
+ if(i->first == replace)
+ return &with;
+ else
+ return &i->second;
+ }
+ };
+
+ /** Check whether dropping a version from a solution breaks
+ * dependencies.
+ *
+ * \param s the "starting" solution; should not have broken deps
+ * \param v the version to drop
+ * \param d output parameter; if there is a broken dependency,
+ * it will be placed in this location.
+ *
+ * \return \b true if a broken dependency exists.
+ */
+ bool broken_by_drop(const solution &s,
+ const version &v,
+ dep &d)
+ {
+ // Don't flag an error if a dep that's SUPPOSED to be unresolved
+ // gets broken.
+ const std::set<dep> &ignore_deps = s.get_unresolved_soft_deps();
+
+ drop_package dropped(s, v.get_package());
+
+ version curr = v.get_package().current_version();
+ for(typename version::revdep_iterator i = curr.revdeps_begin();
+ !i.end(); ++i)
+ if(ignore_deps.find(*i) == ignore_deps.end() &&
+ (*i).broken_under(dropped))
+ {
+ d = *i;
+ return true;
+ }
+
+ for(typename version::revdep_iterator i = curr.revdeps_begin();
+ !i.end(); ++i)
+ if(ignore_deps.find(*i) == ignore_deps.end() &&
+ (*i).broken_under(dropped))
+ {
+ d = *i;
+ return true;
+ }
+
+ for(typename version::dep_iterator i = curr.deps_begin();
+ !i.end(); ++i)
+ if(ignore_deps.find(*i) == ignore_deps.end() &&
+ (*i).broken_under(dropped))
+ {
+ d = *i;
+ return true;
+ }
+
+ return false;
+ }
+
+ /** Represents an empty solution. */
+ class null_solution
+ {
+ public:
+ version version_of(const package &p) const
+ {
+ return p.current_version();
+ }
+ };
+
+ /** Represents a partial solution based directly (by reference) on a
+ * reference to a map.
+ */
+ class map_solution
+ {
+ const std::map<package, action> &actions;
+ public:
+ map_solution(const std::map<package, action> &_actions)
+ : actions(_actions)
+ {
+ }
+
+ version version_of(const package &p) const
+ {
+ typename std::map<package, action>::const_iterator found = actions.find(p);
+
+ if(found == actions.end())
+ return p.current_version();
+ else
+ return found->second.ver;
+ }
+ };
+
+ /** Represents a possible successor to the given solution, created
+ * by installing a single 'new' version.
+ */
+ template<typename SolutionType>
+ class partial_solution
+ {
+ /** The solution this is based on. */
+ const SolutionType &s;
+ /** More stuff to install */
+ std::map<package, version> installations;
+ public:
+ /** Initialize a new solution.
+ */
+ partial_solution (const SolutionType &_s)
+ :s(_s)
+ {
+ }
+
+ /** Takes a range of versions and installs them in order. */
+ template<typename InputIterator>
+ void install(const InputIterator &begin, const InputIterator &end)
+ {
+ for(InputIterator i = begin; i != end; ++i)
+ install(i->get_package(), *i);
+ }
+
+ void install(const package &p, const version &v)
+ {
+ assert(installations.find(p) == installations.end());
+
+ installations[p]=v;
+ }
+
+ /** \return the currently installed version of the given
+ * package.
+ */
+ version version_of(const package &pkg) const
+ {
+ typename std::map<package, version>::const_iterator found=installations.find(pkg);
+
+ if(found!=installations.end())
+ return found->second;
+ else
+ return s.version_of(pkg);
+ }
+
+ /** Test whether the given version is installed in this solution.
+ */
+ bool ver_installed(const version &test_ver) const
+ {
+ return version_of(test_ver.get_package()) == test_ver;
+ }
+ };
+
+ /** Wrap a solver list by generating pairs where the second value
+ * is a bound constant value.
+ */
+ class forbid_iter_builder
+ {
+ typename dep::solver_iterator i;
+ dep d;
+ public:
+ forbid_iter_builder(const typename dep::solver_iterator &_i,
+ const dep &_d)
+ :i(_i), d(_d)
+ {
+ }
+
+ typename std::pair<version, dep> operator*() const
+ {
+ return std::pair<version, dep>(*i, d);
+ }
+
+ forbid_iter_builder &operator++()
+ {
+ ++i;
+ return *this;
+ }
+
+ bool end() const
+ {
+ return i.end();
+ }
+ };
+
+ /** Wrap an iterator over pairs to an iterator over the second
+ * element of the pair.
+ */
+ template<typename Iter>
+ class project_iter_2nd_impl
+ {
+ Iter i;
+ public:
+ project_iter_2nd_impl(const Iter &_i)
+ :i(_i)
+ {
+ }
+
+ typename Iter::value_type::second_type &operator*() const
+ {
+ return i->second;
+ }
+
+ typename Iter::value_type::second_type *operator->() const
+ {
+ return &i->second;
+ }
+
+ project_iter_2nd_impl &operator++()
+ {
+ ++i;
+ return *this;
+ }
+
+ bool operator==(const project_iter_2nd_impl &other) const
+ {
+ return i == other.i;
+ }
+
+ bool operator!=(const project_iter_2nd_impl &other) const
+ {
+ return i != other.i;
+ }
+ };
+
+ template<typename Iter>
+ project_iter_2nd_impl<Iter> project_iter_2nd(const Iter &i)
+ {
+ return project_iter_2nd_impl<Iter>(i);
+ }
+
+ /** Wrap an action iterator to select its version element. */
+ template<class Iter>
+ class project_ver_impl
+ {
+ Iter i;
+ public:
+ project_ver_impl(const Iter &_i)
+ :i(_i)
+ {
+ }
+
+ bool operator==(const project_ver_impl &other)
+ {
+ return i == other.i;
+ }
+
+ bool operator!=(const project_ver_impl &other)
+ {
+ return i != other.i;
+ }
+
+ project_ver_impl &operator++()
+ {
+ ++i;
+ return *this;
+ }
+
+ const version &operator*() const
+ {
+ return i->ver;
+ }
+
+ const version *operator->() const
+ {
+ return &i->ver;
+ }
+ };
+
+ template<typename Iter>
+ project_ver_impl<Iter> project_ver(const Iter &i)
+ {
+ return project_ver_impl<Iter>(i);
+ }
+
+
+ /** Convert a [begin,end) pair to a single APT-style iterator.
+ */
+ template<class iter>
+ class apt_iter_wrapper_impl
+ {
+ iter curr, the_end;
+ public:
+ apt_iter_wrapper_impl(const iter &_begin, const iter &_end)
+ :curr(_begin), the_end(_end)
+ {
+ }
+
+ bool end() const
+ {
+ return curr==the_end;
+ }
+
+ apt_iter_wrapper_impl &operator++()
+ {
+ ++curr;
+ return *this;
+ }
+
+ const typename std::iterator_traits<iter>::reference operator*() const
+ {
+ return *curr;
+ }
+
+ const typename std::iterator_traits<iter>::reference operator->() const
+ {
+ return &*curr;
+ }
+ };
+
+ template<class Iter>
+ apt_iter_wrapper_impl<Iter> apt_iter_wrapper(const Iter &begin,
+ const Iter &end)
+ {
+ return apt_iter_wrapper_impl<Iter>(begin, end);
+ }
+
+
+ /** Given a list of actions, remove "obviously" unnecessary actions;
+ * that is, actions which are not inspired by any broken
+ * dependency. Corresponds to ResolveFrom, but is a bit more
+ * extreme in its approach. As usual, no effort is made to avoid
+ * stupid pairs.
+ *
+ * \param actions the set of actions to apply
+ *
+ * \param unresolved_soft_deps a set of broken dependencies which
+ * are permitted (indeed, required?)
+ * to be broken at the end of the process.
+ *
+ * \param forbidden_iter an iterator over the list of versions that
+ * should be forbidden in the new solution.
+ *
+ * \return a solution containing only the actions which appear to
+ * be necessary.
+ */
+ template<typename forbid_iter>
+ solution filter_unnecessary_actions(const std::vector<action> &actions,
+ const std::set<dep> &unresolved_soft_deps,
+ const forbid_iter &forbidden_iter)
+ {
+ // Versions from which to choose.
+ std::set<version> avail_versions(project_ver(actions.begin()),
+ project_ver(actions.end()));
+
+ // Currently broken deps.
+ std::set<dep> broken_deps = initial_broken;
+
+ // The solution being built.
+ std::map<package, action> output_actions;
+
+ // Score of this solution
+ int action_score = 0;
+
+ while(!broken_deps.empty())
+ {
+ assert(!avail_versions.empty());
+
+ // Pick an "arbitrary" broken dependency.
+ dep d = *broken_deps.begin();
+ broken_deps.erase(broken_deps.begin());
+
+ // If it's to be ignored, just drop it on the floor.
+ if(unresolved_soft_deps.find(d) != unresolved_soft_deps.end())
+ continue;
+
+ // Now, pick an "arbitrary" resolver of this dep.
+ //
+ // (maybe pick the best-scored one that's available instead?)
+ bool found_one = false;
+ version solver;
+ for(typename dep::solver_iterator si = d.solvers_begin();
+ !si.end(); ++si)
+ {
+ typename std::set<version>::const_iterator found
+ = avail_versions.find(*si);
+
+ if(found != avail_versions.end())
+ {
+ found_one = true;
+ solver = *si;
+ break;
+ }
+ }
+
+ // I have a proof that a solver should exist, but that doesn't
+ // mean it does ;-)
+ assert(found_one);
+ assert(output_actions.find(solver.get_package()) == output_actions.end());
+ assert(solver != solver.get_package().current_version());
+
+ if(debug)
+ std::cout << "Filter: resolving " << d << " with "
+ << solver.get_package().get_name() << ":"
+ << solver.get_name() << std::endl;
+
+ action act(solver, d, output_actions.size());
+
+ // Update the set of broken dependencies. FIXME: it should be
+ // possible to do this in-place, maybe using immutable sets?
+ std::set<dep> new_broken;
+ update_broken(partial_solution<map_solution>(output_actions),
+ project_ver(&act),
+ project_ver((&act)+1),
+ unresolved_soft_deps,
+ broken_deps,
+ new_broken);
+
+ // Expensive copy.
+ broken_deps = new_broken;
+
+ // Finally, update the old solution with the new action.
+ output_actions[solver.get_package()] = act;
+ action_score += step_score;
+ action_score += version_scores[solver.get_id()];
+ action_score -= version_scores[solver.get_package().current_version().get_id()];
+ }
+
+ // By definition we have a solution now.
+ int score = action_score + full_solution_score + unfixed_soft_score * unresolved_soft_deps.size();
+
+ return solution(project_iter_2nd(output_actions.begin()),
+ project_iter_2nd(output_actions.end()),
+ unresolved_soft_deps,
+ broken_deps,
+ forbidden_iter,
+ score,
+ action_score);
+ }
+
+ /** Eliminate stupid pairs from the given solution.
+ *
+ * \param s the solution from which stupid pairs should be removed.
+ *
+ * \return the new solution
+ */
+ solution eliminate_stupid(const solution &s)
+ {
+ assert(s.get_broken().empty());
+
+ stupid_table stupid_pairs;
+
+ initialize_stupid_pairs(s, stupid_pairs);
+
+ solution rval = s;
+
+ bool contained_stupid = !stupid_pairs.empty();
+
+ while(!stupid_pairs.empty())
+ {
+ if(debug)
+ {
+ std::cout << "Eliminating stupid pairs from ";
+ rval.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ // The pair to eliminate; picked (sorta) arbitrarily.
+ const typename std::pair<version, version> &victim = *stupid_pairs.begin();
+ stupid_pairs.erase(stupid_pairs.begin());
+
+ // This is where we discard pairs that are no longer relevant
+ // because one of the elements of the pair has been dropped.
+ if(rval.get_actions().find(victim.first.get_package()) == rval.get_actions().end() ||
+ rval.get_actions().find(victim.second.get_package()) == rval.get_actions().end())
+ {
+ if(debug)
+ std::cout << "Dropping invalidated stupid pair("
+ << victim.first.get_package().get_name()
+ << ":" << victim.first.get_name() << ","
+ << victim.second.get_package().get_name()
+ << ":" << victim.second.get_name() << ")"
+ << std::endl;
+
+ continue;
+ }
+
+ if(debug)
+ std::cout << "Examining stupid pair ("
+ << victim.first.get_package().get_name()
+ << ":" << victim.first.get_name() << ","
+ << victim.second.get_package().get_name()
+ << ":" << victim.second.get_name() << ")" << std::endl;
+
+ // Suppose we drop the second element in favor of the first.
+ // Will that produce a valid solution?
+ dep first_broken;
+
+ // If something was broken, then we need to use the fallback
+ // position: just change the dependency given as the "reason"
+ // for the first action.
+ if(broken_by_drop(rval, victim.first, first_broken))
+ {
+ if(debug)
+ std::cout << "Unable to drop "
+ << victim.first.get_package().get_name()
+ << ":" << victim.first.get_name()
+ << ", changing justification to "
+ << first_broken << std::endl;
+
+ typename std::map<package, action>::const_iterator found = rval.get_actions().find(victim.first.get_package());
+ assert(found != rval.get_actions().end());
+
+ action repl_action(victim.first, first_broken,
+ found->second.id);
+
+ // Note that the score is perfectly correct, as it depends
+ // only on the set of versions in rval (which doesn't
+ // change). The set of forbidden versions is in some
+ // sense "wrong", but if we have a final solution this
+ // shouldn't be a problem.
+
+ swap_action swbegin(rval.get_actions().begin(),
+ victim.first.get_package(),
+ repl_action);
+ swap_action swend(rval.get_actions().end(),
+ victim.first.get_package(),
+ repl_action);
+
+ rval = solution(swbegin, swend,
+ rval.get_unresolved_soft_deps(),
+ rval.get_broken(),
+ apt_iter_wrapper(rval.get_forbidden_versions().begin(),
+ rval.get_forbidden_versions().end()),
+ rval.get_score(), rval.get_action_score());
+ }
+ else
+ {
+ if(debug)
+ std::cout << "Dropping "
+ << victim.first.get_package().get_name()
+ << ":" << victim.first.get_name()
+ << " and filtering unnecessary installations."
+ << std::endl;
+ // Ok, it's safe.
+ //
+ // Generate a node by dropping the second element and
+ // changing the version of the first:
+ std::vector<action> actions;
+
+ for(typename std::map<package, action>::const_iterator i =
+ s.get_actions().begin(); i != s.get_actions().end(); ++i)
+ {
+ if(i->second.ver == victim.first)
+ actions.push_back(action(victim.second, i->second.d,
+ i->second.id));
+ else if(i->second.ver != victim.second)
+ actions.push_back(i->second);
+ }
+
+ rval = filter_unnecessary_actions(actions,
+ rval.get_unresolved_soft_deps(),
+ apt_iter_wrapper(rval.get_forbidden_versions().begin(),
+ rval.get_forbidden_versions().end()));
+ }
+ }
+
+ if(debug)
+ {
+ if(contained_stupid)
+ {
+ std::cout << "Done eliminating stupid pairs, result is ";
+ rval.dump(std::cout);
+ std::cout << std::endl;
+ }
+ else
+ {
+ std::cout << "No stupid pairs in ";
+ rval.dump(std::cout);
+ std::cout << std::endl;
+ }
+ }
+
+ assert(rval.get_broken().empty());
+
+ return rval;
+ }
+#endif
+
+ solution eliminate_stupid(const solution &s) const
+ {
+ if(debug)
+ std::cout << "Would eliminate stupid, but stupid elimination is disabled." << std::endl;
+
+ return s;
+ }
+
+ /** Calculate whether the solution is rejected based on
+ * user_rejected by testing whether the intersection of the
+ * solution domain and the rejected set is nonempty.
+ */
+ bool contains_rejected(const solution &s) const
+ {
+ for(typename std::set<version>::const_iterator uri
+ = user_rejected.begin(); uri != user_rejected.end(); ++uri)
+ {
+ typename imm::map<package, action>::node found = s.get_actions().lookup(uri->get_package());
+
+ if(found.isValid() && found.getVal().second.ver == *uri)
+ {
+ if(debug)
+ std::cout << "Rejected version " << found.getVal().first.get_name()
+ << " " << found.getVal().second.ver.get_name()
+ << " detected." << std::endl;
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool breaks_hardened(const solution &s) const
+ {
+ typename std::set<dep>::const_iterator uh_iter
+ = user_hardened.begin();
+
+ if(uh_iter == user_hardened.end())
+ return false;
+
+ typename imm::set<dep>::const_iterator su_iter
+ = s.get_unresolved_soft_deps().begin();
+
+ while(uh_iter != user_hardened.end() &&
+ su_iter != s.get_unresolved_soft_deps().end())
+ {
+ if(*uh_iter == *su_iter)
+ {
+ if(debug)
+ std::cout << "Broken hardened dependency " << *uh_iter
+ << " detected." << std::endl;
+
+ return true;
+ }
+ else if(*uh_iter < *su_iter)
+ ++uh_iter;
+ else
+ ++su_iter;
+ }
+
+ return false;
+ }
+
+ /** \return \b true if the given solution passed up an opportunity
+ * to include an 'mandated' version.
+ */
+ bool avoids_mandated(const solution &s) const
+ {
+ // NB: The current algorithm is not terribly efficient.
+ for(typename std::set<version>::const_iterator ai = user_mandated.begin();
+ ai != user_mandated.end(); ++ai)
+ {
+ version v = s.version_of(ai->get_package());
+ // If it's already being installed, then we're fine.
+ if(v == *ai)
+ continue;
+
+ // Check (very slowly) whether we made a decision where we had
+ // the opportunity to use this version (and of course didn't).
+ for(typename imm::map<package, action>::const_iterator si = s.get_actions().begin();
+ si != s.get_actions().end(); ++si)
+ if(si->second.d.solved_by(*ai) &&
+ user_mandated.find(si->second.ver) == user_mandated.end())
+ {
+ if(debug)
+ {
+ std::cout << ai->get_package().get_name() << " version " << ai->get_name() << " is avoided (when resolving " << si->second.d << ") by the solution:" << std::endl;
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** \return \b true if the resolution of the given dependency might
+ * be affected by a user constraint.
+ */
+ bool impinges_user_constraint(const dep &d) const
+ {
+ if(user_hardened.find(d) != user_hardened.end())
+ return true;
+
+ for(typename dep::solver_iterator si = d.solvers_begin();
+ !si.end(); ++si)
+ {
+ if(user_rejected.find(*si) != user_rejected.end())
+ return true;
+
+ for(typename std::set<version>::iterator mi = user_mandated.begin();
+ mi != user_mandated.end(); ++mi)
+ {
+ if(mi->get_package() == (*si).get_package())
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /** \return \b true if the given solution should be deferred. */
+ bool should_defer(const solution &s) const
+ {
+ return contains_rejected(s) || breaks_hardened(s) ||
+ avoids_mandated(s);
+ }
+
+ /** Place any solutions that were deferred and are no longer
+ * rejected back on the open queue.
+ */
+ void reexamine_deferred()
+ {
+ // NB: the STL guarantees that erasing elements from a set does
+ // not invalidate existing iterators. Hence this very careful
+ // iteration:
+
+ typename std::set<solution, solution_contents_compare>::const_iterator
+ i = deferred.begin(), j = i;
+
+ while(i != deferred.end())
+ {
+ ++j;
+
+ if(!should_defer(*i))
+ {
+ open.push(*i);
+ deferred.erase(i);
+ }
+
+ i = j;
+ }
+
+ // Note that we might have to rescind the "finished" state: the
+ // actions above can actually cause new solutions to be available
+ // for processing!
+ if(finished && !open.empty())
+ finished = false;
+
+ deferred_dirty = false;
+ }
+
+ /** \return \b true if the given solution is "irrelevant": that is,
+ * either it was already generated and placed in the closed queue,
+ * or it includes an already-generated solution as a proper subset.
+ */
+ bool irrelevant(const solution &s)
+ {
+ if(closed.find(s) != closed.end())
+ return true;
+
+ if(contains_conflict(s))
+ return true;
+
+ // The efficiency of this step hinges on the *assumption* that the
+ // number of solutions that will be generated is small relative to
+ // the number of potential solutions.
+ for(typename std::vector<solution>::const_iterator i=generated_solutions.begin();
+ i!=generated_solutions.end(); ++i)
+ if(std::includes(s.get_actions().begin(), s.get_actions().end(),
+ i->get_actions().begin(), i->get_actions().end()))
+ return true;
+
+ if(s.get_score() < minimum_score)
+ {
+ if(debug)
+ std::cout << "Not generating solution (infinite badness " << s.get_score() << "<" << minimum_score << ")" << std::endl;
+ return true;
+ }
+
+ return false;
+ }
+
+ /** Tries to enqueue the given package.
+ *
+ * \return \b true if the solution was not irrelevant.
+ */
+ bool try_enqueue(const solution &s)
+ {
+ if(irrelevant(s))
+ {
+ if(debug)
+ {
+ std::cout << "Dropping irrelevant solution ";
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ return false;
+ }
+ else if(should_defer(s))
+ {
+ if(debug)
+ {
+ std::cout << "Deferring rejected solution ";
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ deferred.insert(s);
+ return false;
+ }
+ else
+ {
+ if(debug)
+ {
+ std::cout << "Enqueuing ";
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ open.push(s);
+
+ return true;
+ }
+ }
+
+ /** Try to enqueue the given collection of packages. */
+ void try_enqueue(const std::vector<solution> &sols)
+ {
+ for(typename std::vector<solution>::const_iterator
+ succi = sols.begin(); succi != sols.end(); ++succi)
+ try_enqueue(*succi);
+ }
+
+ /** Internal routine to check for the legality of a 'move' and
+ * generate a conflictor if it's not legal.
+ */
+ bool is_legal(const solution &s,
+ const version &v,
+ action &out_act) const
+ {
+ package p = v.get_package();
+ version cur = p.current_version();
+ version inst = s.version_of(p);
+
+ if(inst != cur)
+ {
+ if(debug)
+ std::cout << "Discarding " << p.get_name() << " "
+ << v.get_name() << ": monotonicity violation"
+ << std::endl;
+
+ out_act.ver = inst;
+ out_act.from_dep_source = false;
+ return false;
+ }
+ else
+ {
+ assert(v != cur);
+
+ typename imm::map<version, dep>::node found
+ = s.get_forbidden_versions().lookup(v);
+
+ if(!found.isValid())
+ return true;
+ else
+ {
+ const dep &found_d = found.getVal().second;
+
+ if(debug)
+ std::cout << "Discarding " << p.get_name() << " "
+ << v.get_name() << ": forbidden by the resolution of "
+ << found_d << std::endl;
+
+ out_act.ver = s.version_of(found_d.get_source().get_package());
+ out_act.d = found_d;
+ out_act.from_dep_source = true;
+
+ return false;
+ }
+ }
+ }
+
+ /** Internal routine to insert a new conflictor into a conflict set.
+ * Handles the case in which the conflictor subsumes an existing
+ * element of the set.
+ */
+ void insert_conflictor(imm::map<package, action> &conflict,
+ const action &act) const
+ {
+ package p = act.ver.get_package();
+ typename imm::map<package, action>::node found
+ = conflict.lookup(p);
+
+ if(!found.isValid())
+ conflict.put(p, act);
+ else
+ {
+ const action &found_act = found.getVal().second;
+
+ action a2 = act;
+
+ assert(found_act.ver == act.ver);
+ if(a2.from_dep_source)
+ {
+ if(found_act.from_dep_source)
+ assert(a2.d == found_act.d);
+
+ else
+ a2.from_dep_source = false;
+ }
+ conflict.put(p, a2);
+ }
+ }
+
+ /** Generate a solution and push it onto an encapsulated vector of
+ * solutions.
+ */
+ class real_generator
+ {
+ std::vector<solution> ⌖
+ public:
+ real_generator(std::vector<solution> &_target)
+ :target(_target)
+ {
+ }
+
+ template<typename a_iter, class u_iter>
+ void make_successor(const solution &s,
+ const a_iter &abegin, const a_iter &aend,
+ const u_iter &ubegin, const u_iter &uend,
+ const PackageUniverse &universe,
+ const solution_weights &weights) const
+ {
+ target.push_back(solution::successor(s,
+ abegin, aend,
+ ubegin, uend,
+ universe, weights));
+ }
+ };
+
+ /** Don't actually generate solutions; instead, just count how many
+ * *would* be generated. Each time a solution would be generated,
+ * the integer referenced by this object is incremented (it is
+ * never set to 0; if you need it to be initialized to 0, do that
+ * yourself).
+ */
+ class null_generator
+ {
+ int &count;
+ public:
+ null_generator(int &_count)
+ :count(_count)
+ {
+ }
+
+ template<typename a_iter, class u_iter>
+ void make_successor(const solution &s,
+ const a_iter &abegin, const a_iter &aend,
+ const u_iter &ubegin, const u_iter &uend,
+ const PackageUniverse &universe,
+ const solution_weights &weights) const
+ {
+ ++count;
+ }
+
+ int get_count() const
+ {
+ return count;
+ }
+ };
+
+ /** Convenience routine for the below: try to generate a successor
+ * by installing a single package version. NB: assumes that the
+ * solution's actions have dense identifiers (i.e., less than
+ * s.get_actions().size()).
+ *
+ * \param s the solution for which a successor should be generated
+ * \param v the version to install
+ * \param d the dependency for which the successor is being generated.
+ * \param from_dep_source if \b true, this successor is the result
+ * of an action on the source of a dependency
+ * \param conflict a map to which conflictors for this version, if
+ * any, will be added.
+ *
+ * \param generator an object supporting the make_successor() routine,
+ * as real_generator and null_generator above.
+ */
+ template<typename SolutionGenerator>
+ void generate_single_successor(const solution &s,
+ const dep &d,
+ const version &v,
+ bool from_dep_source,
+ imm::map<package, action> &conflict,
+ const SolutionGenerator &generator) const
+ {
+ action conflictor;
+
+ if(debug)
+ {
+ std::cout << "Trying to resolve " << d << " by installing "
+ << v.get_package().get_name() << " "
+ << v.get_name();
+ if(from_dep_source)
+ std::cout << " from the dependency source";
+
+ std::cout << std::endl;
+ }
+
+ const int newid = s.get_actions().size();
+
+ if(!is_legal(s, v, conflictor))
+ insert_conflictor(conflict, conflictor);
+ else
+ {
+ action act(v, d, from_dep_source, newid);
+
+ // Speculatively form the new set of actions; doing this
+ // rather than forming the whole solution allows us to avoid
+ // several rather expensive steps in the successor routine
+ // (most notably the formation of the new broken packages
+ // set).
+ imm::map<package, action> new_acts = s.get_actions();
+ new_acts.put(v.get_package(), act);
+
+ typename conflictset::const_iterator
+ found = find_matching_conflict(new_acts,
+ std::pair<package, action>(v.get_package(), act));
+
+ if(found == conflicts.end())
+ generator.make_successor(s, &act, &act+1,
+ (dep *) 0, (dep *) 0,
+ universe, weights);
+ else
+ {
+ if(debug)
+ {
+ std::cout << "Discarding " << v.get_package().get_name()
+ << " " << v.get_name() << " due to conflict ";
+ dump_conflict(std::cout, *found);
+ std::cout << std::endl;
+ }
+
+ imm::map<package, action> m = *found;
+
+ for(typename imm::map<package, action>::const_iterator ci
+ = m.begin(); ci != m.end(); ++ci)
+ {
+ // Discard the version that we were trying to install,
+ // so that the caller can use this to form a more
+ // general conflict if all its resolutions fail.
+ if(ci->first != v.get_package())
+ insert_conflictor(conflict, ci->second);
+ else
+ assert(ci->second.ver == v);
+ }
+ }
+ }
+ }
+
+ /** Build the successors of a solution node for a particular
+ * dependency.
+ *
+ * \param conflict a map which will be initialized with a conflict
+ * explaining the non-appearance of some solvers (if there are no
+ * solvers, this will be a full conflict explaining the lack of
+ * solvers).
+ *
+ * \param out a vector onto which the successors should be pushed.
+ */
+ template<typename SolutionGenerator>
+ void generate_successors(const solution &s,
+ const dep &d,
+ imm::map<package, action> &conflict,
+ const SolutionGenerator &generator) const
+ {
+ version source = d.get_source();
+ typename imm::map<package, action>::node
+ source_found = s.get_actions().lookup(source.get_package());
+
+ // Try moving the source, if it is legal to do so
+ if(source_found.isValid())
+ insert_conflictor(conflict, action(source, d, false, -1));
+ else
+ {
+ assert(source == source.get_package().current_version());
+
+ for(typename package::version_iterator vi = source.get_package().versions_begin();
+ !vi.end(); ++vi)
+ if(*vi != source)
+ generate_single_successor(s, d, *vi, true, conflict, generator);
+ }
+
+ // Now try installing each target of the dependency.
+ for(typename dep::solver_iterator si = d.solvers_begin();
+ !si.end(); ++si)
+ generate_single_successor(s, d, *si, false, conflict, generator);
+
+ // Finally, maybe we can leave this dependency unresolved.
+ if(d.is_soft())
+ generator.make_successor(s, (action *) 0, (action *) 0,
+ &d, &d+1, universe, weights);
+ }
+
+ /** Processes the given solution by enqueuing its successor nodes
+ * (if any are available). Note that for clarity, we now generate
+ * *all* successors before examining *any*.
+ */
+ void process_solution(const solution &s)
+ {
+ // Any forcings are immediately applied to 'curr', so that
+ // forcings are performed ASAP.
+ solution curr = s;
+
+ assert(!s.get_broken().empty());
+
+ // This loop attempts to generate all the successors of a
+ // solution. However, if a "forced" dependency arises, it
+ // re-verifies all the dependencies of the solution.
+ bool done = false;
+ while(!done)
+ {
+ if(debug)
+ {
+ std::cout << "Processing ";
+ curr.dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ done = true;
+
+ // Remember the solution whose broken dependencies we're
+ // iterating over.
+ solution starting_solution = curr;
+
+ for(typename imm::set<dep>::const_iterator bi=starting_solution.get_broken().begin();
+ bi!=starting_solution.get_broken().end(); ++bi)
+
+ {
+ // Check for the case where this dependency has been
+ // fortuitously solved by forcing another broken
+ // dependency.
+ if(starting_solution != curr && !(*bi).broken_under(curr))
+ continue;
+
+ // Assert against impossible conditions (if this happens
+ // something is broken elsewhere).
+ if(starting_solution == curr && !(*bi).broken_under(curr))
+ {
+ std::cerr << "Unexpectedly non-broken dependency "
+ << *bi << "!" << std::endl;
+
+ version source = (*bi).get_source();
+
+ if(curr.version_of(source.get_package()) != source)
+ std::cerr << " (" << source.get_package().get_name()
+ << " " << source.get_name()
+ << " is not installed)" << std::endl;
+
+ for(typename dep::solver_iterator si = (*bi).solvers_begin();
+ !si.end(); ++si)
+ if(curr.version_of((*si).get_package()) == *si)
+ std::cerr << " (" << source.get_package().get_name()
+ << " " << source.get_name()
+ << " is not installed)" << std::endl;
+
+ abort();
+ }
+
+ imm::map<package, action> conflict;
+
+
+ int num_successors = 0;
+ generate_successors(curr, *bi, conflict,
+ null_generator(num_successors));
+
+
+
+ if(num_successors == 0)
+ {
+ if(debug)
+ {
+ std::cout << "Discarding solution; unresolvable dependency "
+ << *bi << " with conflict ";
+
+ dump_conflict(std::cout, conflict);
+
+ std::cout << std::endl;
+ }
+
+ add_conflict(conflict);
+
+ return;
+ }
+ else if(num_successors == 1)
+ {
+ if(debug)
+ std::cout << "Forced resolution of " << *bi << std::endl;
+
+ std::vector<solution> v;
+ real_generator g(v);
+ // NB: this may do redundant work adding to 'conflict'.
+ // Use a generator object to avoid that?
+ generate_successors(curr, *bi, conflict,
+ real_generator(v));
+
+ assert(v.size() == 1);
+
+ curr = v.back();
+ done = false;
+ }
+ }
+ }
+
+ // In the course of forcing dependencies, we might have actually
+ // arrived at a solution, in which case we should just enqueue it
+ // and stop (a full solution has no successors to generate).
+ if(curr.is_full_solution())
+ {
+ try_enqueue(curr);
+ return;
+ }
+
+ unsigned int nsols = 0;
+
+ // First try to enqueue stuff related to a dependency that the
+ // user constrained; then just go for a free-for-all.
+ for(typename imm::set<dep>::const_iterator bi=curr.get_broken().begin();
+ bi!=curr.get_broken().end() && (nsols == 0 ||
+ nsols < max_successors); ++bi)
+
+ if(impinges_user_constraint(*bi))
+ {
+ // Is it possible to take this out somehow?
+ imm::map<package, action> conflict;
+
+ if(debug)
+ std::cout << "Generating successors for " << *bi
+ << std::endl;
+
+ std::vector<solution> v;
+ generate_successors(curr, *bi, conflict, real_generator(v));
+ try_enqueue(v);
+ nsols += v.size();
+ }
+
+ for(typename imm::set<dep>::const_iterator bi=curr.get_broken().begin();
+ bi!=curr.get_broken().end() && (nsols == 0 ||
+ nsols < max_successors); ++bi)
+ {
+ // Is it possible to take this out somehow?
+ imm::map<package, action> conflict;
+
+ if(debug)
+ std::cout << "Generating successors for " << *bi
+ << std::endl;
+
+ std::vector<solution> v;
+ generate_successors(curr, *bi, conflict, real_generator(v));
+ try_enqueue(v);
+ nsols += v.size();
+ }
+ }
+public:
+
+ /** Construct a new generic_problem_resolver.
+ *
+ * \param _score_score the score per "step" of a (partial) solution. Typically negative.
+ * \param _broken_score the score to add per broken dependency of a (partial) solution. Typically negative.
+ * \param _unfixed_soft_score the score to add per soft dependency LEFT UNFIXED. Typically negative.
+ * \param infinity a score value that will be considered to be "infinite". Solutions
+ * with less than -infinity points will be immediately discarded.
+ * \param _full_solution_score a bonus for goal nodes (things
+ * that solve all dependencies)
+ * \param _universe the universe in which we are working.
+ */
+ generic_problem_resolver(int _step_score, int _broken_score,
+ int _unfixed_soft_score,
+ int infinity, unsigned int _max_successors,
+ int _full_solution_score,
+ const PackageUniverse &_universe)
+ :weights(_step_score, _broken_score, _unfixed_soft_score,
+ _full_solution_score, _universe.get_version_count()),
+ minimum_score(-infinity), max_successors(_max_successors),
+ universe(_universe), finished(false), deferred_dirty(false),
+ debug(false), remove_stupid(true),
+ solver_executing(false), solver_cancelled(false),
+ conflicts(_universe.get_package_count())
{
// Find all the broken deps.
for(typename PackageUniverse::broken_dep_iterator bi=universe.broken_begin();
@@ -2462,7 +2462,7 @@
out << " SCORE " << (*i).get_name() << " <";
for(typename PackageUniverse::package::version_iterator j=(*i).versions_begin();
- !j.end(); ++j)
+ !j.end(); ++j)
if(weights.version_scores[(*j).get_id()]!=0)
out << " " << (*j).get_name() << " " << weights.version_scores[(*j).get_id()];
More information about the Aptitude-svn-commit
mailing list