linineq.red 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. module linineq; % Linear inequalities and linear optimization.
  2. % Author: Herbert Melenk <melenk@sc.zib-berlin.dbp.de>
  3. % Version 1 January 1990
  4. % Version 1.1 February 1990
  5. % added parameter "record=t"
  6. % Version 2 May 1991
  7. % added Branch-and-Bound for Integer Prgramming
  8. %
  9. % Solution of linear inequalities & equations with numerical
  10. % coefficients.
  11. %
  12. % Fourier(1826) /Motzkin(1936): George. B. Dantzig,
  13. % Linear Programming and Extensions.
  14. % Branch and Bound: Beightler, Phillips, Wilde,
  15. % Foundations of Optimization
  16. % Prentice Hall, 1979
  17. %
  18. put('linineq,'psopfn,'linineqeval);
  19. global '(!*trlinineq !*trlinineqint !*prlinineq);
  20. switch trlinineq,prlinineq,trlinineqint;
  21. fluid '(linineqinterval!* linineqrecord!*);
  22. algebraic operator <=,>=;
  23. symbolic procedure linineqeval(u);
  24. % Interface for algebraic mode.
  25. begin scalar prob,equa,requa,vars,oldorder,res,u1,x,y,p,e,msg;
  26. scalar direction,rec,linineqrecord!*,r,intvars;
  27. msg := !*prlinineq or !*trlinineq;
  28. u1 := getrlist reval car u; u := cdr u;
  29. if u then <<x:= reval car u; u := cdr u>>;
  30. if eqcar(x,'LIST) then vars := getrlist x else
  31. u := x . u;
  32. while u do <<x := reval car u; u := cdr u;
  33. if eqcar(x,'EQUAL) and
  34. ((cadr x='RECORD and (rec:=t)) or
  35. (cadr x='INT and (intvars:=getrlist caddr x)))
  36. then t else
  37. <<print x; rederr "illegal parameter">>;
  38. >>;
  39. x := vars := for each u in vars collect
  40. <<u := reval u;
  41. if eqcar(u,'equal) then
  42. if member(caddr u,'(min max)) then
  43. <<direction:=(cadr u. caddr u) . direction;
  44. u := cadr u>> else
  45. rederr "illegal form in 2nd parameter";
  46. u>>;
  47. for each u in u1 collect
  48. <<u := reval u;
  49. if eqcar(u,'geq)then
  50. prob := (simp cadr u . simp caddr u) . prob else
  51. if eqcar(u,'leq)then
  52. prob := (simp caddr u . simp cadr u) . prob else
  53. if eqcar(u,'equal) then
  54. equa := (simp caddr u . simp cadr u) . equa else
  55. rederr "relational operator '>=','<='or'='missing" >>;
  56. % control the linearity
  57. for each p in append(equa,prob) do
  58. <<if not domainp denr car p or not domainp denr cdr p
  59. then rederr "unable to process nonlinear system";
  60. vars := linineqevaltest(numr car p,
  61. linineqevaltest(numr cdr p,vars));
  62. >>;
  63. if msg then <<prin2 "variables:"; prin2t vars;>>;
  64. oldorder := setkorder vars;
  65. prob := for each p in prob collect
  66. (reorder numr car p./denr car p).
  67. (reorder numr cdr p./denr cdr p);
  68. equa:= for each p in equa collect
  69. (reorder numr car p./denr car p).
  70. (reorder numr cdr p./denr cdr p);
  71. % eliminate variables from equations
  72. while equa do
  73. <<e := car equa; equa := cdr equa;
  74. e := addsq(car e,negsq cdr e);
  75. if domainp numr e then
  76. <<if numr e then % nonzero constant equated to 0
  77. rederr "equation part inconsistent">>
  78. else
  79. <<u := list((x := mvar numr e).
  80. prepsq(y:=multsq(negf red numr e ./ 1,
  81. invsq(lc numr e ./ 1))));
  82. if member(x,intvars) then
  83. % Dont eliminate integer variables;
  84. % represent equation by double inequality instead.
  85. <<x:=simp x; prob:=append({x.y,y.x},prob)>>
  86. else
  87. <<
  88. prob := for each p in prob collect
  89. subsq(car p,u).subsq(cdr p,u);
  90. equa := for each p in equa collect
  91. subsq(car p,u).subsq(cdr p,u);
  92. requa := append(u,requa);
  93. if msg then
  94. <<prin2 " ";prin2 x;
  95. prin2 " eliminated by equation";
  96. terpri()>>;
  97. vars := delete(x,vars);
  98. >>
  99. >> >>;
  100. res := if intvars then linineqint(prob,vars,msg,direction,rec,intvars)
  101. else linineq1(prob,vars,msg,direction,rec);
  102. % backsubstitution in equations;
  103. for each e in requa do
  104. <<x := prepsq subsq(y:=simp cdr e,res);
  105. res := (car e . x) . res;
  106. if rec then
  107. <<x := prepsq y;
  108. linineqrecord!* := list(x,x) . linineqrecord!*>>;
  109. >>;
  110. setkorder oldorder;
  111. r := if rec then for each p in pair(res,linineqrecord!*) collect
  112. list('LIST,list('EQUAL,caar p,cdar p),cadr p,caddr p)
  113. else
  114. for each p in res collect list('EQUAL,car p,cdr p);
  115. return 'LIST . r;
  116. end;
  117. symbolic procedure linineqevaltest(f,v);
  118. % Collect the variables in standard form f and control linearity.
  119. if domainp f then v else
  120. if not(ldeg f=1) then
  121. rederr "unable to process nonlinear system" else
  122. if member(mvar f,v) then linineqevaltest(red f,v) else
  123. linineqevaltest(red f,mvar f.v);
  124. symbolic procedure linineq0(prob,vars,dir,rec);
  125. % Interface for symbolic mode.
  126. % Prob is a list (e1,e2,..) of algebraic expressions without
  127. % relational operators, which are interpreted as
  128. % set of inequalities ei >= 0. They are linear in the
  129. % variables vars.
  130. % Silent operation: result=nil if the system is inconsistent.
  131. begin scalar oldorder,res;
  132. linineqrecord!* := nil;
  133. oldorder := setkorder vars;
  134. prob := for each u in prob collect simp u . (nil ./ 1);
  135. res := linineq1(prob,vars,nil,dir,rec);
  136. setkorder oldorder;
  137. return res;
  138. end;
  139. symbolic procedure linineqint(prob,vars,msg,dir,rec,intvars);
  140. begin scalar x,x0,y,y0,y1,z,w,problems,best,z,z0,zbest,zf,bestr;
  141. % test integer variables and adjust order;
  142. for each x in vars do
  143. if member(x,intvars) then<<w:=x.w;intvars:=delete(x,intvars)>>;
  144. if intvars then typerr('list.intvars,"int variables");
  145. intvars:=reversip w;
  146. % select primary optimization principle.
  147. if dir then<<z:=caar dir;zf:=if cdar dir='MAX then 1 else -1>>;
  148. problems:=list (nil.prob);
  149. % macro loop.
  150. while problems do
  151. <<z0:=caar problems; prob:=cdar problems; problems:=cdr problems;
  152. if msg or !*trlinineqint
  153. then linineqprint2("=== next integer subproblem",prob);
  154. w:=if best and not evalgreaterp({'times,zf,z0},{'times,zf,zbest})
  155. then nil % skip problem with suboptimal bound.
  156. else linineq1(prob,vars,msg,dir,rec);
  157. if !*trlinineqint then linineqprint3("=== subresult",w);
  158. if w and dir then
  159. <<% is better than best so far?
  160. z0:=cdr assoc(z,w);
  161. if best and evalgreaterp({'times,zf,zbest},{'times,zf,z0})
  162. then w:=nil;
  163. >>;
  164. if w then
  165. <<% test feasability;
  166. y:=list prob;
  167. for each x in intvars do
  168. <<x0:=cdr assoc(x,w);
  169. if not fixp x0 then % branch and bound
  170. <<x:=simp x; y0:=simp{'ceiling,x0}; y1:=simp {'floor,x0};
  171. y:= for each q in y join {(x.y0).q, (y1.x).q};
  172. if msg or !*trlinineqint then
  173. <<writepri("branch and bound with",'first);
  174. writepri(mkquote{'list,{'geq,x:=prepsq x,prepsq y0},
  175. {'leq,x,prepsq y1}},'last);
  176. >>;
  177. >>;
  178. >>;
  179. if cdr y then
  180. problems:=append(problems,for each q in y collect z0.q)
  181. else
  182. <<zbest:=z0; best:=w; bestr:=linineqrecord!*;
  183. if !*trlinineqint then prin2t "===> is feasable";>>;
  184. >>; % if w
  185. % without target dont need additional result.
  186. if best and null dir then problems:=nil;
  187. >>; % while problems
  188. linineqrecord!*:=bestr;
  189. return best;
  190. end;
  191. symbolic procedure linineq1(prob,vars,msg,dir,rec);
  192. % Algebraic evaluation of a set of inequalities:
  193. % prob is a list of pairs of standard quotients,
  194. % (( p1 . q1)(p2 . q2) .. (pn . qn))
  195. % which are interpreted as inequalities:
  196. % pi >= qi ;
  197. % vars is the list of (linear) variables.
  198. % dir the direction of final optimization
  199. % rec switch; if t, the record of inequatlities is produced
  200. % Result is NIL if the system has no solution; otherwise
  201. % the solution has the form of an association list
  202. % ((v1 . val1)(v2 . val2) ... (vn . valn)),
  203. % where vi are the variables and vali are values in algebraic
  204. % form. NIL if the system has no solution.
  205. %
  206. begin scalar v,vq,lh,rh,x,y,z,prob1,prob2,prob3,prob4,nprob,sw,sol;
  207. v := car vars; vars := cdr vars;
  208. vq := mksq(v,1);
  209. if !*trlinineq then
  210. linineqprint2(list("next variable:",v,"; initial system:"),prob);
  211. prob := linineqnormalize prob;
  212. for each p in prob do
  213. <<lh := car p; rh := cdr p;
  214. % if v appears on the lhs, isolate it
  215. if not domainp numr lh and mvar numr lh = v then
  216. <<x := invsq(lc numr lh ./ 1);
  217. sw := (numr x < 0);
  218. lh := multsq(lh,x); rh := multsq(rh,x);
  219. rh := addsq(rh,negf red numr lh ./ denr lh);
  220. if not sw then prob1 := (vq . rh) . prob1 else
  221. prob2 := (rh . vq) . prob2;
  222. >>else if domainp numr rh and domainp numr lh then
  223. prob4 := (lh . rh) . prob4 else
  224. prob3 := (lh . rh) . prob3;
  225. >>;
  226. if null prob1 and null prob2 and vars then
  227. << sol := linineq1(prob,vars,msg,dir,rec);
  228. if rec then linineqrecord!* :=
  229. append(linineqrecord!*,'(((MINUS INF),'INF)));
  230. return if sol then (v . 0) . sol else nil>>;
  231. if !*trlinineq then
  232. <<linineqprint2("class 1:",prob1);
  233. linineqprint2("class 2:",prob2);
  234. linineqprint2("class 3:",prob3);
  235. linineqprint2("class 4:",prob4);
  236. >>;
  237. if rec then
  238. << x := for each u in prob1 collect prepsq cdr u;
  239. y := for each u in prob2 collect prepsq car u;
  240. x := if null x then '(MINUS INF) else
  241. if null cdr x then car x else 'MAX . x;
  242. y := if null y then ' INF else
  243. if null cdr y then car y else 'MIN . y;
  244. linineqrecord!* := append(linineqrecord!*, list list(x,y))
  245. >>;
  246. if not linineq2(prob4,msg) then return nil;
  247. nprob := append(prob3,
  248. for each x in prob1 join
  249. for each y in prob2 collect
  250. car y . cdr x);
  251. if vars then
  252. << if null (sol := linineq1(nprob,vars,msg,dir,rec)) then
  253. return nil>>
  254. else if not linineq2(nprob,msg) then return nil;
  255. % lower bound:
  256. x := if null prob1 then nil else
  257. linineqevalmax for each p in prob1 collect
  258. subsq(cdr p,sol);
  259. % upper bound:
  260. y := if null prob2 then nil else
  261. linineqevalmin for each p in prob2 collect
  262. subsq(car p,sol);
  263. if (z:=assoc(v,dir)) then z:= cdr z;
  264. if msg then
  265. <<writepri(" ",'first);
  266. writepri(mkquote if x then prepsq x else '(minus inf),nil);
  267. writepri(" <= ",nil);
  268. writepri(mkquote v,nil);
  269. writepri(" <= ",nil);
  270. writepri(mkquote if y then prepsq y else 'inf,nil);
  271. writepri("; ",nil)>>;
  272. linineqinterval!* := x . y;
  273. if z='min and null x or z='max and null y then
  274. <<if msg then writepri( " max/min cannot be resolved",'last);
  275. return nil>>;
  276. if not x=y then
  277. if z='min then y:=nil else if z='max then x:=nil;
  278. if msg then
  279. << writepri(
  280. if null x and null y then " completely free: " else
  281. if null y then " minimum: " else
  282. if null x then " maximum: " else
  283. if x=y then " zero length interval: " else
  284. " middle: ",nil);
  285. >>;
  286. if null x and null y then x := 0 else % completely free
  287. if null x then x := prepsq y else
  288. if null y then x := prepsq x else
  289. if sqlessp(y,x) then
  290. <<prin2 "system inconsistent:";
  291. prin2 prepsq x; prin2 " not <= "; prin2t prepsq y;
  292. return nil>> else
  293. x := list('quotient,list('plus,prepsq x,prepsq y),2);
  294. x := aeval x;
  295. if msg then
  296. writepri(mkquote list('EQUAL,v,x),'last);
  297. return (v . x) . sol;
  298. end;
  299. symbolic procedure linineq2(prob,msg);
  300. % All variables are elimitated. Control, if the
  301. % remaining numerical inequalities are consistent.
  302. begin scalar rh,lh;
  303. loop: if null prob then return t;
  304. lh := caar prob; rh := cdar prob;
  305. if not domainp numr rh or not domainp numr lh then
  306. <<prin2t " non numeric:"; print rh; print lh;
  307. rederr "linineq";>>;
  308. if sqlessp(lh,rh) then
  309. <<if msg then <<writepri("system inconsistent: ",'first);
  310. writepri(mkquote prepsq lh,nil);
  311. writepri(" not >= ",nil);
  312. writepri(mkquote prepsq rh,'last);>>;
  313. return nil>>;
  314. prob := cdr prob;
  315. goto loop;
  316. end;
  317. symbolic procedure linineqnormalize prob;
  318. % Normalize system: reform all inequalities such that they have
  319. % the canonical form % polynomial >= constant
  320. % (canonical restriction: absolute term of lhs=0,
  321. % denominator of lhs = 1).
  322. % and remove those, which have same lhs, but smaller rhs
  323. % (the latter are superfluous).
  324. begin scalar r,lh,rh,d,ab,x;
  325. for each p in prob do
  326. <<lh := car p; rh := cdr p;
  327. % arithmetic normalizaion
  328. lh := addsq(lh,negsq rh);
  329. d := denr lh;
  330. lh := numr lh;
  331. ab := lh; x := if domainp lh then 1 else lc ab;
  332. while not domainp ab do <<x := gcdf(x,lc ab);ab := red ab>>;
  333. ab := negf ab;
  334. lh := multsq(addf(lh,ab)./1,1 ./ x);
  335. rh := multsq(ab ./ 1, 1 ./ x);
  336. % removal of redundant elements
  337. x := assoc(lh,r);
  338. if null x then r:=(lh.rh) . r else
  339. if sqlessp(cdr x,rh) then rplacd(x,rh);
  340. >>;
  341. if !*trlinineq then
  342. linineqprint2("normalized and reduced:",r);
  343. return r;
  344. end;
  345. symbolic procedure linineqevalmin u;
  346. % Compute the minimum among the list u with sq's.
  347. linineqevalmin1(car u,cdr u);
  348. symbolic procedure linineqevalmin1(q,u);
  349. if null u then q else
  350. (linineqevalmin1( if x and !:minusp x then q else car u, cdr u)
  351. ) where x=numr addsq(q,negsq car u);
  352. symbolic procedure linineqevalmax u;
  353. % compute the maximum among the list u with sq's
  354. linineqevalmax1(car u,cdr u);
  355. symbolic procedure linineqevalmax1(q,u);
  356. if null u then q else
  357. (linineqevalmax1(
  358. if x and !:minusp x then car u else q, cdr u)
  359. ) where x=numr addsq(q,negsq car u);
  360. symbolic procedure sqlessp(q1,q2);
  361. (x and !:minusp x) where x=numr addsq(q1,negsq q2);
  362. symbolic procedure linineqprint1(text,lh,rh);
  363. <<writepri(text,'first);
  364. writepri(mkquote prepsq lh,nil);
  365. writepri(" >= ",nil);
  366. writepri(mkquote prepsq rh,'last)>>;
  367. symbolic procedure linineqprint2(text,prob);
  368. <<prin2t "--------------------------------";
  369. if atom text then text:={text};
  370. for each u in text do prin2 u; terpri();
  371. writepri(mkquote('list .
  372. for each p in prob collect
  373. {'geq,prepsq car p,prepsq cdr p}),'last)
  374. >>;
  375. symbolic procedure linineqprint3(text,res);
  376. <<writepri(text,'first);
  377. writepri(mkquote('list . for each p in res collect
  378. {'equal,car p,cdr p}), 'last);
  379. >>;
  380. endmodule;
  381. end;