#
# Integer Linear Programming
#	This routine is a branch and bound based ILP optimizer.
#	The "best-bound" branching rule is used (ie. greatest objective).
#
# ilp(obj,const) maximizes the linear objective function "obj"
#                with respect to the constraints "const", with
#		 all variables taking on integer values.
#		 An optional third argument "sgn" can be used to
#		 indicate sign restrictions on the variables, either
#		 NONNEGATIVE or UNRESTRICTED.
#
# Output : An optimal assignment is returned if it exists; otherwise,
#	   NULL indicates unboundedness, {} infeasibility.
#
#					Anu Pathria	Jan/90
#

macro( heap=readlib('heap') );

ilp := proc(obj,const) 
   local dummy,sgn,val,lower,incumb,sol,t,i,lp,c,lps,bestbound;

if nargs = 2 then sgn := UNRESTRICTED
elif nargs = 3 then sgn := args[3]
else ERROR(`Wrong number of arguments`)
fi;

incumb := {};			#incumbent solution
lower := -infinity;		#lower bound on optimal value

# with(simplex,[]); # PATCH
sol := simplex[maximize](obj,const,sgn);

#If LP is unbounded, then ILP is infeasible or unboounded.
#Because simplex[maximize] doesn't recognize a constant as a linear
#objective function, the test is as shown here.
if sol = NULL then sol := ilp(dummy,{dummy=0} union const,sgn);
		   if sol = {} then RETURN({}) else RETURN(NULL) fi
 elif sol = {} or `ilp/intsol`(sol) then RETURN(sol)
fi;

# Boolean function for best-bound branching rule.
bestbound := proc(x,y) evalb(x[3] <= y[3]) end:

lps := heap[new](bestbound,[const,sol,subs(sol,obj)]);	#prique of branches

while not(heap[empty](lps)) do 
   lp := heap[extract](lps);		#lp to branch from
   if lower = -infinity or lp[3] > lower then
      t := `ilp/intsol`(lp[2]);
      c[1] := lp[1] union {op(1,t) <= trunc(op(2,t))};
      c[2] := lp[1] union {op(1,t) >= trunc(op(2,t))+1};
      # Consider 2 branches
      for i from 1 to 2 do
         sol := simplex[maximize](obj,c[i],sgn);
         if sol <> {} then
	    t := `ilp/intsol`(sol);
	    val := subs(sol,obj);
	    if lower = -infinity or val > lower
               then if t then incumb := sol ; lower := val;
		         else heap[insert]([c[i],sol,val],lps)
		    fi;
	    fi;
         fi;
      od;
   fi;
od;

incumb;			#final incumbent solution is optimal
end:

# If any of the variable assignments given by S is non-integral,
# then that assignment is returned, else true is returned.
`ilp/intsol` := proc(S)
   local i; 
for i in S do if not(type(op(2,i),integer)) then RETURN(i) fi od;
true;
end:

`help/text/ilp` := TEXT(
`   `,
`FUNCTION: ilp - maximize an integer linear program`,
`   `,
`CALLING SEQUENCE:`,
`   ilp(f, C)`,
`   ilp(f , C, vartype)`,
`   `,
`PARAMETERS:`,
`   f       - a linear expression`,
`   C       - a set of linear constraints`,
`   vartype - (optional) NONNEGATIVE or UNRESTRICTED`,
`   `,
`SYNOPSIS:   `,
`- The function ilp uses a simplex-based branch and bound technique to maximize`,
`  the linear objective function f with respect to the set of linear constraints`,
`  C.  All variables are assumed to be integer valued.`,
`   `,
`- This function returns either a set of equations describing the optimal solu-`,
`  tion to the specified integer linear program, or the empty set in the case`,
`  where no feasible solution to C exists, or NULL in the case where the solu-`,
`  tion is unbounded.`,
`   `,
`- The equations returned by ilp can be substituted back into the objective`,
`  function f to obtain the value of the objective function at the optimal solu-`,
`  tion.   `,
`   `,
`- A third parameter may be used to specify that all variables are constrained`,
`  to be NONNEGATIVE; such constraints may also be listed explicitly. Similarly,`,
`  UNRESTRICTED indicates that no sign constraint is to be placed on the vari-`,
`  ables.   `,
`   `,
`EXAMPLES:   `,
`> ilp(5*x1+8*x2,{x1+x2 <=6,5*x1+9*x2<=45,x1>=0,x2>=0});`,
`   `,
`                               {x1 = 0, x2 = 5}`,
`> ilp(3*x1 + 2*x2,{x1-3*x2 <= 10,x2 +5*x1 >=3,x1+2*x2 <= 20},UNRESTRICTED);`,
`   `,
`                              {x1 = 16, x2 = 2}`,
`SEE ALSO:  simplex`
):

#save `ilp.m`;
#quit
