solver: rework internals a bit

* cleaned up little bit on the internal state machine
* the decision applying mechanism now aborts early to avoid work
  if we are approaching bad solution candidate
* package availability checking is now done on-demand; which
  could still be improved
cute-signatures
Timo Teräs 2012-02-16 20:37:03 +02:00
parent 53f8a36c1f
commit b0c0b900db
1 changed files with 188 additions and 136 deletions

View File

@ -38,8 +38,11 @@ struct apk_package_state {
struct apk_package *backtrack; struct apk_package *backtrack;
unsigned int topology_soft; unsigned int topology_soft;
struct apk_score saved_score; struct apk_score saved_score;
unsigned short flags;
unsigned short conflicts; unsigned short conflicts;
unsigned availability_checked : 1;
unsigned unavailable : 1;
unsigned install_applied : 1;
unsigned flags : 4;
}; };
struct apk_name_state { struct apk_name_state {
@ -57,8 +60,8 @@ struct apk_name_state {
unsigned int solver_flags_local_mask : 4; unsigned int solver_flags_local_mask : 4;
unsigned int solver_flags_inherited : 4; unsigned int solver_flags_inherited : 4;
unsigned int availability_checked : 1;
unsigned int locked : 1; unsigned int locked : 1;
unsigned int no_choices_left : 1;
unsigned int in_changeset : 1; unsigned int in_changeset : 1;
}; };
@ -74,16 +77,22 @@ struct apk_solver_state {
struct apk_score minimum_penalty; struct apk_score minimum_penalty;
unsigned int solver_flags : 4; unsigned int solver_flags : 4;
unsigned int refresh_name_states : 1;
struct apk_solution_array *best_solution; struct apk_solution_array *best_solution;
struct apk_score best_score; struct apk_score best_score;
}; };
typedef enum {
SOLVERR_SOLUTION = 0,
SOLVERR_PRUNED,
SOLVERR_EXPAND,
SOLVERR_STOP,
} solver_result_t;
static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep); static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency *dep);
static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *dep); static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *dep);
static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg, static solver_result_t push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
int flags); int flags);
static void addscore(struct apk_score *a, struct apk_score *b) static void addscore(struct apk_score *a, struct apk_score *b)
{ {
@ -258,32 +267,6 @@ static void sort_name(struct apk_solver_state *ss, struct apk_name *name)
sort_soft_dependencies(ss, name->pkgs->item[i]); sort_soft_dependencies(ss, name->pkgs->item[i]);
} }
static void prepare_name(struct apk_solver_state *ss, struct apk_name *name,
struct apk_name_state *ns)
{
int i;
if (ns->availability_checked)
return;
for (i = 0; i < name->pkgs->num; i++) {
struct apk_package *pkg = name->pkgs->item[i];
struct apk_package_state *ps = pkg_to_ps(pkg);
struct apk_name_state *ns = name_to_ns(pkg->name);
/* if package is needed for (re-)install */
if ((pkg->ipkg == NULL) ||
((ns->solver_flags_local | ns->solver_flags_inherited |
ss->solver_flags) & APK_SOLVERF_REINSTALL)) {
/* and it's not available, we can't use it */
if (!pkg_available(ss->db, pkg))
ps->conflicts = 1024;
}
}
ns->availability_checked = 1;
}
static void foreach_locked_reverse_dependency( static void foreach_locked_reverse_dependency(
struct apk_name *name, struct apk_name *name,
void (*cb)(struct apk_package *rdepend, void *ctx), void *ctx) void (*cb)(struct apk_package *rdepend, void *ctx), void *ctx)
@ -428,7 +411,7 @@ static int get_preference(struct apk_solver_state *ss,
continue; continue;
if (installable_only && if (installable_only &&
(ss->topology_position <= pkg0->topology_hard || (ss->topology_position < pkg0->topology_hard ||
(ps0->flags & APK_PKGSTF_DECIDED))) (ps0->flags & APK_PKGSTF_DECIDED)))
continue; continue;
@ -459,6 +442,30 @@ static int install_if_missing(struct apk_solver_state *ss, struct apk_package *p
return missing; return missing;
} }
static int check_if_package_unavailable(struct apk_solver_state *ss, struct apk_package *pkg)
{
struct apk_name *name = pkg->name;
struct apk_package_state *ps = pkg_to_ps(pkg);
struct apk_name_state *ns = name_to_ns(name);
/* installed and no-reinstall required? no check needed. */
if ((pkg->ipkg != NULL) &&
((ns->solver_flags_local | ns->solver_flags_inherited |
ss->solver_flags) & APK_SOLVERF_REINSTALL) == 0)
return 0;
/* done already? */
if (ps->availability_checked)
return ps->unavailable;
/* and it's not available, we can't use it */
if (!pkg_available(ss->db, pkg))
ps->unavailable = 1;
ps->availability_checked = 1;
return ps->unavailable;
}
static int update_name_state(struct apk_solver_state *ss, struct apk_name *name) static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
{ {
struct apk_name_state *ns = name_to_ns(name); struct apk_name_state *ns = name_to_ns(name);
@ -472,6 +479,9 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
unsigned int preferred_repos, allowed_repos; unsigned int preferred_repos, allowed_repos;
int i, options = 0, skipped_options = 0; int i, options = 0, skipped_options = 0;
if (ns->locked)
return ns->chosen != NULL;
preferred_pinning = ns->preferred_pinning ?: APK_DEFAULT_PINNING_MASK; preferred_pinning = ns->preferred_pinning ?: APK_DEFAULT_PINNING_MASK;
preferred_repos = get_pinning_mask_repos(ss->db, preferred_pinning); preferred_repos = get_pinning_mask_repos(ss->db, preferred_pinning);
allowed_pinning = ns->allowed_pinning | preferred_pinning; allowed_pinning = ns->allowed_pinning | preferred_pinning;
@ -488,16 +498,11 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
struct apk_package_state *ps0 = pkg_to_ps(pkg0); struct apk_package_state *ps0 = pkg_to_ps(pkg0);
if (ps0 == NULL || if (ps0 == NULL ||
pkg0->topology_hard >= ss->topology_position || ss->topology_position < pkg0->topology_hard ||
(ps0->flags & APK_PKGSTF_DECIDED)) (ps0->flags & APK_PKGSTF_DECIDED) ||
check_if_package_unavailable(ss, pkg0))
continue; continue;
if ((pkg0->repos != 0) && (pkg0->ipkg == NULL) && (pkg0->filename == NULL) &&
!(pkg0->repos & allowed_repos)) {
skipped_options++;
continue;
}
if ((preferred_pkg == NULL) || if ((preferred_pkg == NULL) ||
(ps0->conflicts < preferred_ps->conflicts) || (ps0->conflicts < preferred_ps->conflicts) ||
(ps0->conflicts == preferred_ps->conflicts && (ps0->conflicts == preferred_ps->conflicts &&
@ -509,6 +514,16 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
preferred_ps = ps0; preferred_ps = ps0;
} }
/* pinning has not enabled the package */
if ((pkg0->repos != 0) && (pkg0->ipkg == NULL) &&
(pkg0->filename == NULL) && !(pkg0->repos & allowed_repos)) {
skipped_options++;
continue;
}
/* not directly required, there's at least one
* valid install_if, but for not this specific pkg;
* this might get enabled later */
if (ns->requirers == 0 && ns->install_ifs != 0 && if (ns->requirers == 0 && ns->install_ifs != 0 &&
install_if_missing(ss, pkg0)) { install_if_missing(ss, pkg0)) {
skipped_options++; skipped_options++;
@ -523,17 +538,6 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
best_pkg = pkg0, best_topology = pkg0->topology_hard; best_pkg = pkg0, best_topology = pkg0->topology_hard;
} }
if (skipped_options == 0 && options == 0) {
if (!ns->locked) {
ss->score.unsatisfiable += ns->requirers;
ss->score.preference += name->pkgs->num;
}
ns->locked = 1;
ns->chosen = NULL;
} else {
ns->locked = 0;
}
if (ns->requirers == 0 && ns->install_ifs == 0) { if (ns->requirers == 0 && ns->install_ifs == 0) {
if (list_hashed(&ns->unsolved_list)) { if (list_hashed(&ns->unsolved_list)) {
list_del(&ns->unsolved_list); list_del(&ns->unsolved_list);
@ -545,32 +549,39 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
} else { } else {
if (!list_hashed(&ns->unsolved_list)) if (!list_hashed(&ns->unsolved_list))
list_add(&ns->unsolved_list, &ss->unsolved_list_head); list_add(&ns->unsolved_list, &ss->unsolved_list_head);
if (!ns->locked) {
ns->chosen = best_pkg; ns->chosen = best_pkg;
if (preferred_pkg != NULL && if (preferred_pkg != NULL &&
preferred_ps->conflicts < ns->requirers) { preferred_ps->conflicts < ns->requirers) {
ns->minimum_penalty = (struct apk_score) { ns->minimum_penalty = (struct apk_score) {
.unsatisfiable = preferred_ps->conflicts, .unsatisfiable = preferred_ps->conflicts,
.preference = get_preference(ss, preferred_pkg, FALSE), .preference = get_preference(ss, preferred_pkg, FALSE),
}; };
dbg_printf("%s: min.penalty for name {%d, %d} from pkg " PKG_VER_FMT "\n", dbg_printf("%s: min.penalty for name {%d, %d} from pkg " PKG_VER_FMT "\n",
name->name, name->name,
ns->minimum_penalty.unsatisfiable, ns->minimum_penalty.unsatisfiable,
ns->minimum_penalty.preference, ns->minimum_penalty.preference,
PKG_VER_PRINTF(preferred_pkg)); PKG_VER_PRINTF(preferred_pkg));
} else { } else {
ns->minimum_penalty = (struct apk_score) { ns->minimum_penalty = (struct apk_score) {
.unsatisfiable = ns->requirers, .unsatisfiable = ns->requirers,
.preference = name->pkgs->num, .preference = name->pkgs->num,
}; };
} dbg_printf("%s: min.penalty for name {%d, %d} from no pkg\n",
addscore(&ss->minimum_penalty, &ns->minimum_penalty); name->name,
ns->minimum_penalty.unsatisfiable,
ns->minimum_penalty.preference);
} }
dbg_printf("%s: added to unsolved: %d requirers, %d install_ifs, %d options (next topology %d)\n", addscore(&ss->minimum_penalty, &ns->minimum_penalty);
name->name, ns->requirers, ns->install_ifs, options,
dbg_printf("%s: added to unsolved: %d requirers, %d install_ifs, %d options, %d skipped options (next topology %d)\n",
name->name, ns->requirers, ns->install_ifs,
options, skipped_options,
best_topology); best_topology);
} }
ns->no_choices_left = (options == 0 && skipped_options == 0);
return options + skipped_options; return options + skipped_options;
} }
@ -600,22 +611,40 @@ static void untrigger_install_if(struct apk_solver_state *ss,
} }
} }
static void apply_decision(struct apk_solver_state *ss, static solver_result_t apply_decision(struct apk_solver_state *ss,
struct apk_package *pkg, struct apk_package *pkg,
struct apk_package_state *ps) struct apk_package_state *ps)
{ {
struct apk_name_state *ns = name_to_ns(pkg->name); struct apk_name *name = pkg->name;
struct apk_name_state *ns = name_to_ns(name);
dbg_printf("apply_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg), dbg_printf("-->apply_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
(ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL"); (ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL");
if (ps->flags & APK_PKGSTF_INSTALL) { if (ps->flags & APK_PKGSTF_INSTALL) {
unsigned short preference;
subscore(&ss->minimum_penalty, &ns->minimum_penalty); subscore(&ss->minimum_penalty, &ns->minimum_penalty);
ns->minimum_penalty = (struct apk_score) { 0, 0 }; ns->minimum_penalty = (struct apk_score) { 0, 0 };
ss->assigned_names++; preference = get_preference(ss, pkg, FALSE);
ss->score.unsatisfiable += ps->conflicts; ss->score.unsatisfiable += ps->conflicts;
ss->score.preference += get_preference(ss, pkg, FALSE); ss->score.preference += preference;
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
dbg_printf("install causing {%d,%d}, penalty too big: {%d,%d}+{%d,%d}>={%d,%d}\n",
ps->conflicts, preference,
ss->score.unsatisfiable, ss->score.preference,
ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference,
ss->best_score.unsatisfiable, ss->best_score.preference
);
ss->score.unsatisfiable -= ps->conflicts;
ss->score.preference -= preference;
return SOLVERR_PRUNED;
}
ps->install_applied = 1;
ss->assigned_names++;
ns->chosen = pkg; ns->chosen = pkg;
ns->locked = 1; ns->locked = 1;
@ -627,8 +656,29 @@ static void apply_decision(struct apk_solver_state *ss,
foreach_dependency(ss, pkg->depends, apply_constraint); foreach_dependency(ss, pkg->depends, apply_constraint);
foreach_rinstall_if_pkg(ss, pkg, trigger_install_if); foreach_rinstall_if_pkg(ss, pkg, trigger_install_if);
} else { } else {
update_name_state(ss, pkg->name); if (ns->locked == 0 && update_name_state(ss, pkg->name) == 0) {
subscore(&ss->minimum_penalty, &ns->minimum_penalty);
ns->minimum_penalty = (struct apk_score) { 0, 0 };
dbg_printf("%s: out of candidates, locking to none\n",
pkg->name->name);
ss->score.unsatisfiable += ns->requirers;
ss->score.preference += name->pkgs->num;
ns->locked = 1;
}
} }
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) {
dbg_printf("install/uninstall finished penalty too big: {%d,%d}+{%d,%d}>={%d,%d}\n",
ss->score.unsatisfiable, ss->score.preference,
ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference,
ss->best_score.unsatisfiable, ss->best_score.preference
);
return SOLVERR_PRUNED;
}
return SOLVERR_EXPAND;
} }
static void undo_decision(struct apk_solver_state *ss, static void undo_decision(struct apk_solver_state *ss,
@ -637,7 +687,7 @@ static void undo_decision(struct apk_solver_state *ss,
{ {
struct apk_name_state *ns = name_to_ns(pkg->name); struct apk_name_state *ns = name_to_ns(pkg->name);
dbg_printf("undo_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg), dbg_printf("-->undo_decision: " PKG_VER_FMT " %s\n", PKG_VER_PRINTF(pkg),
(ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL"); (ps->flags & APK_PKGSTF_INSTALL) ? "INSTALL" : "NO_INSTALL");
if (ps->flags & APK_PKGSTF_INSTALLIF) if (ps->flags & APK_PKGSTF_INSTALLIF)
@ -646,21 +696,28 @@ static void undo_decision(struct apk_solver_state *ss,
ss->topology_position = pkg->topology_hard; ss->topology_position = pkg->topology_hard;
if (ps->flags & APK_PKGSTF_INSTALL) { if (ps->flags & APK_PKGSTF_INSTALL) {
ss->assigned_names--; if (ps->install_applied) {
ps->install_applied = 0;
foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if); ss->assigned_names--;
foreach_dependency(ss, pkg->depends, undo_constraint); foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if);
foreach_dependency(ss, pkg->depends, undo_constraint);
}
ns->locked = 0; ns->locked = 0;
ns->chosen = NULL; ns->chosen = NULL;
} else {
/* UNINSTALL decision removed - either name is unlocked
* or locked to none */
ns->locked = 0;
ns->chosen = NULL;
} }
ss->score = ps->saved_score; ss->score = ps->saved_score;
update_name_state(ss, pkg->name); update_name_state(ss, pkg->name);
} }
static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg, static solver_result_t push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
int flags) int flags)
{ {
struct apk_package_state *ps = pkg_to_ps(pkg); struct apk_package_state *ps = pkg_to_ps(pkg);
@ -678,7 +735,7 @@ static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
} }
ss->latest_decision = pkg; ss->latest_decision = pkg;
dbg_printf("push_decision: adding new BRANCH at topology_position %d (score: unsatisfied %d, preference %d)\n", dbg_printf("-->push_decision: adding new BRANCH at topology_position %d (score: unsatisfied %d, preference %d)\n",
ss->topology_position, ss->topology_position,
ss->score.unsatisfiable, ss->score.unsatisfiable,
ss->score.preference); ss->score.preference);
@ -687,7 +744,7 @@ static void push_decision(struct apk_solver_state *ss, struct apk_package *pkg,
dbg_printf("triggers due to " PKG_VER_FMT "\n", dbg_printf("triggers due to " PKG_VER_FMT "\n",
PKG_VER_PRINTF(pkg)); PKG_VER_PRINTF(pkg));
apply_decision(ss, pkg, ps); return apply_decision(ss, pkg, ps);
} }
static int next_branch(struct apk_solver_state *ss) static int next_branch(struct apk_solver_state *ss)
@ -700,29 +757,27 @@ static int next_branch(struct apk_solver_state *ss)
ps = pkg_to_ps(pkg); ps = pkg_to_ps(pkg);
if (ps->flags & APK_PKGSTF_ALT_BRANCH) { if (ps->flags & APK_PKGSTF_ALT_BRANCH) {
dbg_printf("next_branch: undo decision at topology_position %d\n", dbg_printf("-->next_branch: undo decision at topology_position %d\n",
ss->topology_position); ss->topology_position);
ps->flags &= ~(APK_PKGSTF_ALT_BRANCH | APK_PKGSTF_DECIDED); ps->flags &= ~(APK_PKGSTF_ALT_BRANCH | APK_PKGSTF_DECIDED);
undo_decision(ss, pkg, ps); undo_decision(ss, pkg, ps);
ss->latest_decision = ps->backtrack; ss->latest_decision = ps->backtrack;
ss->refresh_name_states = 1;
} else { } else {
undo_decision(ss, pkg, ps); dbg_printf("-->next_branch: swapping BRANCH at topology_position %d\n",
dbg_printf("next_branch: swapping BRANCH at topology_position %d\n",
ss->topology_position); ss->topology_position);
undo_decision(ss, pkg, ps);
ps->flags |= APK_PKGSTF_ALT_BRANCH; ps->flags |= APK_PKGSTF_ALT_BRANCH;
ps->flags ^= APK_PKGSTF_INSTALL; ps->flags ^= APK_PKGSTF_INSTALL;
apply_decision(ss, pkg, ps); return apply_decision(ss, pkg, ps);
return 0;
} }
} }
dbg_printf("next_branch: no more branches\n"); dbg_printf("-->next_branch: no more branches\n");
return 1; return SOLVERR_STOP;
} }
static void inherit_name_state(struct apk_name *to, struct apk_name *from) static void inherit_name_state(struct apk_name *to, struct apk_name *from)
@ -771,8 +826,6 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
struct apk_name_state *ns = name_to_ns(name); struct apk_name_state *ns = name_to_ns(name);
int i; int i;
prepare_name(ss, name, ns);
if (ns->locked) { if (ns->locked) {
if (ns->chosen) if (ns->chosen)
dbg_printf("%s: locked to " PKG_VER_FMT " already\n", dbg_printf("%s: locked to " PKG_VER_FMT " already\n",
@ -808,8 +861,11 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
} }
} }
if (ss->latest_decision != NULL) if (ss->latest_decision != NULL) {
dbg_printf("%s: inheriting flags and pinning from %s\n",
name->name, ss->latest_decision->name->name);
inherit_name_state(name, ss->latest_decision->name); inherit_name_state(name, ss->latest_decision->name);
}
if (!dep->optional) if (!dep->optional)
ns->requirers++; ns->requirers++;
@ -867,13 +923,12 @@ static int expand_branch(struct apk_solver_state *ss)
/* FIXME: change unsolved_list to a priority queue */ /* FIXME: change unsolved_list to a priority queue */
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) { list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
if (ss->refresh_name_states) if (ns->chosen == NULL && !ns->no_choices_left) {
/* we have not been able to determine candidate
* for the package, but is not yet completely
* excluded either. try updating it. */
update_name_state(ss, ns->name); update_name_state(ss, ns->name);
}
/* ns->chosen can be NULL if the name has only install_if
* requirers that got later conflicted, but it still has
* other options that can get activated later due to more
* complicated install_if rules in some other package. */
if (ns->chosen == NULL) if (ns->chosen == NULL)
continue; continue;
if (pkg_to_ps(ns->chosen)->topology_soft < ss->topology_position && if (pkg_to_ps(ns->chosen)->topology_soft < ss->topology_position &&
@ -882,20 +937,17 @@ static int expand_branch(struct apk_solver_state *ss)
else if (ns->chosen->topology_hard > topology0) else if (ns->chosen->topology_hard > topology0)
pkg0 = ns->chosen, topology0 = pkg0->topology_hard; pkg0 = ns->chosen, topology0 = pkg0->topology_hard;
} }
ss->refresh_name_states = 0;
if (pkg0 == NULL) { if (pkg0 == NULL) {
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) { list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
if (ns->locked) if (ns->locked)
continue; continue;
ss->score.unsatisfiable += ns->requirers; ss->score.unsatisfiable += ns->requirers;
ss->score.preference += ns->name->pkgs->num; ss->score.preference += ns->name->pkgs->num;
} }
dbg_printf("expand_branch: list is empty (%d unsatisfied)\n", dbg_printf("expand_branch: list is empty (%d unsatisfied)\n",
ss->score.unsatisfiable); ss->score.unsatisfiable);
return SOLVERR_SOLUTION;
return 1;
} }
/* someone needs to provide this name -- find next eligible /* someone needs to provide this name -- find next eligible
@ -907,9 +959,8 @@ static int expand_branch(struct apk_solver_state *ss)
flags = APK_PKGSTF_INSTALL; flags = APK_PKGSTF_INSTALL;
else else
flags = APK_PKGSTF_NOINSTALL; flags = APK_PKGSTF_NOINSTALL;
push_decision(ss, pkg0, flags);
return 0; return push_decision(ss, pkg0, flags);
} }
static int get_tag(struct apk_database *db, unsigned short pinning_mask, unsigned int repos) static int get_tag(struct apk_database *db, unsigned short pinning_mask, unsigned int repos)
@ -1113,7 +1164,8 @@ int apk_solver_solve(struct apk_database *db,
struct apk_solver_state *ss; struct apk_solver_state *ss;
struct apk_installed_package *ipkg; struct apk_installed_package *ipkg;
struct apk_score zero_score; struct apk_score zero_score;
int i, r; solver_result_t r = SOLVERR_STOP;
int i;
ss = calloc(1, sizeof(struct apk_solver_state)); ss = calloc(1, sizeof(struct apk_solver_state));
ss->db = db; ss->db = db;
@ -1135,29 +1187,29 @@ int apk_solver_solve(struct apk_database *db,
zero_score.preference); zero_score.preference);
do { do {
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) < 0) { /* need EXPAND if here, can return SOLUTION|PRUNED|EXPAND */
r = expand_branch(ss); r = expand_branch(ss);
if (r) { if (r == SOLVERR_SOLUTION) {
dbg_printf("solution with: %d unsatisfiable, %d preference\n", dbg_printf("solution with: %d unsatisfiable, %d preference\n",
ss->score.unsatisfiable, ss->score.unsatisfiable,
ss->score.preference); ss->score.preference);
if (cmpscore(&ss->score, &ss->best_score) < 0) if (cmpscore(&ss->score, &ss->best_score) < 0)
record_solution(ss); record_solution(ss);
if (cmpscore(&zero_score, &ss->score) >= 0) { if (cmpscore(&zero_score, &ss->score) >= 0) {
/* found solution - it is optimal because we permutate /* found solution - it is optimal because we permutate
* each preferred local option first, and permutations * each preferred local option first, and permutations
* happen in topologally sorted order. */ * happen in topologally sorted order. */
r = 0; break;
break;
}
r = next_branch(ss);
} }
} else { r = SOLVERR_PRUNED;
r = next_branch(ss);
} }
} while (r == 0); /* next_branch() returns PRUNED, STOP or EXPAND */
while (r == SOLVERR_PRUNED)
r = next_branch(ss);
/* STOP or EXPAND */
} while (r != SOLVERR_STOP);
/* collect packages */ /* collect packages */
if (changeset != NULL) { if (changeset != NULL) {
@ -1171,11 +1223,11 @@ int apk_solver_solve(struct apk_database *db,
} else { } else {
apk_solution_array_free(&ss->best_solution); apk_solution_array_free(&ss->best_solution);
} }
r = ss->best_score.unsatisfiable; i = ss->best_score.unsatisfiable;
apk_solver_free(db); apk_solver_free(db);
free(ss); free(ss);
return r; return i;
} }
static void print_change(struct apk_database *db, static void print_change(struct apk_database *db,