View source with raw comments or as raw
    1%
    2% Toolkit of useful utilities for CLP(BNR)
    3%
    4/*	The MIT License (MIT)
    5 *
    6 *	Copyright (c) 2022-2024 Rick Workman
    7 *
    8 *	Permission is hereby granted, free of charge, to any person obtaining a copy
    9 *	of this software and associated documentation files (the "Software"), to deal
   10 *	in the Software without restriction, including without limitation the rights
   11 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   12 *	copies of the Software, and to permit persons to whom the Software is
   13 *	furnished to do so, subject to the following conditions:
   14 *
   15 *	The above copyright notice and this permission notice shall be included in all
   16 *	copies or substantial portions of the Software.
   17 *
   18 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   19 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   20 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   21 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   22 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   23 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   24 *	SOFTWARE.
   25 */
   26:- module(clpBNR_toolkit,       % SWI module declaration
   27	[
   28	iterate_until/3,            % general purpose iterator
   29	mid_split_one/1,            % contractor to split largest interval at midpoint
   30	mid_split/1,                % contractor to split an interval at midpoint
   31	taylor_contractor/2,        % build cf_contractor based on Taylor expansion
   32	taylor_merged_contractor/2, % build merged Taylor cf_contractor from list of equations
   33	cf_contractor/2,            % execute cf_contractor
   34	cf_solve/1, cf_solve/2,     % a solve predicate for centre form contractors
   35
   36	lin_minimum/3,              % find minimum of linear problem using library(simplex)
   37	lin_maximum/3,              % find maximum of linear problem using library(simplex)
   38	lin_minimize/3,             % lin_minimum/3 plus bind vars to solution minimizers
   39	lin_maximize/3,             % lin_maximum/3 plus bind vars to solution maximizers
   40
   41	local_minima/1,             % apply KT constraints for objective function expression (OFE)
   42	local_maxima/1,             % semantically equivalent to local_minima/1
   43	local_minima/2,             % apply KT constraints for minima with constraints
   44	local_maxima/2              % apply KT constraints for maxima with constraints
   45	]).

clpBNR_toolkit: Toolkit of various utilities used for solving problems with clpBNR

CLP(BNR) (library(clpBNR))is a CLP over the domain of real numbers extended with ±∞. This module contains a number of useful utilities for specific problem domains like the optimization of linear systems, enforcing local optima conditions, and constructing centre form contractors to improve performance (e.g., Taylor extensions of constraints). For more detailed discussion, see A Guide to CLP(BNR) (HTML version included with this pack in directory docs/).

Documentation for exported predicates follows. The "custom" types include:

   56:- use_module(library(apply),[maplist/3]).   57:- use_module(library(clpBNR)).   58:- use_module(library(simplex)).   59
   60% messages for noisy failure
   61fail_msg_(FString,Args) :-
   62	debug(clpBNR,FString,Args), fail.
   63	
   64:- set_prolog_flag(optimise,true).  % for arithmetic, this module only
 iterate_until(+Count:integer, Test, Goal) is nondet
Succeeds when Goal succeeds; otherwise fails. Goal will be called recursively up to Count times as long as Test succeeds Example using this predicate for simple labelling using Test small/2 and Goal midsplit/1 :
?- X::real(-1,1),iterate_until(10,small(X,0),mid_split(X)),format("X = ~w\n",X),fail;true.
X = _6288{real(-1,-1r2)}
X = _6288{real(-1r2,0)}
X = _6288{real(0,1r2)}
X = _6288{real(1r2,1)}
true.

The specific intended use case is to provide an iterator for meta-contractors such as the centre-form contractor such as midsplit/1 (example above) or as constructed by taylor_contractor/2 as in:

?- X::real,taylor_contractor({X**4-4*X**3+4*X**2-4*X+3==0},T),
iterate_until(50,small(X),(T,mid_split_one([X]))),format("X = ~w\n",X),fail;true.
X = _150{real(0.999999999926943,1.00000000007306)}
X = _150{real(2.999999999484828,3.0000000005152105)}
true.

(Aside: For some problems, solving with Taylor contractors can be a faster and more precise alternative to clpBNR:solve/1.) */

   88%
   89% General purpose iterator: execute Goal a maximum of N times or until Test succeeds
   90%
   91iterate_until(N,Test,Goal) :- N>0, !,
   92	Goal,
   93	N1 is N-1,
   94	(Test
   95	 -> true
   96	  ; iterate_until(N1,Test,Goal)
   97	).
   98iterate_until(_N,_,_).  % non-positive N --> exit
   99
  100sandbox:safe_meta(clpBNR_toolkit:iterate_until(_N,Test,Goal), [Test, Goal]).
 mid_split_one(+Xs:numeric_list) is nondet
Succeeds splitting the widest interval in Xs, a list of intervals; fails if Xs is not a list of intervals. See mid_split for details of interval splitting for this predicate.
See also
- mid_split/1 */
  109mid_split_one(Xs) :-
  110	select_split(Xs,X),  % select largest interval with largest width
  111	mid_split(X).        % split it
 mid_split(X:numeric) is nondet
Succeeds if X is an interval that can be split at its midpoint narrowing X to it's lower "half"; on backtracking X is constrained to the upper half; fails if X is not a numeric. If X is an interval, defined as:
mid_split(X) :-
        M is midpoint(X),
        ({X=<M} ; {M=<X}).

Note that mid_split succeeds if X is a number, but doesn't do anything.

Use clpBNR:small as a pre-test to avoid splitting intervals which are already small enough.

See also
- clpBNR:small/1 */
  127mid_split(X) :-
  128	number(X)                    % optimise number case
  129	 -> true
  130	 ;  midpoint(X,M),           % fails if not an interval
  131	    ({X=<M} ; {M=<X}).       % possible choicepoint
  132%
  133% select interval with largest width
  134%
  135select_split([X],X) :- !.       % select last remaining element
  136select_split([X1,X2|Xs],X) :-   % compare widths and discard one interval
  137	delta(X1,D1),
  138	delta(X2,D2),
  139	(D1 >= D2
  140	 -> select_split([X1|Xs],X)
  141	 ;  select_split([X2|Xs],X)
  142	).
 cf_contractor(Xs:interval_list, As:interval_list) is semidet
Succeeds if each interval in As can be unified with the midpoints of the respective interval in Xs; otherwise fails. This predicate executes one narrowing step of the centre form contractor such as that generated by taylor_contractor. In normal usage, a direct call to cf_contractor does appear; instead use cf_contractor or in a Goal for iterate_until/3.
See also
- taylor_contractor/2, cf_solve/1, iterate_until/3 */
  151%
  152% centred form contractor
  153%
  154% Bind the values of As to the midpoints of Xs. To support repetitive application 
  155% of the contractor (required by the iterator), the contractor should not permanently 
  156% bind anything so findall/3 will be used to achieve this "forward checking" 
  157% (as suggested in [CLIP]). After the call to findall, the bounds of the resulting list
  158% of narrowed domains (XDs) are then applied to Xs.
  159%
  160% This contractor can be used with any "centred form", e.g., Newton or Krawczyk, since it
  161% only depends on intervals and their midpoints, hence its name `cf_contractor`. The 
  162% details which distinguish the variety of centred form are built into the variables' 
  163% constraints.
  164%
  165cf_contractor(Xs,As) :-
  166	findall(Ds,(maplist(bind_to_midpoint,Xs,As),maplist(cf_domain,Xs,Ds)),[XDs]),
  167	maplist(set_domain,Xs,XDs).
  168	
  169bind_to_midpoint(X,A) :- A is float(midpoint(X)).
  170
  171cf_domain(X,D) :- 
  172	number(X) -> D = X ; domain(X,D).  % in case X narrowed to a point
  173	
  174set_domain(X,D) :- 
  175	number(D) -> X = D ; X::D.
 cf_solve(+Contractor, +Precision:integer) is nondet
Succeeds if a solution can be found for all variables in the centre form contractor, Contractor, where the resultant domain of any variable is narrower than the limit specified by Precision (for cf_solve/1, default Precision is number of digits as defined by the environment flag clpBNR_default_precision); otherwise fails.

This is done by using iterate_until/3 limited to a count determined by the flag clpBNR_iteration_limit. Examples:

?- X::real, taylor_contractor({X**4-4*X**3+4*X**2-4*X+3==0},T), cf_solve(T).
T = cf_contractor([X], [_A]),
X:: 1.000000000...,
_A::real(-1.0Inf, 1.0Inf) ;
T = cf_contractor([X], [_A]),
X:: 3.00000000...,
_A::real(-1.0Inf, 1.0Inf) ;
false.

?- taylor_contractor({2*X1+5*X1**3+1==X2*(1+X2), 2*X2+5*X2**3+1==X1*(1+X1)},T), cf_solve(T).
T = cf_contractor([X2, X1], [_A, _B]),
X1:: -0.42730462...,
X2:: -0.42730462...,
_B::real(-1.0Inf, 1.0Inf),
_A::real(-1.0Inf, 1.0Inf) ;
false.
See also
- taylor_contractor/2, cf_contractor/2 */
  207cf_solve(T) :-
  208    current_prolog_flag(clpBNR_default_precision,P),
  209    cf_solve(T,P).
  210cf_solve(cf_contractor(Xs,As),P) :-
  211    current_prolog_flag(clpBNR_iteration_limit,L),
  212    Count is L div 10,          % heuristic - primitive iteration limit/10    
  213    cf_iterate_(Count,Xs,As,P).
  214
  215cf_iterate_(Count,Xs,As,P) :-
  216	Count > 0,
  217	\+ small(Xs,P),             % at least one var not narrow enough
  218	!, 
  219	cf_contractor(Xs,As),       % execute contractor
  220	select_split(Xs,X),         % select widest
  221	(small(X,P)                 % still wide enough to split?
  222	 -> true                    % no, we're done 
  223	  ; mid_split(X),           % yes, split it
  224	    Count1 is Count-1,
  225	    cf_iterate_(Count1,Xs,As,P)  % and iterate
  226	).
  227cf_iterate_(_,_,_,_).           % done (Count=<0 or all small Xs)
 taylor_contractor(+Constraints, -Contractor) is semidet
Succeeds if a centre form contractor Contractor can be generated from one or more multivariate equality (== or =:=) constraints Constraints; otherwise fails. Example:
?- taylor_contractor({X**4-4*X**3+4*X**2-4*X+3==0},T).
T = cf_contractor([X], [_A]),
X::real(-1.509169756145379, 4.18727500493995),
_A::real(-1.0Inf, 1.0Inf).

Use the contractor with cf_solve to search for solutions, as in:

?- X::real,taylor_contractor({X**4-4*X**3+4*X**2-4*X+3==0},T), cf_solve(T).
T = cf_contractor([X], [_A]),
X:: 1.000000000...,
_A::real(-1.0Inf, 1.0Inf) ;
T = cf_contractor([X], [_A]),
X:: 3.00000000...,
_A::real(-1.0Inf, 1.0Inf) ;
false.

Multiple equality constraints are supported, as in this example of the Broyden banded problem (N=2):

?- taylor_contractor({2*X1+5*X1**3+1==X2*(1+X2), 2*X2+5*X2**3+1==X1*(1+X1)},T), cf_solve(T).
T = cf_contractor([X2, X1], [_A, _B]),
X1:: -0.42730462...,
X2:: -0.42730462...,
_B::real(-1.0Inf, 1.0Inf),
_A::real(-1.0Inf, 1.0Inf) ;
false.

Centre form contractors can converge faster than the general purpose builtin fixed point iteration provided by solve/1.

See also
- cf_solve/1 */
  264%
  265% build a cf_contractor for a multivariate expression based on Taylor expansion
  266%
  267taylor_contractor({E1=:=E2},CF) :-
  268	taylor_contractor({E1==E2},CF).
  269taylor_contractor({E1==E2},cf_contractor(Xs,As)) :-
  270	Exp=E1-E2,
  271	term_variables(Exp,Xs),              % original arguments, bound to TXs on call
  272	make_EQ_(Exp,TEQ),                   % original constraint with arguments
  273	% build constraint list starting with Z's and ending with TEQ and DEQ ()
  274	T::real(0,1),
  275	make_As_and_Zs_(Xs,T,As,Zs,Cs,[TEQ,DEQ]),  % T per Z
  276	% now build Taylor constraint, DEQ
  277	copy_term_nat(Exp,AExp),              % copy of original constraint with  As
  278	term_variables(AExp,As),
  279	sum_diffs(Xs, As, Zs, Zs, Exp, AExp, DEQ),  % add on D(Z)'s'
  280	% make any vars in original equation and contractor arguments finite real intervals
  281	!,
  282	Xs::real,  % all vars are intervals
  283	{Cs}.      % apply constraints
  284taylor_contractor({Es},CF) :-
  285	taylor_merged_contractor({Es},CF),  % list or sequence	
  286	!.
  287taylor_contractor(Eq,_) :-
  288	fail_msg_('Invalid constraint for Taylor contractor: ~w',[Eq]).
  289
  290make_As_and_Zs_([],_,[],[],Tail,Tail).
  291make_As_and_Zs_([X|Xs],T,[A|As],[Z|Zs],[Z==A+T*(X-A)|CZs],Tail) :-
  292	make_As_and_Zs_(Xs,T,As,Zs,CZs,Tail).
  293
  294sum_diffs([], [], [], _AllZs, _Exp, ExpIn, EQ) :- make_EQ_(ExpIn,EQ).
  295sum_diffs([X|Xs], [A|As], [Z|Zs], AllZs, Exp, AExp, DEQ) :-
  296	copy_term_nat(Exp,NExp),        % copy expression and replace Xs by Zs
  297	term_variables(NExp,AllZs),
  298	partial_derivative(NExp,Z,DZ),  % differentiate wrt. Z and add to generated expression
  299	sum_diffs(Xs, As, Zs, AllZs, Exp, AExp+DZ*(X-A), DEQ).
  300
  301% map expression Exp to an equation equivalent to Exp==0 with numeric RHS
  302make_EQ_(Exp,LHS==RHS) :-    % turn expression into equation equivalent to Exp==0.
  303	make_EQ_(Exp,LHS,RHS).
  304	
  305make_EQ_(E,E,0)         :- var(E), !.
  306make_EQ_(X+Y,X,SY)      :- number(Y), !, SY is -Y.
  307make_EQ_(X-Y,X,Y)       :- number(Y), !.
  308make_EQ_(X+Y,Y,SX)      :- number(X), !, SX is -X.
  309make_EQ_(X-Y,SY,SX)     :- number(X), !, SX is -X, negate_sum_(Y,SY).
  310make_EQ_(X+Y,LHS+Y,RHS) :- !, make_EQ_(X,LHS,RHS).
  311make_EQ_(X-Y,LHS-Y,RHS) :- !, make_EQ_(X,LHS,RHS).
  312make_EQ_(E,E,0).        % default (non +/- subexpression)
  313
  314negate_sum_(Y,-Y) :- var(Y), !.
  315negate_sum_(X+Y,NX-Y) :- !, negate_sum_(X,NX).
  316negate_sum_(X-Y,NX+Y) :- !, negate_sum_(X,NX).
  317negate_sum_(E,-E).
 taylor_merged_contractor(+Constraints, -Contractor) is semidet
Succeeds if a merged centre form contractor Contractor can be generated from each equality (== or =:=) constraint in Constraints; otherwise fails.
deprecated
-
  • use taylor_contractor/2 instead */
  326%
  327% build a cf_contractor by merging a list of cf_contractor's
  328%
  329taylor_merged_contractor({Es},T) :-
  330	cf_list(Es,Ts),
  331	cf_merge(Ts,T).
  332
  333cf_list([],[]) :- !.
  334cf_list([C|Cs],[CF|CFs]) :- !,
  335	cf_list(C, CF),
  336	cf_list(Cs,CFs).
  337cf_list((C,Cs),[CF|CFs]) :-  !,
  338	cf_list(C, CF),
  339	cf_list(Cs,CFs).
  340cf_list(C,CF) :-
  341	taylor_contractor({C},CF).
  342
  343cf_merge(CFs,CF) :- cf_merge(CFs,cf_contractor([],[]),CF).
  344
  345cf_merge([],CF,CF).
  346cf_merge([CF|CFs],CFIn,CFOut) :-
  347	cf_merge(CF,CFIn,CFNxt),
  348	cf_merge(CFs,CFNxt,CFOut).	
  349cf_merge(cf_contractor(Xs,As),cf_contractor(XsIn,AsIn),cf_contractor(XsOut,AsOut)) :-
  350	cf_add(Xs,As,XsIn,AsIn,XsOut,AsOut).
  351
  352cf_add([],[],Xs,As,Xs,As).
  353cf_add([X|Xs],[A|As],XsIn,AsIn,XsOut,AsOut) :-
  354	var_existing(XsIn,AsIn,X,A), !,
  355	cf_add(Xs,As,XsIn,AsIn,XsOut,AsOut).
  356cf_add([X|Xs],[A|As],XsIn,AsIn,XsOut,AsOut) :-
  357	cf_add(Xs,As,[X|XsIn],[A|AsIn],XsOut,AsOut).
  358
  359var_existing([Xex|Xs],[Aex|As], X,A) :- Xex==X -> Aex=A ; var_existing(Xs,As,X,A).
 lin_minimum(+ObjF, Constraints:{}, ?Min:numeric) is semidet
Succeeds if the global minimum value of the objective function ObjF subject to Constraints can be unified with Min; otherwise fails. Both the objective function and Constraints must be linear, i.e., only subexpressions summing of the form X*C (or C*X) are permitted since the actual computation is done using library(simplex). Narrowing of minimizers (variables in ObjF) is limited to that constrained by the Min result to accomodate multiple sets of minimizers. (See lin_minimize/3 to use minimizers used to derive Min.) A solution generator, e.g., clpBNR:solve/1 can be used to search for alternative sets of minimizers. "Universal Mines" example from the User Guide:
?- [M_Idays,M_IIdays,M_IIIdays]::integer(0,7),
lin_minimum(20*M_Idays+22*M_IIdays+18*M_IIIdays,
{4*M_Idays+6*M_IIdays+M_IIIdays>=54,4*M_Idays+4*M_IIdays+6*M_IIIdays>=65}, Min).
Min = 284,
M_Idays::integer(2, 7),
M_IIdays::integer(4, 7),
M_IIIdays::integer(2, 7).

?- [M_Idays,M_IIdays,M_IIIdays]::integer(0,7),
lin_minimum(20*M_Idays+22*M_IIdays+18*M_IIIdays,
{4*M_Idays+6*M_IIdays+M_IIIdays>=54,4*M_Idays+4*M_IIdays+6*M_IIIdays>=65}, Min),
solve([M_Idays,M_IIdays,M_IIIdays]).
M_Idays = 2,
M_IIdays = 7,
M_IIIdays = 5,
Min = 284 ;
false.

For linear systems, lin_minimum/3, lin_maximum/3 can be significantly faster than using the more general purpose clpBNR:global_minimum/3, clpBNR:global_maximum/3

See also
- lin_minimize/3, library(simplex) */
 lin_maximum(+ObjF, Constraints:{}, ?Max:numeric) is semidet
Succeeds if the global minimum value of the objective function ObjF subject to Constraints can be unified with Max; otherwise fails. This is the corresponding predicate to lin_minimum/3 for finding global maxima.
See also
- lin_minimum/3, lin_maximize/3 */
  396lin_minimum(ObjF,{Constraints},MinValue) :-
  397	lin_minimum_(ObjF,{Constraints},MinValue,false).
  398
  399lin_maximum(ObjF,{Constraints},MinValue) :-
  400	lin_maximum_(ObjF,{Constraints},MinValue,false).
 lin_minimize(+ObjF, Constraints:{}, ?Min:numeric) is semidet
Succeeds if the global minimum value of the objective function ObjF subject to Constraints can be unified with Min; otherwise fails. This behaves identically to lin_minimum/3 except variables in ObjF will be narrowed to the values used in calculating the final value of Min. Any other sets of minimizers corresponding to Min are removed from the solution space. "Universal Mines" example from the User Guide:
?- [M_Idays,M_IIdays,M_IIIdays]::integer(0,7),
lin_minimize(20*M_Idays+22*M_IIdays+18*M_IIIdays,
{4*M_Idays+6*M_IIdays+M_IIIdays>=54,4*M_Idays+4*M_IIdays+6*M_IIIdays>=65}, Min).
M_Idays = 2,
M_IIdays = 7,
M_IIIdays = 5,
Min = 284.
See also
- lin_minimum/3 */
 lin_maximize(+ObjF, Constraints:{}, ?Max:numeric) is semidet
Succeeds if the global maximum value of the objective function ObjF subject to Constraints can be unified with Max; otherwise fails. This behaves identically to lin_maximum/3 except variables in ObjF will be narrowed to the values used in calculating the final value of Max. Any other sets of minimizers corresponding to Min are removed from the solution space.
See also
- lin_maximum/3 */
  423lin_minimize(ObjF,{Constraints},MinValue) :-
  424	lin_minimum_(ObjF,{Constraints},MinValue,true).
  425
  426lin_maximize(ObjF,{Constraints},MinValue) :-
  427	lin_maximum_(ObjF,{Constraints},MinValue,true).
  428	
  429
  430lin_minimum_(ObjF,{Constraints},MinValue,BindVars) :-
  431	init_simplex_(ObjF,Constraints,Objective,S0,Vs),
  432	(minimize(Objective,S0,S)
  433	 -> objective(S,MinValue), {ObjF == MinValue},
  434	    (BindVars == true
  435	     -> bind_vars_(Vs,S)
  436	     ;  remove_names_(Vs),
  437	        {Constraints}  % apply constraints
  438	    )
  439	 ;  fail_msg_('Failed to minimize: ~w',[ObjF])
  440	).
  441	
  442lin_maximum_(ObjF,{Constraints},MaxValue,BindVars) :-
  443	init_simplex_(ObjF,Constraints,Objective,S0,Vs),
  444	(maximize(Objective,S0,S)
  445	 -> objective(S,MaxValue), {ObjF == MaxValue},
  446	    (BindVars == true
  447	     -> bind_vars_(Vs,S)
  448	     ;  remove_names_(Vs),
  449	        {Constraints}  % apply constraints
  450	    )
  451	 ;  fail_msg_('Failed to maximize: ~w',[ObjF])
  452	).
  453
  454init_simplex_(ObjF,Constraints,Objective,S,Vs) :-
  455	gen_state(S0),
  456	term_variables((ObjF,Constraints),Vs),
  457	(Vs = []
  458	 -> fail_msg_('No variables in expression to optimize: ~w',[ObjF])
  459	 ;  sim_constraints_(Constraints,S0,S1),
  460	    constrain_ints_(Vs,S1,S),
  461	    (map_simplex_(ObjF,T/T,Objective/[])
  462	     -> true
  463	     ;  fail_msg_('Illegal linear objective: ~w',[ObjF])
  464	    )
  465	).
  466
  467% use an attribute to associate a var with a unique simplex variable name
  468simplex_var_(V,SV) :- var(V),
  469	(get_attr(V,clpBNR_toolkit,SV)
  470	 -> true
  471	 ;  statistics(inferences,VID), SV = var(VID), put_attr(V,clpBNR_toolkit,SV)
  472	).
  473
  474% Name attribute removed before exit.
  475remove_names_([]).
  476remove_names_([V|Vs]) :-
  477	del_attr(V,clpBNR_toolkit),
  478	remove_names_(Vs).
  479		 
  480attr_unify_hook(var(_),_).     % unification always does nothing and succeeds
  481
  482constrain_ints_([],S,S).
  483constrain_ints_([V|Vs],Sin,Sout) :- 
  484	% Note: library(simplex) currently constrains all variables to be non-negative
  485	simplex_var_(V,SV),               % corresponding simplex variable name
  486	% if not already an interval, make it one with finite non-negative value
  487	(domain(V,D) -> true ; V::real(0,_), domain(V,D)),
  488	(D == boolean -> Dom = integer(0,1); Dom = D),
  489	Dom =.. [Type,L,H],
  490	(Type == integer -> constraint(integral(SV),Sin,S1) ; S1 = Sin),
  491	(L < 0
  492	 -> % apply non-negativity condition    
  493	    ({V >= 0} -> L1 = 0 ; fail_msg_('Negative vars not supported by \'simplex\': ~w',[V]))
  494	 ;  L1 = L
  495	),
  496	% explicitly constrain any vars not (0,inf)
  497	(L1 > 0   -> constraint([SV] >= L1,S1,S2)   ; S2 = S1),    % L1 not negative
  498	(H  < inf -> constraint([SV] =< H,S2,SNxt)  ; SNxt = S2),
  499	constrain_ints_(Vs,SNxt,Sout).
  500
  501bind_vars_([],_).
  502bind_vars_([V|Vs],S) :-  
  503	% Note: skip anything nonvar (already bound due to active constraints)
  504	(simplex_var_(V,SV) -> variable_value(S,SV,V) ; true),
  505	bind_vars_(Vs,S).
  506
  507% clpBNR constraints have already been applied so worst errors have been detected
  508sim_constraints_([],S,S) :- !.
  509sim_constraints_([C|Cs],Sin,Sout) :- !,
  510	sim_constraints_(C, Sin,Snxt),
  511	sim_constraints_(Cs,Snxt,Sout).
  512sim_constraints_((C,Cs),Sin,Sout) :-  !,
  513	sim_constraints_(C, Sin,Snxt),
  514	sim_constraints_(Cs,Snxt,Sout).
  515sim_constraints_(C,Sin,Sout) :- 
  516	sim_constraint_(C,SC),
  517	constraint(SC,Sin,Sout).  % from simplex
  518
  519sim_constraint_(C,SC) :-
  520	C=..[Op,LHS,RHS],              % decompose
  521	constraint_op(Op,COp),         % acceptable to simplex
  522	number(RHS), RHS >= 0,         % requirement of simplex
  523	map_simplex_(LHS,T/T,Sim/[]),  % map to simplex list of terms
  524	!,
  525	SC=..[COp,Sim,RHS].            % recompose
  526sim_constraint_(C,_) :-
  527	fail_msg_('Illegal linear constraint: ~w',[C]).	
  528
  529map_simplex_(T,CT/[S|Tail],CT/Tail) :-
  530	map_simplex_term_(T,S),
  531	!.
  532map_simplex_(A+B,Cin,Cout) :- !,
  533	map_simplex_(A,Cin,Cnxt),
  534	map_simplex_(B,Cnxt,Cout).
  535map_simplex_(A-B,Cin,Cout) :- !,
  536	map_simplex_(A,Cin,Cnxt),
  537	map_simplex_(-B,Cnxt,Cout).
  538			
  539map_simplex_term_(V,1*SV)   :- simplex_var_(V,SV), !.
  540map_simplex_term_(-T,NN*V)  :- !,
  541	map_simplex_term_(T,N*V),
  542	NN is -N.
  543map_simplex_term_(N*V,N*SV) :- number(N), simplex_var_(V,SV), !.
  544map_simplex_term_(V*N,N*SV) :- number(N), simplex_var_(V,SV).
  545
  546constraint_op(==,=).
  547constraint_op(=<,=<).
  548constraint_op(>=,>=).
 local_minima(+ObjF) is semidet
Succeeds if the value of objective function ObjF can be constrained to be a local minimum, i.e, it's "slope" is 0 in every dimension; otherwise fails. This requires that a partial derivative of ObjF exists for each variable. local_minima should be executed prior to a call to clpBNR:global_minimum using the same objective function, e.g.,
?- X::real(0,10), OF=X**3-6*X**2+9*X+6, local_minima(OF), global_minimum(OF,Z).
OF = X**3-6*X**2+9*X+6,
X:: 3.00000000000000...,
Z:: 6.000000000000... .

Using any local optima predicate can significantly improve performance compared to searching for global optima (clpBNR:global_*) without local constraints.

See also
- clpBNR:local_minima/2 */
 local_maxima(+ObjF) is semidet
Succeeds if the value of objective function ObjF can be constrained to be a local maximum, i.e, it's "slope" is 0 in every dimension; otherwise fails. This requires that a partial derivative of ObjF exists for each variable. local_maxima should be executed prior to a call to clpBNR:global_maximum using the same objective function, e.g.,
?- X::real(0,10), OF=X**3-6*X**2+9*X+6, local_maxima(OF), global_maximum(OF,Z).
OF = X**3-6*X**2+9*X+6,
X:: 1.000000000000000...,
Z:: 10.0000000000000... .
See also
- clpBNR:local_maxima/2 */
  576%
  577%	local_minima/1,       % apply KT constraints for objective function expression (OFE)
  578%	local_maxima/1,       % semantically equivalent to local_minima/1
  579%
  580local_minima(ObjExp) :-
  581	local_optima_(min,ObjExp,[]).
  582
  583local_maxima(ObjExp) :-
  584	local_optima_(max,ObjExp,[]).
 local_minima(+ObjF, +Constraints:{}) is semidet
Succeeds if the value of objective function ObjF can be constrained to be a local minimum, i.e, it's "slope" is 0 in every dimension, subject to Constraints; otherwise fails. This requires that a partial derivative of ObjF, and any subexpression in Constraints, exists for each variable. local_minima should be executed prior to a call to clpBNR:global_minimum using the same objective function, e.g.,
?- [X1,X2]::real, OF=X1**4*exp(-0.01*(X1*X2)**2),
local_minima(OF,{2*X1**2+X2**2==10}), global_minimum(OF,Z), solve([X1,X2]).
OF = X1**4*exp(-0.01*(X1*X2)**2),
X1::real(-1.703183936003284e-108, 1.703183936003284e-108),
X2:: -3.16227766016838...,
Z:: 0.0000000000000000... ;
OF = X1**4*exp(-0.01*(X1*X2)**2),
X1::real(-1.703183936003284e-108, 1.703183936003284e-108),
X2:: 3.16227766016838...,
Z:: 0.0000000000000000... .
See also
- clpBNR:local_minima/1 */
 local_maxima(+ObjF, +Constraints:{}) is semidet
Succeeds if the value of objective function ObjF can be constrained to be a local maximum, i.e, it's "slope" is 0 in every dimension; otherwise fails. This requires that a partial derivative of ObjF, and any subexpression in Constraints, exists for each variable. local_maxima should be executed prior to a call to clpBNR:global_maximum using the same objective function, e.g.,
?- [X1,X2]::real,OF=X1**4*exp(-0.01*(X1*X2)**2),
local_maxima(OF,{2*X1**2+X2**2==10}), global_maximum(OF,Z),solve([X1,X2]).
OF = X1**4*exp(-0.01*(X1*X2)**2),
X1:: -2.23606797749979...,
X2:: 0.0000000000000000...,
Z:: 25.0000000000000... ;
OF = X1**4*exp(-0.01*(X1*X2)**2),
X1:: 2.23606797749979...,
X2:: 0.0000000000000000...,
Z:: 25.0000000000000... .
See also
- clpBNR:local_maxima/1 */
  623%
  624%	local_minima/2,       % apply KT constraints for minima with constraints
  625%	local_maxima/2        % apply KT constraints for maxima with constraints
  626%
  627local_minima(ObjExp,{Constraints}) :-
  628	local_optima_(min,ObjExp,Constraints).
  629	
  630local_maxima(ObjExp,{Constraints}) :-
  631	local_optima_(max,ObjExp,Constraints).
  632	
  633	
  634local_optima_(MinMax,ObjF,Constraints) :-
  635	local_optima_(MinMax,ObjF,Constraints,Cs),  % generate constraints
  636	{Cs}.                                       % then apply
  637
  638local_optima_(MinMax,ObjF,Constraints,[Constraints,GCs,LCs]) :-
  639	lagrangian_(Constraints,MinMax,ObjF,LObjF,LCs),
  640	term_variables((Constraints,ObjF),Vs),
  641	gradient_constraints_(Vs,GCs,LObjF).
  642
  643gradient_constraints_([],[],_Exp).
  644gradient_constraints_([X|Xs],[C|Cs],Exp) :-
  645	partial_derivative(Exp,X,D),
  646	(number(D) -> C=[] ; C=(D==0)),  % no constraint if PD is a constant
  647	gradient_constraints_(Xs,Cs,Exp).
  648	
  649% for each constraint add to Lagrangian expression with auxiliary KKT constraints
  650lagrangian_(C,MinMax,Exp,LExp, LC) :- nonvar(C),
  651	kt_constraint_(C,CExp, LC), % generate langrange term with multiplier
  652	lexp(MinMax,Exp,CExp,LExp),
  653	!.
  654lagrangian_([],_,L,L,[]).
  655lagrangian_([C|Cs],MinMax,Exp,LExp,[LC|LCs]) :-
  656	lagrangian_(C, MinMax,Exp,NExp,LC),
  657	lagrangian_(Cs,MinMax,NExp,LExp,LCs).	
  658lagrangian_((C,Cs),MinMax,Exp,LExp,[LC|LCs]) :-
  659	lagrangian_(C,MinMax,Exp,NExp,LC),
  660	lagrangian_(Cs,MinMax,NExp,LExp,LCs).
  661		
  662lexp(min,Exp,CExp,Exp+CExp).
  663lexp(max,Exp,CExp,Exp-CExp).
  664
  665kt_constraint_(LHS == RHS, M*(LHS-RHS), []) :-
  666	M::real.                          % finite multiplier only
  667kt_constraint_(LHS =< RHS, MGx,     MGx==0) :-
  668	MGx = M*(LHS-RHS), M::real(0,_).  % positive multiplier and KKT non-negativity condition
  669kt_constraint_(LHS >= RHS, Exp,         LC) :-
  670	kt_constraint_(RHS =< LHS, Exp, LC).  % >= convert to =<