Skip to content

Commit

Permalink
Merge pull request #5510 from kit-ty-kate/solver-tolerance
Browse files Browse the repository at this point in the history
Add the OPAMSOLVERTOLERANCE environment variable to allow users to fix solver timeouts for good
  • Loading branch information
rjbou authored Feb 5, 2025
2 parents de8748b + abef075 commit 30d4706
Show file tree
Hide file tree
Showing 14 changed files with 43 additions and 11 deletions.
1 change: 1 addition & 0 deletions doc/pages/Tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ The following sequence of commands tries to install as much packages as possible
opam update
opam switch create . ocaml-base-compiler.$(VERSION)
export OPAMSOLVERTIMEOUT=3600
export OPAMSOLVERTOLERANCE=1.0
opam list --available -s | xargs opam install --best-effort --yes
# Be patient
```
1 change: 1 addition & 0 deletions master_changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ users)
## Clean

## Env
* Add the `OPAMSOLVERTOLERANCE` environment variable to allow users to fix solver timeouts for good [#5510 @kit-ty-kate - fix #3230]

## Opamfile

Expand Down
10 changes: 10 additions & 0 deletions src/client/opamArg.ml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ let environment_variables =
"change the time allowance of the solver. Default is %.1f, set to 0 \
for unlimited. Note that all solvers may not support this option."
(OpamStd.Option.default 0. OpamSolverConfig.(default.solver_timeout)));
"SOLVERTOLERANCE", cli_from cli2_4, (fun v -> SOLVERTOLERANCE (env_float v)),
(Printf.sprintf
"changes the tolerance towards the solver choosing an unoptimized \
solution (i.e. might pull outdated packages). Typical values range \
from 0.0 (best solution known to the solver) to 1.0 (unoptimized \
solution). Default is %.1f. This option is useful in case the solver \
can't find a solution in a reasonable time \
(see $(b,\\$OPAMSOLVERTIMEOUT)). Note that all solvers may not \
support this option."
(OpamStd.Option.default 0. OpamSolverConfig.default.solver_tolerance));
"UPGRADECRITERIA", cli_original,
(fun v -> UPGRADECRITERIA (env_string v)),
"specifies user $(i,preferences) for dependency solving when performing \
Expand Down
1 change: 1 addition & 0 deletions src/client/opamClientConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ val opam_init:
?solver_preferences_fixup:string option Lazy.t ->
?solver_preferences_best_effort_prefix: string option Lazy.t ->
?solver_timeout:float option ->
?solver_tolerance:float option ->
?solver_allow_suboptimal:bool ->
?cudf_trim:string option ->
?dig_depth:int ->
Expand Down
2 changes: 1 addition & 1 deletion src/solver/opamBuiltin0install.ml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ let parse_criteria criteria =
in
parse default (OpamCudfCriteria.of_string criteria)

let call ~criteria ?timeout:_ (preamble, universe, request) =
let call ~criteria ?timeout:_ ?tolerance:_ (preamble, universe, request) =
let {
drop_installed_packages;
prefer_oldest;
Expand Down
2 changes: 1 addition & 1 deletion src/solver/opamBuiltinMccs.dummy.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module S = struct
crit_best_effort_prefix = None;
}

let call ~criteria:_ ?timeout:_ _cudf =
let call ~criteria:_ ?timeout:_ ?tolerance:_ _cudf =
failwith "This opam was compiled without a solver built in"
end

Expand Down
5 changes: 3 additions & 2 deletions src/solver/opamBuiltinMccs.real.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ let default_criteria = {
crit_best_effort_prefix = Some "+count[opam-query:,false],";
}

let call solver_backend ext ~criteria ?timeout cudf =
let call solver_backend ext ~criteria ?timeout ?tolerance cudf =
let solver = match solver_backend, ext with
| `LP _, Some ext -> `LP ext
| _ -> solver_backend
in
match
Mccs.resolve_cudf
~solver
~verbose:OpamCoreConfig.(abs !r.debug_level >= 2)
?mip_gap:tolerance
~verbosity:(!OpamCoreConfig.r.debug_level - 1) (* *)
?timeout criteria cudf
with
| None -> raise Dose_common.CudfSolver.Unsat
Expand Down
2 changes: 1 addition & 1 deletion src/solver/opamBuiltinZ3.dummy.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ let default_criteria = {
crit_best_effort_prefix = None;
}

let call ~criteria:_ ?timeout:_ _cudf =
let call ~criteria:_ ?timeout:_ ?tolerance:_ _cudf =
failwith "This opam was compiled without the Z3 solver built in"
3 changes: 2 additions & 1 deletion src/solver/opamBuiltinZ3.real.ml
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,14 @@ let extract_solution_packages universe opt =
[]
| None -> failwith "no model ??"

let call ~criteria ?timeout (preamble, universe, _ as cudf) =
let call ~criteria ?timeout ?tolerance:_ (preamble, universe, _ as cudf) =
(* try *)
log "Generating problem...";
let cfg = match timeout with
| None -> []
| Some secs -> ["timeout", string_of_int (int_of_float (1000. *. secs))]
in
(* TODO: use tolerance. Maybe the rlimit cfg option + intermediate solution could be used *)
let ctx = {
z3 = Z3.mk_context cfg;
pkgs = Hashtbl.create 2731;
Expand Down
7 changes: 5 additions & 2 deletions src/solver/opamCudf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1595,9 +1595,12 @@ let call_external_solver ~version_map univ req =
let msg =
Printf.sprintf
"Sorry, resolution of the request timed out.\n\
Try to specify a more precise request, use a different solver, or \
increase the allowed time by setting OPAMSOLVERTIMEOUT to a bigger \
Try to specify a more precise request, use a different solver, \
increase the tolerance for unoptimized solutions by setting \
OPAMSOLVERTOLERANCE to a bigger value (currently, it is set to %.1f) \
or increase the allowed time by setting OPAMSOLVERTIMEOUT to a bigger \
value (currently, it is set to %.1f seconds)."
OpamSolverConfig.(OpamStd.Option.default 0. !r.solver_tolerance)
OpamSolverConfig.(OpamStd.Option.default 0. !r.solver_timeout)
in
raise (Solver_failure msg)
Expand Down
2 changes: 1 addition & 1 deletion src/solver/opamCudfSolver.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module type ExternalArg = sig
val default_criteria: criteria_def
end

let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) =
let call_external_solver command ~criteria ?timeout ?tolerance:_ (_, universe,_ as cudf) =
let solver_in =
OpamFilename.of_string (OpamSystem.temp_file "solver-in") in
let solver_out =
Expand Down
2 changes: 1 addition & 1 deletion src/solver/opamCudfSolverSig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module type S = sig
it's only run if the solver returns unsat, to extract the explanations. *)

val call:
criteria:string -> ?timeout:float -> Cudf.cudf ->
criteria:string -> ?timeout:float -> ?tolerance:float -> Cudf.cudf ->
Cudf.preamble option * Cudf.universe

end
13 changes: 12 additions & 1 deletion src/solver/opamSolverConfig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module E = struct
| PREPRO of bool option
| SOLVERALLOWSUBOPTIMAL of bool option
| SOLVERTIMEOUT of float option
| SOLVERTOLERANCE of float option
| UPGRADECRITERIA of string option
| USEINTERNALSOLVER of bool option
| VERSIONLAGPOWER of int option
Expand All @@ -42,6 +43,7 @@ module E = struct
let solverallowsuboptimal =
value (function SOLVERALLOWSUBOPTIMAL b -> b | _ -> None)
let solvertimeout = value (function SOLVERTIMEOUT f -> f | _ -> None)
let solvertolerance = value (function SOLVERTOLERANCE f -> f | _ -> None)
let useinternalsolver = value (function USEINTERNALSOLVER b -> b | _ -> None)
let upgradecriteria = value (function UPGRADECRITERIA s -> s | _ -> None)
let versionlagpower = value (function VERSIONLAGPOWER i -> i | _ -> None)
Expand All @@ -59,6 +61,7 @@ type t = {
solver_preferences_fixup: string option Lazy.t;
solver_preferences_best_effort_prefix: string option Lazy.t;
solver_timeout: float option;
solver_tolerance: float option;
solver_allow_suboptimal: bool;
cudf_trim: string option;
dig_depth: int;
Expand All @@ -75,6 +78,7 @@ type 'a options_fun =
?solver_preferences_fixup:string option Lazy.t ->
?solver_preferences_best_effort_prefix:string option Lazy.t ->
?solver_timeout:float option ->
?solver_tolerance:float option ->
?solver_allow_suboptimal:bool ->
?cudf_trim:string option ->
?dig_depth:int ->
Expand All @@ -95,6 +99,7 @@ let default =
solver_preferences_fixup = lazy None;
solver_preferences_best_effort_prefix = lazy None;
solver_timeout = Some 60.;
solver_tolerance = Some 0.0;
solver_allow_suboptimal = true;
cudf_trim = None;
dig_depth = 2;
Expand All @@ -111,6 +116,7 @@ let setk k t
?solver_preferences_fixup
?solver_preferences_best_effort_prefix
?solver_timeout
?solver_tolerance
?solver_allow_suboptimal
?cudf_trim
?dig_depth
Expand All @@ -133,6 +139,8 @@ let setk k t
solver_preferences_best_effort_prefix;
solver_timeout =
t.solver_timeout + solver_timeout;
solver_tolerance =
t.solver_tolerance + solver_tolerance;
solver_allow_suboptimal =
t.solver_allow_suboptimal + solver_allow_suboptimal;
cudf_trim = t.cudf_trim + cudf_trim;
Expand Down Expand Up @@ -193,6 +201,8 @@ let initk k =
E.besteffortprefixcriteria () >>| fun c -> (lazy (Some c)) in
let solver_timeout =
E.solvertimeout () >>| fun f -> if f <= 0. then None else Some f in
let solver_tolerance =
E.solvertolerance () >>| fun f -> if f <= 0. then None else Some f in
setk (setk (fun c -> r := with_auto_criteria c; k)) !r
~cudf_file:(E.cudffile ())
~solver
Expand All @@ -202,6 +212,7 @@ let initk k =
?solver_preferences_fixup:fixup_criteria
?solver_preferences_best_effort_prefix:best_effort_prefix_criteria
?solver_timeout
?solver_tolerance
?solver_allow_suboptimal:(E.solverallowsuboptimal ())
~cudf_trim:(E.cudftrim ())
?dig_depth:(E.digdepth ())
Expand Down Expand Up @@ -253,6 +264,6 @@ let call_solver ~criteria cudf =
OpamConsole.log "SOLVER" "Calling solver %s with criteria %s"
(OpamCudfSolver.get_name (module S)) criteria;
let chrono = OpamConsole.timer () in
let r = S.call ~criteria ?timeout:(!r.solver_timeout) cudf in
let r = S.call ~criteria ?timeout:(!r.solver_timeout) ?tolerance:(!r.solver_tolerance) cudf in
OpamConsole.log "SOLVER" "External solver took %.3fs" (chrono ());
r
3 changes: 3 additions & 0 deletions src/solver/opamSolverConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module E : sig
| PREPRO of bool option
| SOLVERALLOWSUBOPTIMAL of bool option
| SOLVERTIMEOUT of float option
| SOLVERTOLERANCE of float option
| UPGRADECRITERIA of string option
| USEINTERNALSOLVER of bool option
| VERSIONLAGPOWER of int option
Expand All @@ -40,6 +41,7 @@ type t = private {
solver_preferences_fixup: string option Lazy.t;
solver_preferences_best_effort_prefix: string option Lazy.t;
solver_timeout: float option;
solver_tolerance: float option;
solver_allow_suboptimal: bool;
cudf_trim: string option;
dig_depth: int;
Expand All @@ -56,6 +58,7 @@ type 'a options_fun =
?solver_preferences_fixup:string option Lazy.t ->
?solver_preferences_best_effort_prefix:string option Lazy.t ->
?solver_timeout:float option ->
?solver_tolerance:float option ->
?solver_allow_suboptimal:bool ->
?cudf_trim:string option ->
?dig_depth:int ->
Expand Down

0 comments on commit 30d4706

Please sign in to comment.