solver: get rid of saved score in backtracking

also, discover late if package is needed or not.
cute-signatures
Timo Teräs 2012-02-17 09:43:14 +02:00
parent 4bc8add78d
commit 15c920ab90
1 changed files with 58 additions and 42 deletions

View File

@ -35,7 +35,6 @@ struct apk_score {
struct apk_package_state { 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;
unsigned short conflicts; unsigned short conflicts;
unsigned availability_checked : 1; unsigned availability_checked : 1;
unsigned unavailable : 1; unsigned unavailable : 1;
@ -441,7 +440,7 @@ 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) static int check_if_package_unavailable(struct apk_solver_state *ss, struct apk_package *pkg, int do_check)
{ {
struct apk_name *name = pkg->name; struct apk_name *name = pkg->name;
struct apk_package_state *ps = pkg_to_ps(pkg); struct apk_package_state *ps = pkg_to_ps(pkg);
@ -454,7 +453,7 @@ static int check_if_package_unavailable(struct apk_solver_state *ss, struct apk_
return 0; return 0;
/* done already? */ /* done already? */
if (ps->availability_checked) if (ps->availability_checked && !do_check)
return ps->unavailable; return ps->unavailable;
/* and it's not available, we can't use it */ /* and it's not available, we can't use it */
@ -499,7 +498,7 @@ static int update_name_state(struct apk_solver_state *ss, struct apk_name *name)
if (ps0 == NULL || if (ps0 == NULL ||
ss->topology_position < pkg0->topology_hard || ss->topology_position < pkg0->topology_hard ||
ps0->locked || ps0->locked ||
check_if_package_unavailable(ss, pkg0)) check_if_package_unavailable(ss, pkg0, 0))
continue; continue;
options++; options++;
@ -600,21 +599,21 @@ static solver_result_t apply_decision(struct apk_solver_state *ss,
{ {
struct apk_name *name = pkg->name; struct apk_name *name = pkg->name;
struct apk_name_state *ns = name_to_ns(name); struct apk_name_state *ns = name_to_ns(name);
unsigned short preference;
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 };
preference = get_preference(ss, pkg, FALSE); preference = get_preference(ss, pkg, FALSE);
ss->score.unsatisfiable += ps->conflicts; ss->score.unsatisfiable += ps->conflicts;
ss->score.preference += preference; ss->score.preference += preference;
if (cmpscore2(&ss->score, &ss->minimum_penalty, &ss->best_score) >= 0) { 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", check_if_package_unavailable(ss, pkg, 1)) {
dbg_printf("install causing {%d,%d}, penalty too big (or unavailable %d): {%d,%d}+{%d,%d}>={%d,%d}\n",
ps->conflicts, preference, ps->conflicts, preference,
ss->score.unsatisfiable, ss->score.preference, ss->score.unsatisfiable, ss->score.preference,
ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference, ss->minimum_penalty.unsatisfiable, ss->minimum_penalty.preference,
@ -639,7 +638,7 @@ static solver_result_t 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 {
if (ns->locked == 0 && update_name_state(ss, pkg->name) == 0) { if (update_name_state(ss, pkg->name) == 0) {
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 };
@ -668,7 +667,8 @@ static void undo_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("-->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");
@ -680,22 +680,28 @@ static void undo_decision(struct apk_solver_state *ss,
if (ps->flags & APK_PKGSTF_INSTALL) { if (ps->flags & APK_PKGSTF_INSTALL) {
if (ps->install_applied) { if (ps->install_applied) {
unsigned short preference;
ps->install_applied = 0; ps->install_applied = 0;
ss->assigned_names--; ss->assigned_names--;
foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if); foreach_rinstall_if_pkg(ss, pkg, untrigger_install_if);
foreach_dependency(ss, pkg->depends, undo_constraint); foreach_dependency(ss, pkg->depends, undo_constraint);
preference = get_preference(ss, pkg, FALSE);
ss->score.unsatisfiable -= ps->conflicts;
ss->score.preference -= preference;
} }
ns->locked = 0;
ns->chosen = NULL;
} else { } else {
/* UNINSTALL decision removed - either name is unlocked if (ns->locked) {
* or locked to none */ /* UNINSTALL decision removed - either name is unlocked
ns->locked = 0; * or locked to none */
ns->chosen = NULL; ss->score.unsatisfiable -= ns->requirers;
ss->score.preference -= name->pkgs->num;
}
} }
ns->locked = 0;
ns->chosen = NULL;
ss->score = ps->saved_score;
update_name_state(ss, pkg->name); update_name_state(ss, pkg->name);
} }
@ -705,16 +711,15 @@ static solver_result_t push_decision(struct apk_solver_state *ss, struct apk_pac
struct apk_package_state *ps = pkg_to_ps(pkg); struct apk_package_state *ps = pkg_to_ps(pkg);
ps->backtrack = ss->latest_decision; ps->backtrack = ss->latest_decision;
ps->locked = 1;
ps->flags = flags; ps->flags = flags;
ps->saved_score = ss->score; ps->locked = 1;
ps->handle_install_if = 0;
if (ps->topology_soft < ss->topology_position) { if (ps->topology_soft < ss->topology_position) {
if (flags & APK_PKGSTF_INSTALL) if (flags & APK_PKGSTF_INSTALL)
ps->handle_install_if = 1; ps->handle_install_if = 1;
ss->topology_position = ps->topology_soft; ss->topology_position = ps->topology_soft;
} else { } else {
ps->handle_install_if = 0;
ss->topology_position = pkg->topology_hard; ss->topology_position = pkg->topology_hard;
} }
ss->latest_decision = pkg; ss->latest_decision = pkg;
@ -814,14 +819,19 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
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",
dep->name->name, PKG_VER_PRINTF(ns->chosen)); name->name, PKG_VER_PRINTF(ns->chosen));
else else
dbg_printf("%s: locked to empty\n", dbg_printf("%s: locked to empty\n",
dep->name->name); name->name);
if (!apk_dep_is_satisfied(dep, ns->chosen)) if (!apk_dep_is_satisfied(dep, ns->chosen))
ss->score.unsatisfiable++; ss->score.unsatisfiable++;
return; return;
} }
if (name->pkgs->num == 0) {
if (!dep->optional)
ss->score.unsatisfiable++;
return;
}
if (dep->repository_tag) { if (dep->repository_tag) {
dbg_printf("%s: adding pinnings %d\n", dbg_printf("%s: adding pinnings %d\n",
@ -830,6 +840,12 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
ns->allowed_pinning |= BIT(dep->repository_tag); ns->allowed_pinning |= BIT(dep->repository_tag);
} }
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);
}
for (i = 0; i < name->pkgs->num; i++) { for (i = 0; i < name->pkgs->num; i++) {
struct apk_package *pkg0 = name->pkgs->item[i]; struct apk_package *pkg0 = name->pkgs->item[i];
struct apk_package_state *ps0 = pkg_to_ps(pkg0); struct apk_package_state *ps0 = pkg_to_ps(pkg0);
@ -846,12 +862,6 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_dependency
} }
} }
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);
}
if (!dep->optional) if (!dep->optional)
ns->requirers++; ns->requirers++;
@ -867,11 +877,18 @@ static void undo_constraint(struct apk_solver_state *ss, struct apk_dependency *
if (ns->locked) { if (ns->locked) {
if (ns->chosen != NULL) { if (ns->chosen != NULL) {
dbg_printf(PKG_VER_FMT " selected already for %s\n", dbg_printf(PKG_VER_FMT " selected already for %s\n",
PKG_VER_PRINTF(ns->chosen), dep->name->name); PKG_VER_PRINTF(ns->chosen), name->name);
} else { } else {
dbg_printf("%s selected to not be satisfied\n", dbg_printf("%s selected to not be satisfied\n",
dep->name->name); name->name);
} }
if (!apk_dep_is_satisfied(dep, ns->chosen))
ss->score.unsatisfiable--;
return;
}
if (name->pkgs->num == 0) {
if (!dep->optional)
ss->score.unsatisfiable--;
return; return;
} }
@ -919,13 +936,6 @@ static int expand_branch(struct apk_solver_state *ss)
pkg0 = ns->chosen, topology0 = pkg0->topology_hard; pkg0 = ns->chosen, topology0 = pkg0->topology_hard;
} }
if (pkg0 == NULL) { if (pkg0 == NULL) {
list_for_each_entry(ns, &ss->unsolved_list_head, unsolved_list) {
if (ns->locked)
continue;
ss->score.unsatisfiable += ns->requirers;
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 SOLVERR_SOLUTION;
@ -1011,7 +1021,6 @@ static void record_solution(struct apk_solver_state *ss)
pkg = ps->backtrack; pkg = ps->backtrack;
} }
apk_solution_array_resize(&ss->best_solution, i); apk_solution_array_resize(&ss->best_solution, i);
ss->best_score = ss->score;
} }
static int compare_solution_entry(const void *p1, const void *p2) static int compare_solution_entry(const void *p1, const void *p2)
@ -1185,14 +1194,21 @@ int apk_solver_solve(struct apk_database *db,
/* need EXPAND if here, can return SOLUTION|PRUNED|EXPAND */ /* need EXPAND if here, can return SOLUTION|PRUNED|EXPAND */
r = expand_branch(ss); r = expand_branch(ss);
if (r == SOLVERR_SOLUTION) { if (r == SOLVERR_SOLUTION) {
struct apk_score score;
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) score = ss->score;
record_solution(ss); addscore(&score, &ss->minimum_penalty);
if (cmpscore(&zero_score, &ss->score) >= 0) { if (cmpscore(&score, &ss->best_score) < 0) {
record_solution(ss);
ss->best_score = score;
}
if (cmpscore(&zero_score, &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. */