:- module(branch_and_bound).
:- export bb_probe / 7.
:- export bb_finish / 1.
:- export bb_solution / 2.
:- export bb_cost / 2.
:- export bb_init / 2.
:- export minimize / 3.
:- export minimize / 2.
:- export bb_min / 7.
:- export bb_min / 6.
:- export bb_min / 4.
:- export bb_min / 3.
:- export struct(bb_options(strategy, from, to, delta, factor, timeout, probe_timeout, report_success, report_failure)).
:- tool(minimize / 2,minimize / 3).
:- tool(bb_min / 6,bb_min / 7).
:- tool(bb_min / 3,bb_min / 4).
:- comment(minimize / 3, hidden).
:- comment(bb_min / 7, hidden).
:- comment(bb_min / 4, hidden).
:- comment(summary, "Generic branch-and-bound primitives").
:- comment(copyright, "Cisco Systems, Inc").
:- comment(author, "Joachim Schimpf, Vassilis Liatsos, IC-Parc, Imperial College, London").
:- comment(date, "$Date: 2008/03/07 15:19:46 $").
:- comment(index, ["branch-and-bound", "dichotomic search"]).
:- comment(desc, html("\n\tThis is a solver-independent library implementing branch-and-bound\n\tprimitives. It can be used with any nondeterministic search routine\n\tthat instantiates a cost variable when a solution is found. The cost\n\tvariable can be an arbitrary numerical domain variable or even a\n\tsimple domain-less variable.\n\t<P>\n\tThe main predicates are bb_min/3, bb_min/6 and, as a shorthand,\n\tminimize/2.\n\t<P>\n\tNote on the treatment of bounded reals: The library allows the cost\n\tto be instantiated to a number of type breal. This is useful e.g.\n\twhen using lib(ic) to solve problems with continuous variables.\n\tWhen the variable domains have been narrowed sufficiently, the\n\tproblem variables (in particular the cost variable) should be\n\tinstantiated to a bounded real, e.g. using the following idiom:\n\t<PRE>\n\t\tX is breal_from_bounds(get_min(X),get_max(X))\n\t</PRE>\n\tBounded reals contain some uncertainty about their true value. If\n\tthis uncertainty is too large, the branch-and-bound procedure may\n\tnot be able to compare the quality of two solutions. In this case,\n\ta warning is issued and the search terminated prematurely.  The\n\tproblem can be solved by increasing the delta-parameter, or by\n\tlocating the cost value more precisely.\n\t")).
:- comment(bb_cost / 2, [summary : "Low-level primitive for building branch-and-bound-style search procedures", template : "bb_cost(++Handle, -Cost)", see_also : [bb_probe / 7]]).
:- comment(bb_finish / 1, [summary : "Low-level primitive for building branch-and-bound-style search procedures", template : "bb_finish(++Handle)", see_also : [bb_probe / 7]]).
:- comment(bb_init / 2, [summary : "Low-level primitive for building branch-and-bound-style search procedures", template : "bb_init(++ExtremeCost, -Handle)", see_also : [bb_probe / 7]]).
:- comment(bb_min / 3, [summary : "Find a minimal solution using the branch-and-bound method", see_also : [bb_min / 6], desc : html("\tA solution of the goal <EM>Goal</EM> is found that minimizes\n\tthe value of <EM>Cost</EM>.  <EM>Cost</EM> should be a\n\tvariable that is affected, and eventually instantiated, by\n\t<EM>Goal</EM>.  Usually, <EM>Goal</EM> is the search procedure\n\tof a constraint problem and <EM>Cost</EM> is the variable\n\trepresenting the cost.  The solution is found using the branch\n\tand bound method:  as soon as a solution is found, it gets\n\tremembered and the search is continued or restarted with an\n\tadditional constraint on the <EM>Cost</EM> variable which\n\trequires the next solution to be better than the previous one. \n\tIterating this process yields an optimal solution in the end.\n\t<P>\n\tThe possible options are\n\t<DL>\n\t<DT><STRONG>strategy:</STRONG></DT><DD>\n\t    <DL>\n\t    <DT>continue (default)</DT>\n\t    \t<DD>after finding a solution, continue search with the newly\n\t\tfound bound imposed on Cost</DD>\n\t    <DT>restart</DT>\n\t    \t<DD>after finding a solution, restart the whole search with\n\t\tthe newly found bound imposed on Cost</DD>\n\t    <DT>step</DT>\n\t    \t<DD>a synonym for 'restart'</DD>\n\t    <DT>dichotomic</DT>\n\t    \t<DD>after finding a solution, split the remaining cost range\n\t\tand restart search to find a solution in the lower sub-range.\n\t\tIf that fails, assume the upper sub-range as the remaining\n\t\tcost range and split again.</DD>\n\t    </DL>\n\t    The new bound or the split point, respectively, are computed\n\t    from the current best solution, while taking into account the\n\t    parameters delta and factor.\n\t    </DD>\n\t<DT><STRONG>from:</STRONG></DT>\n\t    <DD>number - an initial lower bound for the cost (default -1.0Inf)</DD>\n\n\t<DT><STRONG>to:</STRONG></DT>\n\t    <DD>number - an initial upper bound for the cost (default +1.0Inf)</DD>\n\n\t<DT><STRONG>delta:</STRONG></DT>\t\n\t    <DD>number - minimal absolute improvement required for each step\n\t    (default 1.0), applies to all strategies</DD>\n\n\t<DT><STRONG>factor:</STRONG></DT>\n\t    <DD>number - minimal improvement ratio (with respect to the lower\n\t    cost bound) for strategies 'continue' and 'restart' (default 1.0),\n\t    or split factor for strategy 'dichotomic' (default 0.5)</DD>\n\n\t<DT><STRONG>timeout:</STRONG></DT>\n\t    <DD>number - maximum seconds of cpu time to spend (default: no limit)</DD>\n\n\t<DT><STRONG>report_success:</STRONG></DT>\n\t    <DD>GoalPrefix - an atom (predicate name) or structure (goal prefix),\n\t    specifying a goal to be invoked whenever the branch-and-bound\n\t    process finds a better solution.  The invoked goal is constructed\n\t    by adding three arguments (Cost, Handle, Module) to GoalPrefix.\n\t    Cost is a float number representing the cost of the solution found,\n\t    Handle is a handle as accepted by bb_cost/2 or bb_solution/2,\n\t    and Module is the context module of the minimisation.\n\t    The default handler prints a message.</DD>\n\n\t<DT><STRONG>report_failure:</STRONG></STRONG></DT>\n\t    <DD>GoalPrefix - an atom (predicate name) or structure (goal prefix),\n\t    specifying a goal to be invoked whenever the branch-and-bound\n\t    process cannot find a solution in a cost range.  The invoked goal\n\t    is constructed by adding three arguments (Cost, Handle, Module) to\n\t    GoalPrefix.  Cost is a From..To structure representing the range\n\t    of cost in which no solution could be found, Handle is a handle\n\t    as accepted by bb_cost/2 or bb_solution/2, and Module is the\n\t    context module of the minimisation.\n\t    The default handler prints a message.</DD>\n\t</DL>\n\tThe default options can be selected by passing a free variable as\n\tthe Options-argument. To specify other options, pass a bb_options-\n\tstructure in struct-syntax, e.g.\n\t<PRE>\n\tbb_options{strategy:dichotomic, timeout:60}\n\t</PRE>\n\tIn order to maximize instead of minimizing, introduce a negated\n\tcost variable in your model and minimize that instead.\n\t<P>\n\tCompatibility note: For backward compatibility, the report_success and\n\treport_failure options also accept Name/Arity specifications with\n\tmaximum arity 3 for the handler goals. The three optional arguments\n\tare then Cost, Handle, and Module.\n\t"), args : ["Goal" : "The (nondeterministic) search goal", "Cost" : "A (usually numeric domain) variable representing the cost", "Options" : "A bb_options structure or variable"], amode : bb_min(+, ?, ?), eg : "\n?- bb_min(member(X,[9,6,8,4,7,2,4,7]), X, O).\nFound a solution with cost 9\nFound a solution with cost 6\nFound a solution with cost 4\nFound a solution with cost 2\nFound no solution with cost -1.0Inf .. 1\nX = 2\nO = bb_options(continue, -1.0Inf, 1.0Inf, 1, 1, 0, 0, _, _)\nyes.\n\n[eclipse 6]: bb_min(member(X,[9,6,8,4,7,2,4,7]), X, bb_options{delta:4}).\nFound a solution with cost 9\nFound a solution with cost 4\nFound no solution with cost -1.0Inf .. 0\nX = 4\nyes.\n\n[eclipse 10]: bb_min(member(X,[99,60,80,40,70,30,70]), X,\n\tbb_options{strategy:dichotomic, from:0}).\nFound a solution with cost 99\nFound a solution with cost 40\nFound no solution with cost 0.0 .. 20.0\nFound a solution with cost 30\nFound no solution with cost 20.0 .. 25.0\nFound no solution with cost 25.0 .. 27.5\nFound no solution with cost 27.5 .. 28.75\nFound no solution with cost 28.75 .. 29.0\n\nX = 30\nyes.\n"]).
:- comment(bb_min / 6, [summary : "Find a minimal solution using the branch-and-bound method", see_also : [bb_min / 3], desc : html("\tA solution of the goal <EM>Goal</EM> is found that minimizes\n\tthe value of <EM>Cost</EM>.  <EM>Cost</EM> should be a\n\tvariable that is affected, and eventually instantiated, by\n\t<EM>Goal</EM>.  Usually, <EM>Goal</EM> is the search procedure\n\tof a constraint problem and <EM>Cost</EM> is the variable\n\trepresenting the cost.  The solution is found using the branch\n\tand bound method:  as soon as a solution is found, it gets\n\tremembered and the search is continued or restarted with an\n\tadditional constraint on the <EM>Cost</EM> variable which\n\trequires the next solution to be better than the previous one. \n\tIterating this process yields an optimal solution in the end.\n\t<P>\n\tThe possible options are\n\t<DL>\n\t<DT><STRONG>strategy:</STRONG></DT><DD>\n\t    <DL>\n\t    <DT>continue (default)</DT>\n\t    \t<DD>after finding a solution, continue search with the newly\n\t\tfound bound imposed on Cost</DD>\n\t    <DT>restart</DT>\n\t    \t<DD>after finding a solution, restart the whole search with\n\t\tthe newly found bound imposed on Cost</DD>\n\t    <DT>step</DT>\n\t    \t<DD>a synonym for 'restart'</DD>\n\t    <DT>dichotomic</DT>\n\t    \t<DD>after finding a solution, split the remaining cost range\n\t\tand restart search to find a solution in the lower sub-range.\n\t\tIf that fails, assume the upper sub-range as the remaining\n\t\tcost range and split again.</DD>\n\t    </DL>\n\t    The new bound or the split point, respectively, are computed\n\t    from the current best solution, while taking into account the\n\t    parameters delta and factor.\n\t    </DD>\n\t<DT><STRONG>from:</STRONG></DT>\n\t    <DD>number - an initial lower bound for the cost (default -1.0Inf)</DD>\n\n\t<DT><STRONG>to:</STRONG></DT>\n\t    <DD>number - an initial upper bound for the cost (default +1.0Inf)</DD>\n\n\t<DT><STRONG>delta:</STRONG></DT>\t\n\t    <DD>number - minimal absolute improvement required for each step\n\t    (default 1.0), applies to all strategies</DD>\n\n\t<DT><STRONG>factor:</STRONG></DT>\n\t    <DD>number - minimal improvement ratio (with respect to the lower\n\t    cost bound) for strategies 'continue' and 'restart' (default 1.0),\n\t    or split factor for strategy 'dichotomic' (default 0.5)</DD>\n\n\t<DT><STRONG>timeout:</STRONG></DT>\n\t    <DD>number - maximum seconds of cpu time to spend (default: no limit)</DD>\n\n\t<DT><STRONG>report_success:</STRONG></DT>\n\t    <DD>GoalPrefix - an atom (predicate name) or structure (goal prefix),\n\t    specifying a goal to be invoked whenever the branch-and-bound\n\t    process finds a better solution.  The invoked goal is constructed\n\t    by adding three arguments (Cost, Handle, Module) to GoalPrefix.\n\t    Cost is a float number representing the cost of the solution found,\n\t    Handle is a handle as accepted by bb_cost/2 or bb_solution/2,\n\t    and Module is the context module of the minimisation.\n\t    The default handler prints a message.</DD>\n\n\t<DT><STRONG>report_failure:</STRONG></STRONG></DT>\n\t    <DD>GoalPrefix - an atom (predicate name) or structure (goal prefix),\n\t    specifying a goal to be invoked whenever the branch-and-bound\n\t    process cannot find a solution in a cost range.  The invoked goal\n\t    is constructed by adding three arguments (Cost, Handle, Module) to\n\t    GoalPrefix.  Cost is a From..To structure representing the range\n\t    of cost in which no solution could be found, Handle is a handle\n\t    as accepted by bb_cost/2 or bb_solution/2, and Module is the\n\t    context module of the minimisation.\n\t    The default handler prints a message.</DD>\n\t</DL>\n\tThe default options can be selected by passing a free variable as\n\tthe Options-argument. To specify other options, pass a bb_options-\n\tstructure in with-syntax, e.g.\n\t<PRE>\n\tbb_options{strategy:dichotomic, timeout:60}\n\t</PRE>\n\tIn order to maximize instead of minimizing, introduce a negated\n\tcost variable in your model and minimize that instead.\n\t<P>\n\tUnlike bb_min/3, bb_min/6 does <STRONG>not</STRONG> affect Goal or Cost after\n\tthe optimum has been found. Instead, the optimum cost value is returned\n\tin Optimum, and the Solution argument gets unified with an instance of\n\tTemplate where the variables have the values that correspond to the\n\toptimal solution. Note that bb_min/3 is actually based on bb_min/6\n\tand can be defined as:\n\t<PRE>\n\tbb_min(Goal, Cost, Options) :-\n\t    bb_min(Goal, Cost, Goal, Goal, Cost, Options).\n\t</PRE>\n\t<P>\n\tCompatibility note: For backward compatibility, the report_success and\n\treport_failure options also accept Name/Arity specifications with\n\tmaximum arity 3 for the handler goals. The three optional arguments\n\tare then Cost, Handle, and Module.\n\t"), args : ["Goal" : "The (nondeterministic) search goal", "Cost" : "A (usually numeric domain) variable representing the cost", "Template" : "A term containing all or some problem variables", "Solution" : "A term which will be unified with the optimized Template", "Optimum" : "A variable which will be set to the optimum value of Cost", "Options" : "A bb_options structure or variable"], amode : bb_min(+, ?, ?, ?, ?, ?)]).
:- comment(bb_probe / 7, [summary : "Low-level primitive for building branch-and-bound-style search procedures", template : "bb_probe(++From, ++To, +Goal, ?Template, ?Cost, ++Handle, ++Module)", desc : html("\n\tbb_probe tries to find a solution for Goal in the range From..To.\n\tIf there is a solution, its Template and Cost are stored in Handle,\n\tthe computation is undone, and bb_probe succeeds.\n\tIf there is no solution, Handle is not changed and bb_probe fails.\n\tThe primitive set_var_bounds/3 is used to impose cost bounds\n\tduring the search process in a generic way."), see_also : [bb_init / 2, bb_cost / 2, bb_solution / 2, bb_finish / 1, bb_min / 3, bb_min / 6, set_var_bounds / 3], eg : "% a simple branch-and-bound procedure\nmy_minimize(Goal, Cost, Solution, OptCost, Module) :-\n\tbb_init(1000000, Handle),\n\t(\n\t    bb_delta(0, 1000000, Goal, Cost, Handle, Module)\n\t;\n\t    bb_solution(Handle, Solution),\n\t    bb_cost(Handle, OptCost)\n\t),\n\tbb_finish(Handle).\n\nbb_delta(From, To, Goal, Cost, Handle, Module) :-\n\tbb_probe(From, To, Goal, Goal, Cost, Handle, Module),\n\tNewTo is bb_cost(Handle) - 1,\n\tbb_delta(From, NewTo, Goal, Cost, Handle, Module).\n    "]).
:- comment(bb_solution / 2, [summary : "Low-level primitive for building branch-and-bound-style search procedures", template : "bb_solution(++Handle, -Solution)", see_also : [bb_probe / 7]]).
:- comment(minimize / 2, [summary : "Find a minimal solution using the branch-and-bound method", desc : html("This is a shorthand for\n    \t<PRE>\n\tbb_min(+Goal, ?Cost, _DefaultOptions)\n\t</PRE>\n\tSee bb_min/3 for details."), template : "minimize(+Goal, ?Cost)", see_also : [bb_min / 3]]).
