[Aptitude-svn-commit] r3943 - in branches/aptitude-0.3/aptitude: .
src/generic/problemresolver tests
Daniel Burrows
dburrows at costa.debian.org
Wed Aug 24 23:38:52 UTC 2005
Author: dburrows
Date: Wed Aug 24 23:38:47 2005
New Revision: 3943
Modified:
branches/aptitude-0.3/aptitude/ChangeLog
branches/aptitude-0.3/aptitude/src/generic/problemresolver/problemresolver.h
branches/aptitude-0.3/aptitude/src/generic/problemresolver/solution.h
branches/aptitude-0.3/aptitude/tests/test_resolver.cc
Log:
Refactor the problem resolver to be more comprehensible and reasonably sized.
Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog (original)
+++ branches/aptitude-0.3/aptitude/ChangeLog Wed Aug 24 23:38:47 2005
@@ -1,3 +1,23 @@
+2005-08-24 Daniel Burrows <dburrows at debian.org>
+
+ * src/generic/problemresolver/problemresolver.h, src/generic/problemresolver/solution.h, tests/test_resolver.h:
+
+ Massive refactoring of the problem resolver. The solution
+ abstraction is now much stronger: solutions can *only* be
+ constructed as either a root node or a successor to an existing
+ solution, and score calculation is handled in the constructor
+ (actually a static factory function).
+
+ Instead of trying hard to not actually build solution objects,
+ the main resolver now generates all solutions up-front; this
+ introduces some new inefficiencies while removing others, but it
+ does seem to have slightly slowed the program down.
+
+ Needless to say, separating solution generation from solution
+ processing makes everything so much cleaner that it's not even
+ funny. Some errors may also been introduced that will need to
+ be dealt with; actual testing is probably a good idea here.
+
2005-08-23 Daniel Burrows <dburrows at debian.org>
* src/generic/problemresolver/problemresolver.h:
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 Wed Aug 24 23:38:47 2005
@@ -232,10 +232,10 @@
}
};
- /** How much to reward long and/or broken solutions. Typically
- * negative to penalize such things, or 0 to ignore them.
- */
- int step_score, broken_score, unfixed_soft_score;
+ // 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.
@@ -264,24 +264,16 @@
* 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;
- /** How much to reward real solutions -- solutions that fix all
- * dependencies. Make this big to immediately pick them up, or
- * small to ignore "bad" solutions (at the risk of running out of
- * time if no better solution pops up).
- */
- int full_solution_score;
-
/** The universe in which we are solving problems. */
const PackageUniverse universe;
- /** The internal scores to apply to individual package
- * versions.
- */
- int *version_scores;
-
/** If \b true, we have exhausted the list of solutions. */
bool finished:1;
@@ -309,102 +301,10 @@
*/
std::set<solution, solution_contents_compare> deferred;
- /** An action implicated in a conflict: this can either be a pure
- * version installation (i.e., the version is implicated no matter
- * how it's installed) or a dep-source-qualified version
- * installation (i.e., the version is only implicated if it's
- * installed to fix the source of a particular dep).
- */
- struct act_conflict
- {
- /** The version to install. */
- version ver;
-
- /** The dependency whose source was fixed by this, if
- * from_dep_source is \b true.
- */
- dep d;
-
- bool from_dep_source;
-
- act_conflict()
- :from_dep_source(false)
- {
- }
-
- act_conflict(const version &_ver)
- :ver(_ver), from_dep_source(false)
- {
- }
-
- act_conflict(const version &_ver, const dep &_d)
- :ver(_ver), d(_d), from_dep_source(true)
- {
- }
-
- /** \return \b true if this action will match the action given by
- * other: true if the versions are equal and either they
- * both follow from the same dep, or 'other' doesn't
- * follow from any dep.
- */
- bool subsumes(const act_conflict &other) const
- {
- if(ver != other.ver)
- return false;
-
- if(!other.from_dep_source)
- return true;
- else
- return from_dep_source && d == other.d;
- }
-
- bool operator==(const act_conflict &other) const
- {
- if(from_dep_source != other.from_dep_source)
- return false;
-
- if(from_dep_source)
- return ver == other.ver && d == other.d;
- else
- return ver == other.ver;
- }
-
- bool operator!=(const act_conflict &other) const
- {
- if(from_dep_source != other.from_dep_source)
- return true;
-
- if(from_dep_source)
- return ver != other.ver || d != other.d;
- else
- return ver != other.ver;
- }
-
- bool operator<(const act_conflict &other) const
- {
- if(!from_dep_source && other.from_dep_source)
- return true;
- else if(from_dep_source && !other.from_dep_source)
- return false;
- else if(!from_dep_source)
- return ver < other.ver;
- else if(ver < other.ver) // if(from_dep_source)
- return true;
- else if(other.ver < ver)
- return false;
- else
- return d < other.d;
- }
- };
-
- // Needs to be a friend since it has to be global.
- template<class PackageUniverse2>
- friend std::ostream &operator<<(std::ostream &out, const typename std::map<typename PackageUniverse2::package, typename generic_problem_resolver<PackageUniverse2>::act_conflict> &ac);
-
/** Stores conflicts: sets of installations that have been
* determined to be mutually incompatible.
*/
- std::set<std::map<package, act_conflict> > conflicts;
+ std::set<std::map<package, action> > conflicts;
/** The initial set of broken dependencies. Kept here for use in
* the stupid-elimination algorithm.
@@ -432,59 +332,69 @@
typedef std::set<std::pair<version, version> > stupid_table;
- // FIXME: temporary hack so I don't have to lift code all over.
- std::ostream &dump(std::ostream &out, const std::map<package, act_conflict> &act) const;
- std::ostream &dump(std::ostream &out, const act_conflict &act) const;
+ std::ostream &dump_conflict(std::ostream &out, const std::map<package, action> &conflict) const;
+ std::ostream &dump_conflict(std::ostream &out, const action &act) const;
- /** Populate a 'partial conflict map' from a solution. */
- static void populate_partial_conflict(const solution &s,
- typename std::map<package, act_conflict> &pc)
- {
- for(typename std::map<package, action>::const_iterator si
- = s.get_actions().begin(); si != s.get_actions().end(); ++si)
- pc[si->first] = act_conflict(si->second.ver);
- }
- /** \return \b true if each element of pc2 is subsumed by an element
+ /** \param conflictor an action contained in a conflict.
+ * \param a an action 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 action &conflictor,
+ const action &a)
+ {
+ 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 partial_conflict_subsumes(const typename std::map<package, act_conflict> &pc1,
- const typename std::map<package, act_conflict> &pc2)
+ static bool conflict_matches(const typename std::map<package, action> &c,
+ const typename std::map<package, action> &acts)
{
- typename std::map<package, act_conflict>::const_iterator pc1i = pc1.begin();
- typename std::map<package, act_conflict>::const_iterator pc2i = pc2.begin();
+ typename std::map<package, action>::const_iterator ci = c.begin();
+ typename std::map<package, action>::const_iterator ai = acts.begin();
- while(pc1i != pc1.end() &&
- pc2i != pc2.end())
+ while(ci != c.end() &&
+ ai != acts.end())
{
- if(pc1i->first < pc2i->first)
- ++pc1i;
- else if(pc2i->first < pc1i->first)
+ if(ai->first < ci->first)
+ ++ai;
+ else if(ci->first < ai->first)
return false;
- else if(!(pc1i->second.subsumes(pc2i->second)))
+ else if(!(conflictor_matches(ci->second, ai->second)))
return false;
else
{
- ++pc1i;
- ++pc2i;
+ ++ci;
+ ++ai;
}
}
- return (pc2i == pc2.end());
+ return (ci == c.end());
}
/** Test whether the given partial conflict subsumes an existing
* conflict.
*
- * \return a conflict subsumed by m, or conflicts.end() if no such
+ * \return a conflict matched by m, or conflicts.end() if no such
* conflict exists.
*/
- typename std::set<std::map<package, act_conflict> >::const_iterator
- subsumes_any_conflict(const std::map<package, act_conflict> &m) const
+ typename std::set<std::map<package, action> >::const_iterator
+ matches_any_conflict(const std::map<package, action> &m) const
{
- for(typename std::set<std::map<package, act_conflict> >::const_iterator ci = conflicts.begin();
+ for(typename std::set<std::map<package, action> >::const_iterator ci = conflicts.begin();
ci != conflicts.end(); ++ci)
- if(partial_conflict_subsumes(m, *ci))
+ if(conflict_matches(*ci, m))
return ci;
return conflicts.end();
@@ -493,17 +403,14 @@
/** Test whether the given solution contains a conflict. */
bool contains_conflict(const solution &s) const
{
- std::map<package, act_conflict> m;
- populate_partial_conflict(s, m);
-
- typename std::set<std::map<package, act_conflict> >::const_iterator
- found = subsumes_any_conflict(m);
+ typename std::set<std::map<package, action> >::const_iterator
+ found = matches_any_conflict(s.get_actions());
bool rval = (found != conflicts.end());
if(debug && rval)
{
std::cout << "The conflict ";
- dump(std::cout, *found);
+ dump_conflict(std::cout, *found);
std::cout << " is triggered by the solution ";
s.dump(std::cout);
std::cout << std::endl;
@@ -512,6 +419,55 @@
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 std::map<package, action> &conflict)
+ {
+ typename std::set<std::map<package, action> >::const_iterator
+ found = matches_any_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
+ {
+ for(typename std::set<std::map<package, action> >::const_iterator ci = conflicts.begin(), cj;
+ ci != conflicts.end(); ci = cj)
+ {
+ cj = ci;
+ ++cj;
+
+ if(conflict_matches(conflict, *ci))
+ {
+ if(debug)
+ {
+ std::cout << "Dropping conflict ";
+ dump_conflict(std::cout, *ci);
+ std::cout << " because it is redundant with ";
+ dump_conflict(std::cout, conflict);
+ std::cout << std::endl;
+ }
+
+ conflicts.erase(ci);
+ }
+ }
+
+ conflicts.insert(conflict);
+ }
+ }
+
+#if 0
/** Test whether the given solution contains a conflict when the
* given action is taken.
*
@@ -866,7 +822,6 @@
return project_iter_2nd_impl<Iter>(i);
}
-
/** Wrap an action iterator to select its version element. */
template<class Iter>
class project_ver_impl
@@ -953,87 +908,6 @@
return apt_iter_wrapper_impl<Iter>(begin, end);
}
- /** Calculate a new set of broken dependencies given an a starting
- * solution object, old set of broken dependencies and a range of
- * versions to install.
- *
- * \param s the starting package -> version map
- * \param vbegin the start of the range of versions to install
- * \param vend the end of the range of versions to install
- * \param unresolved_soft_deps a set of soft dependencies to ignore
- * \param old_broken the old set of broken dependencies
- * \param new_broken a set into which the newly broken dependencies
- * should be inserted.
- */
- template<typename VerIter, typename SolutionType>
- void update_broken(const SolutionType &s,
- const VerIter &vbegin,
- const VerIter &vend,
- const std::set<dep> &unresolved_soft_deps,
- const std::set<dep> &old_broken,
- std::set<dep> &new_broken)
- {
- // Check all revdeps of the old AND new package versions, and all
- // previously broken deps. Is there any way to speed this up?
- // For instance, by keeping a cache of already-examined deps.
- // Note that for the old version we only care about Depends and
- // for the new we only care about Conflicts...but it's not clear
- // that information can really be used in any good way (aside from
- // satisfied_by()).
- //
- // Note that this might be a rather expensive step -- on the other
- // hand, if lots of deps are broken, we might have problems
- // anyway. Just plowing ahead for now.
- //
- // If you touch this code, remember that the revdeps list is not a
- // full list of the reverse dependencies; the constraint on it is
- // that any deps newly broken by installing v must appear in
- // *either* the reverse list of v *or* the reverse list of
- // old_version.
-
-
-
- // Generate a temporary solution mapping. Expensive and probably
- // redundant with other code, should it be passed in as an
- // argument?
- partial_solution<SolutionType> tmpsol(s);
- tmpsol.install(vbegin, vend);
-
-
- for(VerIter vi=vbegin; vi!=vend; ++vi)
- {
- const version &v=*vi;
- version old_version=s.version_of(v.get_package());
-
- // Check reverse deps of the old version
- for(typename version::revdep_iterator rd=old_version.revdeps_begin();
- !rd.end(); ++rd)
- if(unresolved_soft_deps.find(*rd) == unresolved_soft_deps.end() &&
- (*rd).broken_under(tmpsol))
- new_broken.insert(*rd);
-
- // Check reverse deps of the new version
- for(typename version::revdep_iterator rd=v.revdeps_begin();
- !rd.end(); ++rd)
- if(unresolved_soft_deps.find(*rd) == unresolved_soft_deps.end() &&
- (*rd).broken_under(tmpsol))
- new_broken.insert(*rd);
-
- // Check forward deps of the new version
- for(typename version::dep_iterator di=v.deps_begin();
- !di.end(); ++di)
- if(unresolved_soft_deps.find(*di) == unresolved_soft_deps.end() &&
- (*di).broken_under(tmpsol))
- new_broken.insert(*di);
- }
-
- for(typename std::set<dep>::const_iterator bd=old_broken.begin();
- bd!=old_broken.end(); ++bd)
- if(unresolved_soft_deps.find(*bd) == unresolved_soft_deps.end() &&
- (*bd).broken_under(tmpsol))
- new_broken.insert(*bd);
- }
-
/** Given a list of actions, remove "obviously" unnecessary actions;
* that is, actions which are not inspired by any broken
@@ -1294,6 +1168,15 @@
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
@@ -1479,60 +1362,6 @@
return false;
}
- /** Generates a successor to s by performing the actions
- * [abegin,aend) and ignoring the (soft) deps [ubegin,uend).
- */
- template<class forbid_iter, class a_iter, class u_iter>
- solution do_install(const solution &s,
- const a_iter &abegin, const a_iter &aend,
- const u_iter &ubegin, const u_iter &uend,
- const forbid_iter &forbidden_iter)
- {
- int new_action_score=s.get_action_score();
-
- for(a_iter a=abegin; a!=aend; ++a)
- {
- const version &v=a->ver;
- version old_version=s.version_of(v.get_package());
-
- assert(old_version == v.get_package().current_version() &&
- old_version != v);
-
- new_action_score+=step_score;
-
- assert(v.get_id()<universe.get_version_count());
- new_action_score+=version_scores[v.get_id()];
- assert(v.get_package().current_version().get_id()<universe.get_version_count());
- new_action_score-=version_scores[v.get_package().current_version().get_id()];
- }
-
-
- std::set<dep> new_broken;
- std::set<dep> new_unresolved_soft(s.get_unresolved_soft_deps());
- new_unresolved_soft.insert(ubegin, uend);
- update_broken(s, project_ver(abegin),
- project_ver(aend),
- new_unresolved_soft,
- s.get_broken(),
- new_broken);
-
- for(u_iter u = ubegin; u != uend; ++u)
- new_broken.erase(*u);
-
- int new_score=new_action_score + broken_score * new_broken.size()
- + unfixed_soft_score * new_unresolved_soft.size();
-
- if(new_broken.size() == 0)
- new_score += full_solution_score;
-
- return solution(abegin, aend, s,
- ubegin, uend,
- new_broken,
- forbidden_iter,
- new_score,
- new_action_score);
- }
-
/** Tries to enqueue the given package.
*
* \return \b true if the solution was not irrelevant.
@@ -1577,441 +1406,350 @@
}
}
- /** Enqueues a single successor node to the given solution by
- * installing the given package versions.
- *
- * \param s the predecessor of the new solution
- * \param v the version to install
- * \param forbidden_iter an APT-style iterator over a set
- * of versions that should be forbidden.
- *
- * \return \b true if the new solution was not in the closed set.
- */
- template<class forbid_iter, class action_iter, class unresolved_iter>
- bool try_install(const solution &s,
- const action_iter &abegin, const action_iter &aend,
- const unresolved_iter &ubegin, const unresolved_iter &uend,
- const forbid_iter &forbidden_iter)
- {
- solution s2=do_install(s, abegin, aend, ubegin, uend,
- forbidden_iter);
- return try_enqueue(s2);
- }
-
- /** Generates (and enqueues) successor nodes for the given broken
- * dependency of the given solution.
- *
- * \param s the solution to generate successors for
- * \param d the dependency to fix
- * \param count a value to increment for every successor that
- * is enqueued or discarded (as long as it's discarded
- * for being too bad, and not for being in closed).
- */
- void generate_successors(const solution &s,
- const dep &d,
- unsigned int &count)
+ /** Try to enqueue the given collection of packages. */
+ void try_enqueue(const std::vector<solution> &sols)
{
- version source_version=d.get_source();
- package source_package=source_version.get_package();
- version sol_version=s.version_of(source_package);
- bool found = false;
-
-// if(d.solvers_begin().end())
-// {
-// std::cout << "No solvers for " << d.get_dep().ParentPkg().Name()
-// << " " << d.get_dep().DepType() << " "
-// << d.get_dep().TargetPkg().Name();
-// if(d.get_dep().TargetVer())
-// std::cout << " (" << d.get_dep().CompType()
-// << " " << d.get_dep().TargetVer() << ")";
-
-// if(!d.get_prv().end())
-// std::cout << " (through "
-// << d.get_prv().OwnerPkg().Name() << " "
-// << d.get_prv().OwnerVer().VerStr()
-// << " Provides "
-// << d.get_prv().ParentPkg().Name()
-// << ")";
-
-// std::cout << std::endl;
-// }
-
- // if not, then the dep is satisfied!
- assert(sol_version == source_version);
-
- // If the source of the dependency was not modified and is
- // presently installed, try installing a *different version*.
- if(source_version == source_package.current_version())
- {
- for(typename package::version_iterator vi=source_package.versions_begin();
- !vi.end(); ++vi)
- if(*vi != source_version)
- {
- if(s.get_forbidden_versions().find(*vi) != s.get_forbidden_versions().end())
- {
- if(debug)
- std::cout << "Discarding forbidden version "
- << (*vi).get_name() << std::endl;
-
- continue;
- }
+ for(typename std::vector<solution>::const_iterator
+ succi = sols.begin(); succi != sols.end(); ++succi)
+ try_enqueue(*succi);
+ }
- if(debug)
- std::cout << " Trying to resolve " << d << " by installing " << (*vi).get_package().get_name() << " version " << (*vi).get_name() << std::endl;
+ /** 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);
- found = true;
+ if(inst != cur)
+ {
+ if(debug)
+ std::cout << "Discarding " << p.get_name() << " "
+ << v.get_name() << ": monotonicity violation"
+ << std::endl;
- action act(*vi, d, s.get_actions().size());
- if(try_install(s, &act, (&act)+1,
- (dep*)0, (dep*)0,
- forbid_iter_builder(d.solvers_begin(), d)))
- ++count;
- }
+ out_act.ver = inst;
+ out_act.from_dep_source = false;
+ return false;
}
-
- // Enqueue each potential solver.
- //
- // (note: skip packages that have already been assigned...I could
- // probably speed this up very slightly by pushing knowledge down
- // out of the abstract layer -- no need to iterate over all the
- // versions of each package that's already assigned)
- for(typename dep::solver_iterator si=d.solvers_begin();
- !si.end(); ++si)
+ else
{
- version sol_ver=*si;
+ assert(v != cur);
- if(sol_ver != sol_ver.get_package().current_version() &&
- s.get_actions().find(sol_ver.get_package())==s.get_actions().end())
- {
- if(s.get_forbidden_versions().find(sol_ver) != s.get_forbidden_versions().end())
- {
- if(debug)
- std::cout << "Discarding forbidden version "
- << sol_ver.get_name() << std::endl;
-
- continue;
- }
+ typename std::map<version, dep>::const_iterator found
+ = s.get_forbidden_versions().find(v);
+ if(found == s.get_forbidden_versions().end())
+ return true;
+ else
+ {
if(debug)
- std::cout << " Trying to resolve " << d << " by installing " << (*si).get_package().get_name() << " version " << (*si).get_name() << std::endl;
-
- found = true;
+ std::cout << "Discarding " << p.get_name() << " "
+ << v.get_name() << ": forbidden by the resolution of "
+ << found->second << std::endl;
+
+ out_act.ver = s.version_of(v.get_package());
+ out_act.d = found->second;
+ out_act.from_dep_source = true;
- action act(*si, d, s.get_actions().size());
- if(try_install(s, &act, (&act)+1,
- (dep*)0, (dep*)0,
- dummy_end_iterator<std::pair<version, dep> >()))
- ++count;
+ return false;
}
}
+ }
- // For soft dependencies, we can optionally leave the dependency
- // unresolved!
- if(d.is_soft())
- {
- if(debug)
- std::cout << " Trying to ignore soft dependency " << d
- << std::endl;
+ /** 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(std::map<package, action> &conflict,
+ const action &act) const
+ {
+ package p = act.ver.get_package();
+ typename std::map<package, action>::const_iterator found
+ = conflict.find(p);
- found = true;
+ if(found == conflict.end())
+ conflict[p]=act;
+ else
+ {
+ action a2 = act;
- int new_score = s.get_score() - broken_score + unfixed_soft_score;
- // Handle the corner case where this is a full solution.
- if(s.get_broken().size() == 1)
- new_score += full_solution_score;
+ assert(found->second.ver == act.ver);
+ if(a2.from_dep_source)
+ {
+ if(found->second.from_dep_source)
+ assert(a2.d == found->second.d);
- // Only score change is that something goes from being broken
- // to being permanently unfixed and "soft".
- if(try_enqueue(solution(s, d,
- new_score,
- s.get_action_score())))
- ++count;
+ else
+ a2.from_dep_source = false;
+ }
+ conflict[p] = a2;
}
-
- // Otherwise we should have discarded this before calling
- // generate_successors!
- assert(found);
}
- /** Generate "forced" successors -- those successors which are
- * logically required by the existing dependencies.
- *
- * \return \b true iff the dependency can't be resolved at all
- *
- * \param s the solution to try.
- *
- * \param d the dep to test.
- *
- * \param actions an out parameter -- any generated forced
- * successor will be pushed onto this list.
+ /** 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 toforbid an out parameter -- includes additional
- * forbidden versions over what's forbidden by s.
+ * \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 out a vector onto which the successor will be pushed.
*/
- bool generate_forced_successor(const solution &s,
+ void generate_single_successor(const solution &s,
const dep &d,
- std::set<action> &actions,
- std::map<version, dep> &toforbid,
- std::set<dep> &toignore)
- {
- // As we progress, reasons for forbidding each solver are tossed
- // into this map. If the solution is eventaully discarded, this
- // object will contain a conflict that explains the discard.
- std::map<package, act_conflict> partial_conflict;
-
- version v;
- version source_version=d.get_source();
- package source_package=source_version.get_package();
- bool found=false, from_source=false;
-
+ const version &v,
+ bool from_dep_source,
+ std::map<package, action> &conflict,
+ std::vector<solution> &out) const
+ {
+ action conflictor;
if(debug)
{
- std::cout << "Testing whether " << d << " is a forced dependency." << std::endl;
+ 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;
}
- // If the source of the dependency was not modified and is
- // presently installed, try installing a *different version*.
- if(source_version == source_package.current_version())
+ const int newid = s.get_actions().size();
+
+ if(!is_legal(s, v, conflictor))
+ insert_conflictor(conflict, conflictor);
+ else
{
- for(typename package::version_iterator vi=source_package.versions_begin();
- !vi.end(); ++vi)
- {
- // Ignore the source version, of course.
- if(*vi == source_version)
- continue;
+ action act(v, d, from_dep_source, newid);
- typename std::map<version, dep>::const_iterator fi = s.get_forbidden_versions().find(*vi);
- if(fi != s.get_forbidden_versions().end())
- {
- // Whoops, it was forbidden in the past.
- partial_conflict[fi->first.get_package()]
- = act_conflict(fi->first, fi->second);
+ solution new_sol = solution::successor(s, &act, &act+1,
+ (dep *) 0,
+ (dep *) 0,
+ universe, weights);
- continue;
- }
+ typename std::set<std::map<package, action> >::const_iterator
+ found = matches_any_conflict(new_sol.get_actions());
- fi = toforbid.find(*vi);
- if(fi != toforbid.end())
+ if(found == conflicts.end())
+ out.push_back(new_sol);
+ else
+ {
+ if(debug)
{
- partial_conflict[fi->first.get_package()]
- = act_conflict(fi->first, fi->second);
-
- continue;
+ std::cout << "Discarding " << v.get_package().get_name()
+ << " " << v.get_name() << " due to conflict ";
+ dump_conflict(std::cout, *found);
+ std::cout << std::endl;
}
- // If there's not essential conflict, check for a
- // higher-order conflict.
- typename std::set<std::map<package, act_conflict> >::const_iterator
- found_conflict = will_conflict(s, act_conflict(*vi, d));
- if(found_conflict != conflicts.end())
+ for(typename std::map<package, action>::const_iterator ci
+ = found->begin(); ci != found->end(); ++ci)
{
- // Insert the elements of the new conflict other than
- // the one that was speculatively inserted.
- for(typename std::map<package, act_conflict>::const_iterator
- fci = found_conflict->begin();
- fci != found_conflict->end(); ++fci)
- {
- if(fci->first != (*vi).get_package())
- partial_conflict.insert(*fci);
- }
- continue;
+ // 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);
}
-
- // If we already found a legal solver, then we now know
- // that we can't force this dep and it's certainly not an
- // essential conflict.
- if(found)
- return false;
-
- found = true;
- from_source = true;
- v = *vi;
}
}
+ }
+
+ /** 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.
+ */
+ void generate_successors(const solution &s,
+ const dep &d,
+ std::map<package, action> &conflict,
+ std::vector<solution> &out) const
+ {
+ version source = d.get_source();
+ typename std::map<package, action>::const_iterator
+ source_found = s.get_actions().find(source.get_package());
+
+ // Try moving the source, if it is legal to do so
+ if(source_found != s.get_actions().end())
+ insert_conflictor(conflict, action(source, d, false, -1));
else
- // If the source of the dependency was already installed, the
- // conflict explanation for not being able to remove it is
- // itself!
- partial_conflict[source_package] = act_conflict(source_version);
+ {
+ 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, out);
+ }
+
+ // Now try installing each target of the dependency.
for(typename dep::solver_iterator si = d.solvers_begin();
!si.end(); ++si)
- {
- version sol_ver = *si;
+ generate_single_successor(s, d, *si, false, conflict, out);
- // If the package has already been modified, don't modify it
- // again.
- typename std::map<package, action>::const_iterator ai
- = s.get_actions().find(sol_ver.get_package());
- if(ai != s.get_actions().end())
- {
- typename std::map<package, act_conflict>::iterator pci
- = partial_conflict.find(ai->first);
+ // Finally, maybe we can leave this dependency unresolved.
+ if(d.is_soft())
+ out.push_back(solution::successor(s, (action *) 0, (action *) 0,
+ &d, &d+1, universe, weights));
+ }
- if(pci != partial_conflict.end())
- {
- // The partial conflict must contain only stuff
- // that's already in this solution.
- assert(pci->second.ver == ai->second.ver);
+ /** 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)
+ {
+ bool forced = false;
+ // Any forcings are immediately applied to 'curr', so that
+ // forcings are performed ASAP.
+ solution curr = s;
+ std::vector<std::pair<dep, std::vector<solution> > > workingq;
+
+ 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 ";
+ s.dump(std::cout);
+ std::cout << std::endl;
+ }
- // Make sure that this is an expansive conflictor.
- pci->second.from_dep_source = false;
- }
- else
- partial_conflict[ai->first] = ai->second.ver;
+ done = true;
+ for(typename std::set<dep>::const_iterator bi=s.get_broken().begin();
+ bi!=s.get_broken().end(); ++bi)
+
+ {
+ // Check for the case where this dependency has been
+ // fortuitously solved by forcing another broken
+ // dependency.
+ if(s != curr && !(*bi).broken_under(curr))
continue;
- }
- if(debug)
- std::cout << "sol_ver is " << sol_ver.get_package().get_name() << " " << sol_ver.get_name() << std::endl;
+ // Assert against impossible conditions (if this happens
+ // something is broken elsewhere).
+ if(!(*bi).broken_under(curr))
+ {
+ std::cerr << "Unexpectedly non-broken dependency "
+ << *bi << "!" << std::endl;
- // Since we assume the dep is broken, this shouldn't happen!
- assert(sol_ver != sol_ver.get_package().current_version());
+ version source = (*bi).get_source();
- typename std::map<version, dep>::const_iterator fi = s.get_forbidden_versions().find(*si);
- if(fi != s.get_forbidden_versions().end())
- {
- // Whoops, it was forbidden in the past.
- partial_conflict[fi->first.get_package()]
- = act_conflict(fi->first, fi->second);
+ 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;
- continue;
- }
+ abort();
+ }
+
+ std::map<package, action> conflict;
+
+ workingq.push_back(std::pair<dep, std::vector<solution> >(*bi, std::vector<solution>()));
+ generate_successors(curr, *bi, conflict, workingq.back().second);
- fi = toforbid.find(*si);
- if(fi != toforbid.end())
- {
- partial_conflict[fi->first.get_package()]
- = act_conflict(fi->first, fi->second);
- continue;
- }
- // If there's not essential conflict, check for a
- // higher-order conflict.
- typename std::set<std::map<package, act_conflict> >::const_iterator
- found_conflict = will_conflict(s, act_conflict(*si));
- if(found_conflict != conflicts.end())
- {
- // Insert the elements of the new conflict other than
- // the one that was speculatively inserted.
- for(typename std::map<package, act_conflict>::const_iterator
- fci = found_conflict->begin();
- fci != found_conflict->end(); ++fci)
+ int sz = workingq.back().second.size();
+
+ if(sz == 0)
{
- if(fci->first != (*si).get_package())
- partial_conflict.insert(*fci);
- }
+ if(debug)
+ {
+ std::cout << "Discarding solution; unresolvable dependency "
+ << *bi << " with conflict ";
- partial_conflict.insert(found_conflict->begin(),
- found_conflict->end());
- continue;
- }
+ dump_conflict(std::cout, conflict);
- if(found)
- return false;
- found=true;
- v=sol_ver;
- }
+ std::cout << std::endl;
+ }
- if(found)
- {
- // Soft dependencies shouldn't be forced, as you have the
- // option of discarding them.
- if(d.is_soft())
- return false;
+ add_conflict(conflict);
- if(debug)
- std::cout << "Forced resolution of " << d
- << " by installing " << v.get_package().get_name()
- << " version " << v.get_name() << std::endl;
-
- if(from_source)
- for(typename dep::solver_iterator si=d.solvers_begin();
- !si.end(); ++si)
- toforbid[*si] = d;
+ return;
+ }
+ else if(sz == 1)
+ {
+ if(debug)
+ std::cout << "Forced resolution of " << *bi << std::endl;
- actions.insert(action(v, d, s.get_actions().size()+actions.size()));
+ curr = workingq.back().second.front();
+ forced = true;
+ done = false;
+ }
+ }
+ };
- return false;
- }
+ if(forced)
+ try_enqueue(curr);
else
{
- // If there really is no choice, then soft deps can be left
- // unresolved.
- if(d.is_soft())
- {
- if(debug)
- std::cout << "Forced soft dependency " << d
- << " to be left unresolved." << std::endl;
+ unsigned int nsols = 0;
- toignore.insert(d);
+ // First try to enqueue stuff related to a dependency that the
+ // user constrained; then just go for a free-for-all.
- return false;
- }
- else
- {
- if(debug)
- {
- std::cout << "Discarding solution: unsolvable dependency " << d
- << " with new conflict ";
- dump(std::cout, partial_conflict);
+ for(typename std::vector<std::pair<dep, std::vector<solution> > >::const_iterator
+ wqi = workingq.begin();
+ wqi != workingq.end() && (nsols == 0 ||
+ nsols < max_successors);
+ ++wqi)
+ if(impinges_user_constraint(wqi->first))
+ {
+ if(debug)
+ std::cout << "Generating successors for " << wqi->first
+ << std::endl;
- std::cout << std::endl;
+ try_enqueue(wqi->second);
+ nsols += wqi->second.size();
+ }
- conflicts.insert(partial_conflict);
- }
+ for(typename std::vector<std::pair<dep, std::vector<solution> > >::const_iterator
+ wqi = workingq.begin();
+ wqi != workingq.end() && (nsols == 0 ||
+ nsols < max_successors);
+ ++wqi)
+ {
+ if(debug)
+ std::cout << "Generating successors for " << wqi->first
+ << std::endl;
- return true;
+ try_enqueue(wqi->second);
+ nsols += wqi->second.size();
}
}
}
-
- /** Processes the given solution by enqueuing its successor nodes
- * (if any are available).
- */
- void process_solution(const solution &s)
- {
- // How many solutions have been generated?
- unsigned int sols=0;
-
- std::set<action> forced_actions;
- std::map<version, dep> forced_forbidden_versions;
- std::set<dep> forced_unresolved_soft_deps;
-
-
- for(typename std::set<dep>::const_iterator bi=s.get_broken().begin();
- bi!=s.get_broken().end(); ++bi)
- if(generate_forced_successor(s, *bi,
- forced_actions,
- forced_forbidden_versions,
- forced_unresolved_soft_deps))
- return;
-
- if(!forced_actions.empty() || !forced_unresolved_soft_deps.empty())
- {
- try_install(s,
- forced_actions.begin(), forced_actions.end(),
- forced_unresolved_soft_deps.begin(), forced_unresolved_soft_deps.end(),
- apt_iter_wrapper(forced_forbidden_versions.begin(), forced_forbidden_versions.end()));
- return;
- }
-
- // First, try to generate successors for the broken dependencies
- // that are affected by user constraints (branching these off ASAP
- // lets us keep the branching factor low and converge faster).
- for(typename std::set<dep>::const_iterator bi=s.get_broken().begin();
- bi!=s.get_broken().end() && (sols==0 || sols<max_successors); ++bi)
- if(impinges_user_constraint(*bi))
- generate_successors(s, *bi, sols);
-
-
- for(typename std::set<dep>::const_iterator bi=s.get_broken().begin();
- bi!=s.get_broken().end() && (sols==0 || sols<max_successors); ++bi)
- generate_successors(s, *bi, sols);
- }
public:
/** Construct a new generic_problem_resolver.
@@ -2030,16 +1768,11 @@
int infinity, unsigned int _max_successors,
int _full_solution_score,
const PackageUniverse &_universe)
- :step_score(_step_score), broken_score(_broken_score),
- unfixed_soft_score(_unfixed_soft_score),
+ :weights(_step_score, _broken_score, _unfixed_soft_score,
+ _full_solution_score, _universe.get_version_count()),
minimum_score(-infinity), max_successors(_max_successors),
- full_solution_score(_full_solution_score),
universe(_universe), finished(false), deferred_dirty(false), debug(false), remove_stupid(true)
{
- version_scores=new int[universe.get_version_count()];
- for(size_t i=0; i<universe.get_version_count(); ++i)
- version_scores[i]=0;
-
// Find all the broken deps.
for(typename PackageUniverse::broken_dep_iterator bi=universe.broken_begin();
!bi.end(); ++bi)
@@ -2048,7 +1781,6 @@
~generic_problem_resolver()
{
- delete[] version_scores;
}
const PackageUniverse &get_universe()
@@ -2056,12 +1788,12 @@
return universe;
}
- int get_step_score() {return step_score;}
- int get_broken_score() {return broken_score;}
- int get_unresolved_soft_dep_score() {return unfixed_soft_score;}
+ int get_step_score() {return weights.step_score;}
+ int get_broken_score() {return weights.broken_score;}
+ int get_unresolved_soft_dep_score() {return weights.unfixed_soft_score;}
int get_infinity() {return -minimum_score;}
int get_max_successors() {return max_successors;}
- int get_full_solution_score() {return full_solution_score;}
+ int get_full_solution_score() {return weights.full_solution_score;}
/** Enables or disables debugging. Debugging is initially
* disabled.
@@ -2090,7 +1822,7 @@
closed.clear();
for(size_t i=0; i<universe.get_version_count(); ++i)
- version_scores[i]=0;
+ weights.version_scores[i]=0;
}
/** \return \b true if no solutions have been examined yet.
@@ -2121,7 +1853,7 @@
void set_version_score(const version &ver, int score)
{
assert(ver.get_id()<universe.get_version_count());
- version_scores[ver.get_id()]=score;
+ weights.version_scores[ver.get_id()]=score;
}
/** As set_version_score, but instead of replacing the current score
@@ -2130,14 +1862,14 @@
void add_version_score(const version &ver, int score)
{
assert(ver.get_id()<universe.get_version_count());
- version_scores[ver.get_id()]+=score;
+ weights.version_scores[ver.get_id()]+=score;
}
/** \return the score of the version ver. */
int get_version_score(const version &ver)
{
assert(ver.get_id()<universe.get_version_count());
- return version_scores[ver.get_id()];
+ return weights.version_scores[ver.get_id()];
}
/** Reject future solutions containing this version.
@@ -2284,7 +2016,9 @@
{
closed.clear();
- open.push(solution(initial_broken, initial_broken.size()*broken_score));
+ open.push(solution::root_node(initial_broken,
+ universe,
+ weights));
}
while(max_steps>0 && !open.empty())
@@ -2320,13 +2054,6 @@
closed.insert(s);
- if(debug)
- {
- std::cout << "Processing ";
- s.dump(std::cout);
- std::cout << std::endl;
- }
-
// If all dependencies are satisfied, we found a solution.
if(s.is_full_solution())
{
@@ -2366,7 +2093,14 @@
generated_solutions.push_back(minimized);
if(debug)
- std::cout << " *** Converged after " << odometer << " steps." << std::endl;
+ {
+ std::cout << " *** Converged after " << odometer << " steps." << std::endl;
+ std::cout << " *** open: " << open.size()
+ << "; closed: " << closed.size()
+ << "; conflicts: " << conflicts.size()
+ << "; deferred: " << deferred.size()
+ << "; generated solutions: " << generated_solutions.size();
+ }
return minimized;
}
@@ -2391,7 +2125,14 @@
finished=true;
if(debug)
- std::cout << " *** Out of solutions after " << odometer << " steps." << std::endl;
+ {
+ std::cout << " *** Out of solutions after " << odometer << " steps." << std::endl;
+ std::cout << " *** open: " << open.size()
+ << "; closed: " << closed.size()
+ << "; conflicts: " << conflicts.size()
+ << "; deferred: " << deferred.size()
+ << "; generated solutions: " << generated_solutions.size();
+ }
throw NoMoreSolutions();
}
@@ -2406,7 +2147,7 @@
for(typename PackageUniverse::package::version_iterator j=(*i).versions_begin();
!j.end(); ++j)
- if(version_scores[(*j).get_id()]!=0)
+ if(weights.version_scores[(*j).get_id()]!=0)
any_modified=true;
if(any_modified)
@@ -2415,8 +2156,8 @@
for(typename PackageUniverse::package::version_iterator j=(*i).versions_begin();
!j.end(); ++j)
- if(version_scores[(*j).get_id()]!=0)
- out << " " << (*j).get_name() << " " << version_scores[(*j).get_id()];
+ if(weights.version_scores[(*j).get_id()]!=0)
+ out << " " << (*j).get_name() << " " << weights.version_scores[(*j).get_id()];
out << " >" << std::endl;
}
@@ -2426,30 +2167,7 @@
};
template<class PackageUniverse>
-inline
-std::ostream &operator<<(std::ostream &out, const typename std::map<typename PackageUniverse::package, typename generic_problem_resolver<PackageUniverse>::act_conflict> &ac)
-{
- out << "(";
-
- for(typename std::map<typename PackageUniverse::package, typename generic_problem_resolver<PackageUniverse>::act_conflict>::const_iterator ci
- = ac.begin(); ci != ac.end(); ++ci)
- {
- if(ci != ci->begin())
- out << ", ";
- out << ci->first.name() << " "
- << ci->second.ver.name();
-
- if(ci->second.from_dep_source)
- out << " [" << ci->second.d << "]";
- }
-
- out << ")";
-
- return out;
-}
-
-template<class PackageUniverse>
-std::ostream &generic_problem_resolver<PackageUniverse>::dump(std::ostream &out, const typename generic_problem_resolver<PackageUniverse>::act_conflict &act) const
+std::ostream &generic_problem_resolver<PackageUniverse>::dump_conflict(std::ostream &out, const typename generic_problem_resolver<PackageUniverse>::action &act) const
{
out << act.ver.get_package().get_name() << " "
<< act.ver.get_name();
@@ -2461,17 +2179,17 @@
}
template<class PackageUniverse>
-std::ostream &generic_problem_resolver<PackageUniverse>::dump(std::ostream &out, const typename std::map<typename PackageUniverse::package, typename generic_problem_resolver<PackageUniverse>::act_conflict> &act) const
+std::ostream &generic_problem_resolver<PackageUniverse>::dump_conflict(std::ostream &out, const typename std::map<typename PackageUniverse::package, typename generic_solution<PackageUniverse>::action> &act) const
{
out << "(";
- for(typename std::map<typename PackageUniverse::package, typename generic_problem_resolver<PackageUniverse>::act_conflict>::const_iterator ci
+ for(typename std::map<typename PackageUniverse::package, typename generic_solution<PackageUniverse>::action>::const_iterator ci
= act.begin(); ci != act.end(); ++ci)
{
if(ci != act.begin())
out << ", ";
- dump(out, ci->second);
+ dump_conflict(out, ci->second);
}
out << ")";
Modified: branches/aptitude-0.3/aptitude/src/generic/problemresolver/solution.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/problemresolver/solution.h (original)
+++ branches/aptitude-0.3/aptitude/src/generic/problemresolver/solution.h Wed Aug 24 23:38:47 2005
@@ -26,6 +26,46 @@
#include <map>
#include <set>
+/** Represents the current score weights for a resolver. Used to
+ * calculate scores at the time a solution is instantiated.
+ */
+struct solution_weights
+{
+ /** How much to reward long and/or broken solutions. Typically
+ * negative to penalize such things, or 0 to ignore them.
+ */
+ int step_score, broken_score, unfixed_soft_score;
+
+
+ /** How much to reward real solutions -- solutions that fix all
+ * dependencies. Make this big to immediately pick them up, or
+ * small to ignore "bad" solutions (at the risk of running out of
+ * time if no better solution pops up).
+ */
+ int full_solution_score;
+
+ /** The scores to apply to individual package versions.
+ */
+ int *version_scores;
+
+ solution_weights(int _step_score, int _broken_score,
+ int _unfixed_soft_score, int _full_solution_score,
+ unsigned long num_versions)
+ :step_score(_step_score), broken_score(_broken_score),
+ unfixed_soft_score(_unfixed_soft_score),
+ full_solution_score(_full_solution_score),
+ version_scores(new int[num_versions])
+ {
+ for(unsigned long i = 0; i < num_versions; ++i)
+ version_scores[i] = 0;
+ }
+
+ ~solution_weights()
+ {
+ delete[] version_scores;
+ }
+};
+
/** Represents a partial or complete solution to a dependency
* problem. Solutions are transparently refcounted to save on
* memory and avoid copies.
@@ -56,16 +96,26 @@
/** The dependency that triggered this action. */
dep d;
+ /** If \b true, this action was triggered by removing the source
+ * of the dependency d.
+ */
+ bool from_dep_source:1;
+
/** The order in which this action should be placed. Used when
* presenting a "story" about a solution.
*/
- int id;
+ int id:31;
action() {}
action(const version &_ver,
const dep &_d,
- int _id):ver(_ver), d(_d), id(_id) {}
+ bool _from_dep_source,
+ int _id)
+ : ver(_ver), d(_d),
+ from_dep_source(_from_dep_source), id(_id)
+ {
+ }
bool operator<(const action &other) const {return ver<other.ver;}
@@ -131,107 +181,20 @@
void incref() const {++refcount;}
void decref() const {assert(refcount>0); if(--refcount==0) delete this;}
- /** Construct a new solution_rep by taking the given solution and
- * moving a single dependency into the set of unresolved soft
- * deps.
- */
- solution_rep(const generic_solution &parent,
- const dep &ignore,
- int _score, int _action_score)
- :actions(parent.get_actions()),
- broken_deps(parent.get_broken()),
- unresolved_soft_deps(parent.get_unresolved_soft_deps()),
- score(_score), action_score(_action_score), refcount(1)
- {
- assert(broken_deps.find(ignore) != broken_deps.end());
-
- broken_deps.erase(ignore);
- unresolved_soft_deps.insert(ignore);
- }
-
- /** Construct a new solution_rep. The initial reference count
- * is \b 1.
- *
- * \param ver the version to install beyond what is in the parent
- * \param parent the parent solution
- * \param _broken_deps the dependencies that are broken in this partial solution
- * \param _score the score of this solution
- * \param forbidden_iter an APT-style iterator over the set of
- * newly forbidden versions as (version, reason) pairs.
- * \param _action_score the score not due to broken deps
- */
- template<class forbid_iter, class action_iter, class unresolved_iter>
- solution_rep(const action_iter &abegin,
- const action_iter &aend,
- const generic_solution &parent,
- const unresolved_iter &ubegin,
- const unresolved_iter &uend,
+ /** Construct a new solution_rep directly. */
+ solution_rep(const std::map<package, action> &_actions,
const std::set<dep> &_broken_deps,
- const forbid_iter &forbidden_iter,
- int _score, int _action_score)
- :actions(parent.get_actions()), broken_deps(_broken_deps),
- unresolved_soft_deps(parent.get_unresolved_soft_deps()),
- forbidden_versions(parent.get_forbidden_versions()),
- score(_score), action_score(_action_score),
- refcount(1)
- {
- for(forbid_iter i=forbidden_iter; !i.end(); ++i)
- forbidden_versions.insert(*i);
-
- for(action_iter a=abegin; a!=aend; ++a)
- {
- assert(actions.find(a->ver.get_package()) == actions.end());
- actions[a->ver.get_package()] = *a;
- }
-
- for(unresolved_iter u = ubegin; u != uend; ++u)
- {
- assert(broken_deps.find(*u) == broken_deps.end());
- unresolved_soft_deps.insert(*u);
- }
- }
-
- /** Construct a new solution_rep. The initial reference count is
- * \b 1.
- *
- * \param abegin the start of a list of actions to perform
- * \param aend the end ot the list of actions to perform
- * \param _broken_deps the dependencies that are broken in this partial solution
- * \param _score the score of this solution
- * \param forbidden_iter an APT-style iterator over the set of
- * newly forbidden versions as (version, reason) pairs
- * \param _action_score the score not due to broken deps
- */
- template<class forbid_iter, class action_iter>
- solution_rep(const action_iter &abegin,
- const action_iter &aend,
const std::set<dep> &_unresolved_soft_deps,
- const std::set<dep> &_broken_deps,
- const forbid_iter &forbidden_iter,
- int _score, int _action_score)
- :broken_deps(_broken_deps),
- unresolved_soft_deps(_unresolved_soft_deps),
- score(_score), action_score(_action_score),
- refcount(1)
- {
- for(forbid_iter i=forbidden_iter; !i.end(); ++i)
- forbidden_versions.insert(*i);
-
- for(action_iter a = abegin; a != aend; ++a)
- {
- assert(actions.find(a->ver.get_package()) == actions.end());
- actions[a->ver.get_package()] = *a;
- }
- }
-
- /** Construct a solution with \b no action, the given set of
- * broken dependencies, and the given score. Used to generate
- * the root node of the search.
- */
- solution_rep(const std::set<dep> &_broken_deps,
- int _score)
- :broken_deps(_broken_deps), score(_score),
- action_score(0), refcount(1)
+ const std::map<version, dep> &_forbidden_versions,
+ int _score,
+ int _action_score)
+ : actions(_actions),
+ broken_deps(_broken_deps),
+ unresolved_soft_deps(_unresolved_soft_deps),
+ forbidden_versions(_forbidden_versions),
+ score(_score),
+ action_score(_action_score),
+ refcount(1)
{
}
@@ -275,76 +238,42 @@
}; // End solution representation.
solution_rep *real_soln;
-public:
- generic_solution():real_soln(0) {}
- /** Create a solution. */
- generic_solution(const generic_solution &parent,
- const dep &ignore,
- int score, int action_score)
- :real_soln(new solution_rep(parent, ignore, score, action_score))
- {
- }
-
- /** Create a solution.
- *
- * \param [abegin,aend) the new actions to enqueue
- *
- * \param parent the parent of this solution
- *
- * \param forbidden_iter an APT-style iterator (i.e., with an end()
- * method) over the set of newly forbidden versions.
- *
- * \param score the total score of this new solution
- *
- * \param action_score the portion of score due to actions
+ /** Create a solution directly from a rep; assumes control of the
+ * reference passed in as an argument (i.e., doesn't incref() it)
*/
- template<class forbid_iter, class action_iter, class unresolved_iter>
- generic_solution(const action_iter &abegin,
- const action_iter &aend,
- const generic_solution &parent,
- const unresolved_iter &ubegin,
- const unresolved_iter &uend,
- const std::set<dep> &broken_deps,
- const forbid_iter &forbidden_iter,
- int score, int action_score)
- :real_soln(new solution_rep(abegin, aend, parent,
- ubegin, uend, broken_deps,
- forbidden_iter, score, action_score))
+ generic_solution(solution_rep *r)
+ :real_soln(r)
{
}
- /** Create a solution.
- *
- * \param [abegin,aend) the new actions to enqueue
- *
- * \param forbidden_iter an APT-style iterator (i.e., with an end()
- * method) over the set of newly forbidden versions.
- *
- * \param score the total score of this new solution
- *
- * \param action_score the portion of score due to actions
+ /** Wrapper structure used to pass a raw map into broken_under().
+ * Used to determine the set of packages broken by a solution
+ * before the solution is actually created.
*/
- template<class forbid_iter, class action_iter>
- generic_solution(const action_iter &abegin,
- const action_iter &aend,
- const std::set<dep> &unresolved_soft_deps,
- const std::set<dep> &broken_deps,
- const forbid_iter &forbidden_iter,
- int score, int action_score)
- :real_soln(new solution_rep(abegin, aend,
- unresolved_soft_deps,
- broken_deps,
- forbidden_iter,
- score, action_score))
+ struct solution_map_wrapper
{
- }
+ const std::map<package, action> &actions;
+ public:
+ solution_map_wrapper(const std::map<package, action> &_actions)
+ :actions(_actions)
+ {
+ }
- generic_solution(const std::set<dep> &broken_deps,
- int score)
- :real_soln(new solution_rep(broken_deps, score))
- {
- }
+ version version_of(const package &p) const
+ {
+ typename std::map<package, action>::const_iterator found
+ = actions.find(p);
+
+ if(found != actions.end())
+ return found->second.ver;
+ else
+ return p.current_version();
+ }
+ };
+
+public:
+ generic_solution():real_soln(0) {}
generic_solution(const generic_solution &other)
:real_soln(other.real_soln)
@@ -353,6 +282,132 @@
real_soln->incref();
}
+ /** Generate the root node for a search in the given universe. */
+ static generic_solution root_node(const std::set<dep> &initial_broken,
+ const PackageUniverse &universe,
+ const solution_weights &weights)
+ {
+ int score = initial_broken.size() * weights.broken_score;
+
+ return generic_solution(new solution_rep(std::map<package, action>(),
+ initial_broken,
+ std::set<dep>(),
+ std::map<version, dep>(),
+ score,
+ 0));
+ }
+
+ /** Generate a successor to the given solution.
+ *
+ * \param [abegin, aend) a range of actions to perform
+ * \param [ubegin, uend) a range of dependencies to leave unresolved
+ */
+ template<typename a_iter, typename u_iter>
+ static generic_solution successor(const generic_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)
+ {
+ // NB: as I fully expect to move to a scheme of shared-memory
+ // sets/maps, the implicit copies here will go away eventually.
+ std::set<dep> broken_deps = s.get_broken();
+ std::map<package, action> actions = s.get_actions();
+ std::map<version, dep> forbidden_versions = s.get_forbidden_versions();
+ std::set<dep> unresolved_soft_deps = s.get_unresolved_soft_deps();
+ int action_score = s.get_action_score();
+
+ for(a_iter ai = abegin; ai != aend; ++ai)
+ {
+ const action &a = *ai;
+ assert(actions.find(a.ver.get_package()) == actions.end());
+ assert(a.ver != a.ver.get_package().current_version());
+
+ actions[a.ver.get_package()] = a;
+
+ action_score += weights.step_score;
+ action_score += weights.version_scores[a.ver.get_id()];
+ action_score -= weights.version_scores[a.ver.get_package().current_version().get_id()];
+
+ if(a.from_dep_source)
+ {
+ for(typename dep::solver_iterator si = a.d.solvers_begin();
+ !si.end(); ++si)
+ forbidden_versions[*si] = a.d;
+ }
+
+
+
+ // Update the set of broken dependencies, trying to re-use as
+ // much of the former set as possible.
+ version old_version=s.version_of(a.ver.get_package());
+ solution_map_wrapper tmpsol(actions);
+
+ // Check reverse deps of the old version
+ for(typename version::revdep_iterator rd=old_version.revdeps_begin();
+ !rd.end(); ++rd)
+ if(unresolved_soft_deps.find(*rd) == unresolved_soft_deps.end() &&
+ (*rd).broken_under(tmpsol))
+ broken_deps.insert(*rd);
+ else
+ broken_deps.erase(*rd);
+
+ // Check reverse deps of the new version
+ //
+ // Because reverse deps of a version might be fixed by its
+ // removal, we need to check brokenness and insert or erase as
+ // appropriate.
+ for(typename version::revdep_iterator rd = a.ver.revdeps_begin();
+ !rd.end(); ++rd)
+ if(unresolved_soft_deps.find(*rd) == unresolved_soft_deps.end() &&
+ (*rd).broken_under(tmpsol))
+ broken_deps.insert(*rd);
+ else
+ broken_deps.erase(*rd);
+
+ // Remove all forward deps of the old version (they're
+ // automagically fixed)
+ for(typename version::dep_iterator di=old_version.deps_begin();
+ !di.end(); ++di)
+ broken_deps.erase(*di);
+
+ // Check forward deps of the new version (no need to erase
+ // non-broken dependencies since they're automatically
+ // non-broken at the start)
+ for(typename version::dep_iterator di=a.ver.deps_begin();
+ !di.end(); ++di)
+ if(unresolved_soft_deps.find(*di) == unresolved_soft_deps.end() &&
+ (*di).broken_under(tmpsol))
+ broken_deps.insert(*di);
+ }
+
+ // Add notes about unresolved dependencies
+ for(u_iter ui = ubegin; ui != uend; ++ui)
+ {
+ const dep &d = *ui;
+
+ typename std::set<dep>::const_iterator found
+ = broken_deps.find(d);
+
+ assert(found != broken_deps.end());
+ broken_deps.erase(found);
+ unresolved_soft_deps.insert(d);
+ }
+
+ int score
+ = action_score + broken_deps.size()*weights.broken_score
+ + unresolved_soft_deps.size()*weights.unfixed_soft_score;
+
+ return generic_solution(new solution_rep(actions,
+ broken_deps,
+ unresolved_soft_deps,
+ forbidden_versions,
+ score,
+ action_score));
+ }
+
~generic_solution()
{
if(real_soln)
Modified: branches/aptitude-0.3/aptitude/tests/test_resolver.cc
==============================================================================
--- branches/aptitude-0.3/aptitude/tests/test_resolver.cc (original)
+++ branches/aptitude-0.3/aptitude/tests/test_resolver.cc Wed Aug 24 23:38:47 2005
@@ -107,15 +107,15 @@
dummy_universe::dep d2 = *di;
dummy_solution::action a1(u.find_package("a").version_from_name("v1"),
- d1, 49);
+ d1, false, 49);
dummy_solution::action a2(u.find_package("a").version_from_name("v1"),
- d2, 21);
+ d2, false, 21);
assertEqEquivalent(a1, a1);
assertEqEquivalent(a1, a2);
dummy_solution::action a3(u.find_package("a").version_from_name("v2"),
- d1, 49);
+ d1, false, 49);
assertEqEquivalent(a2, a2);
assertEqEquivalent(a3, a3);
@@ -124,7 +124,7 @@
assertEqInequivalent(a2, a3);
dummy_solution::action a4(u.find_package("c").version_from_name("v3"),
- d2, 21);
+ d2, false, 21);
assertEqEquivalent(a4, a4);
assertEqInequivalent(a1, a4);
@@ -145,67 +145,68 @@
CPPUNIT_ASSERT(!di.end());
dummy_universe::dep d2 = *di;
+ solution_weights weights(0, 0, 0, 0, u.get_version_count());
+
std::set<dummy_universe::dep> u_broken;
for(dummy_universe::broken_dep_iterator bi = u.broken_begin();
!bi.end(); ++bi)
u_broken.insert(*bi);
dummy_solution::action a1(u.find_package("a").version_from_name("v1"),
- d1, 49);
+ d1, false, 49);
dummy_solution::action a2(u.find_package("a").version_from_name("v1"),
- d2, 21);
+ d2, false, 21);
dummy_solution::action a3(u.find_package("a").version_from_name("v2"),
- d1, 49);
+ d1, false, 49);
dummy_solution::action a4(u.find_package("c").version_from_name("v3"),
- d2, 21);
+ d2, false, 21);
// Generate some meaningless solutions to check that equivalency
// is correctly calculated according to the version mappings and
// the set of unsolved soft deps.
- dummy_solution s0(u_broken, 0);
- dummy_solution s1(&a1, &a1+1, s0,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s0.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
- dummy_solution s2(&a2, &a2+1, s0,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s0.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
- dummy_solution s3(&a3, &a3+1, s0,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s0.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
- dummy_solution s4(&a4, &a4+1, s0,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s0.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
+ dummy_solution s0 = dummy_solution::root_node(u_broken,
+ u, weights);
+ dummy_solution s1
+ = dummy_solution::successor(s0, &a1, &a1+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
+ dummy_solution s2
+ = dummy_solution::successor(s0, &a2, &a2+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
+ dummy_solution s3
+ = dummy_solution::successor(s0, &a3, &a3+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
+ dummy_solution s4
+ = dummy_solution::successor(s0, &a4, &a4+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
// the following two should be equal.
- dummy_solution s5(&a4, &a4+1, s1,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s1.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
- dummy_solution s6(&a1, &a1+1, s4,
- (dummy_universe::dep *) 0,
- (dummy_universe::dep *) 0,
- s4.get_broken(),
- dummy_end_iterator<dummy_universe::version>(),
- 0, 0);
+ dummy_solution s5
+ = dummy_solution::successor(s1, &a4, &a4+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
+ dummy_solution s6
+ = dummy_solution::successor(s4, &a1, &a1+1,
+ (dummy_universe::dep *) 0,
+ (dummy_universe::dep *) 0,
+ u, weights);
// and this should not equal any other solution.
- dummy_solution s7(s0, d1, 0, 0);
+ dummy_solution s7
+ = dummy_solution::successor(s0,
+ (dummy_solution::action *) 0,
+ (dummy_solution::action *) 0,
+ &d1, &d1+1, u, weights);
dummy_resolver::solution_contents_compare solcmp;
More information about the Aptitude-svn-commit
mailing list