solve.red 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. module solve; % Solve one or more algebraic equations.
  2. % Author: David R. Stoutemyer.
  3. % Modifications by: Anthony C. Hearn and Donald R. Morrison.
  4. fluid '(!*exp asymplis!*);
  5. global '(!!arbint
  6. !!gcd
  7. !*allbranch
  8. !*micro!-version
  9. !*nonlnr
  10. !*ppsoln
  11. !*solveinterval
  12. !*solvesingular
  13. multiplicities!*);
  14. switch allbranch,solvesingular; % solveinterval.
  15. flag('(multiplicities!*),'share);
  16. % ***** Global Declarations *****
  17. array !!cf(12), !!interval(10,2), !!exact(10);
  18. !*allbranch := t; % Returns all branches of solutions if T;
  19. %!*solveinterval = nil;% Attempts to isolate insoluble, real roots if T;
  20. !*solvesingular := t; % Default value.
  21. % !!gcd SOLVECOEFF returns GCD of powers of its arg in this
  22. % !!cf : Array of coeffs from SOLVECOEFF
  23. algebraic operator arbint, arbreal, intervl, list;
  24. % algebraic operator arbcomplex;
  25. % Done this way since it's also defined in the glmat module.
  26. deflist('((arbcomplex simpiden)),'simpfn);
  27. % ***** Utility Functions *****
  28. symbolic procedure freeofl(u,v);
  29. null v or freeof(u,car v) and freeofl(u,cdr v);
  30. symbolic procedure ratnump x;
  31. % Returns T iff any prefix expression x is a rational number.
  32. atom numr(x := simp!* x) and atom denr x;
  33. flag ('(ratnump), 'direct);
  34. symbolic procedure allkern elst;
  35. % Returns list of all top-level kernels in the list of standard
  36. % forms elst.
  37. if null elst then nil
  38. else union(kernels car numr elst, allkern cdr elst);
  39. symbolic procedure topkern(u,x);
  40. % Returns list of top level kernels in the standard form u that
  41. % contain the kernel x;
  42. for each j in kernels u conc if not freeof(j,x) then list j else nil;
  43. symbolic procedure coeflis ex;
  44. % Ex is a standard form. Returns a list of the coefficients of the
  45. % main variable in ex in the form ((expon . coeff) (expon . coeff)
  46. % ... ), where the expon's occur in increasing order, and entries do
  47. % not occur of zero coefficients.
  48. begin scalar ans,var;
  49. if domainp(ex) then return (0 . ex);
  50. var := mvar(ex);
  51. while (not domainp(ex)) and mvar(ex)=var do
  52. <<ans := (ldeg(ex) . lc(ex)) . ans; ex := red(ex) >>;
  53. if ex then ans := (0 . ex) . ans;
  54. return ans
  55. end;
  56. % ***** Evaluation Interface *****
  57. symbolic procedure solveeval u;
  58. begin scalar arglist; integer nargs;
  59. arglist := u;
  60. nargs := length(arglist);
  61. u := if nargs=1 then solve0(car arglist,nil)
  62. else if nargs=2
  63. then solve0(car arglist, cadr arglist)
  64. else solve0(car arglist,'list . cdr arglist);
  65. return !*solvelist2solveeqlist u
  66. end;
  67. put('solve,'psopfn,'solveeval);
  68. symbolic procedure !*solvelist2solveeqlist u;
  69. begin scalar x,y,z;
  70. for each j in u do
  71. <<if caddr j=0 then rederr "zero multiplicity"
  72. else if null cadr j
  73. then x := for each k in car j collect
  74. list('equal,mk!*sq k,0)
  75. else x := for each k in pair(cadr j,car j)
  76. collect list('equal,car k,mk!*sq cdr k);
  77. if length x > 1 then z := ('list . x) . z
  78. else z := car x . z;
  79. y := caddr j . y>>;
  80. multiplicities!* := 'list . y;
  81. return 'list . z
  82. end;
  83. % ***** Fundamental SOLVE Procedures *****
  84. comment these procedures return the solution of a list of equations as a
  85. list of elements with three fields: the solutions, the variables (or
  86. NIL if the equations could not be solved) and the multiplicity;
  87. symbolic procedure solve0(elst, xlst);
  88. % elst is any prefix expression, including the kernel named LST with
  89. % any number of arguments. XLST is a kernel, perhaps named LIST with
  90. % any number of arguments. Solves eqns in ELST for vars in XLST,
  91. % returning either a list of solutions, or a single solution;
  92. begin scalar !*exp,vars; integer neqn;
  93. !*exp := t;
  94. elst := for each j in solveargchk elst
  95. collect simp!* if eqexpr j then !*eqn2a j else j;
  96. neqn := length elst;
  97. if neqn = 0 then rederr "SOLVE called with no equations";
  98. if null xlst
  99. then <<vars := allkern elst;
  100. terpri();
  101. if null vars then nil
  102. else if cdr vars
  103. then <<prin2!* "Unknowns: "; maprin('list . vars)>>
  104. else <<prin2!* "Unknown: "; maprin car vars>>;
  105. terpri!* nil>>
  106. else <<xlst := solveargchk xlst;
  107. vars := for each j in xlst collect !*a2k j>>;
  108. if length vars = 0 then rederr "SOLVE called with no variables"
  109. else if neqn = 1
  110. then if null numr car elst
  111. then return if !*solvesingular
  112. then list list(list (makearbcomplex() ./ 1),
  113. vars,1)
  114. else nil
  115. else if length vars=1
  116. then return solvesq(car elst,car vars,1);
  117. % more than one equation or variable.
  118. elst := solvesys(for each j in elst collect numr j,vars);
  119. return if null elst then nil
  120. else if null cdr elst then list list(car elst,vars,1)
  121. else if null !*nonlnr then rederr "Unbalanced SOLVE equations"
  122. else elst
  123. end;
  124. symbolic procedure solveargchk u;
  125. if getrtype u eq 'list then cdr reval u
  126. else if atom u or not(car u eq 'lst) then list u
  127. else cdr u;
  128. % ***** Procedures for solving a single eqn *****
  129. symbolic procedure solvesq (ex,var,mul);
  130. % Attempts to find solutions for standard quotient ex with respect to
  131. % top level occurrences of var and kernels containing variable var.
  132. % Solutions containing more than one such kernel are returned
  133. % unsolved, and solve1 is applied to the other solutions. Integer
  134. % mul is the multiplicity passed from any previous factorizations.
  135. % Returns a list of triplets consisting of solutions, variables and
  136. % multiplicity.
  137. begin scalar e1,x1,y,z; integer mu;
  138. ex := numr ex;
  139. if null topkern(ex,var) then return nil;
  140. ex := fctrf ex;
  141. % now process monomial.
  142. if domainp car ex then ex := cdr ex
  143. else ex := (car ex . 1) . cdr ex;
  144. for each j in ex do
  145. <<e1 := car j;
  146. x1 := topkern(e1,var);
  147. mu := mul*cdr j;
  148. if x1
  149. then z := append(
  150. if null cdr x1 then solve1(e1,car x1,var,mu)
  151. else if (y := principal!-of!-powers!-soln(e1,x1,var,mu))
  152. neq 'unsolved
  153. then y
  154. else if not smemq('sol,
  155. (x1:=simp!* list('sol,mk!*sq(e1 ./ 1), var)))
  156. then solvesq(x1,var,mu)
  157. else list list(list(e1 ./ 1),nil,mu),
  158. z)>>;
  159. return z
  160. end;
  161. symbolic procedure principal!-of!-powers!-soln(ex,x1,var,mu);
  162. % Finds solutions of ex=0 by the principal of powers method, or
  163. % NIL if no such solutions exist.
  164. begin scalar z;
  165. if null !*ppsoln then return 'unsolved;
  166. a: if null x1 then return 'unsolved
  167. else if suitable!-expt car x1
  168. and not((z := pr!-pow!-soln1(ex,car x1,var,mu)) eq 'unsolved)
  169. then return z;
  170. x1 := cdr x1;
  171. go to a
  172. end;
  173. symbolic procedure pr!-pow!-soln1(ex,y,var,mu);
  174. begin scalar oldkord,z;
  175. oldkord := setkorder list y;
  176. z := reorder ex;
  177. setkorder oldkord;
  178. if ldeg z neq 1 then return 'unsolved;
  179. z := coeflis z;
  180. if length z neq 2 or caar z neq 0
  181. then errach list("solve confused",ex,z);
  182. z := exptsq(quotsq(negsq(cdar z ./ 1),cdadr z ./ 1),
  183. caddr caddr y);
  184. z := solvesq(subs2 addsq(simp!* cadr y,negsq z),var,mu);
  185. z := check!-solutions(z,ex);
  186. return z
  187. end;
  188. symbolic procedure check!-solutions(z,ex);
  189. begin scalar x,y;
  190. while z do
  191. if null cadar z then <<z := nil; x := 'unsolved>>
  192. else if null (y := numr subf(ex,list(caadar z .
  193. mk!*sq caaar z)))
  194. or null numvalue y
  195. then <<x := car z . x; z := cdr z>>
  196. else z := cdr z;
  197. return x
  198. end;
  199. symbolic procedure numvalue u;
  200. % Find floating point value of sf u.
  201. begin scalar !*numval,x;
  202. !*numval := t;
  203. x := setdmode('float,t);
  204. u := numr simp prepf u;
  205. if x then setdmode(x,t) else setdmode('float,nil);
  206. return if eqcar(u,'!:ft!:) and 1000000*abs cdr u < 1 then nil
  207. else u
  208. end;
  209. symbolic procedure suitable!-expt u;
  210. eqcar(u,'expt) and eqcar(caddr u,'quotient) and cadr caddr u = 1
  211. and fixp caddr caddr u;
  212. symbolic procedure solve1(e1,x1,var,mu);
  213. comment e1 is a standard form, non-trivial in the kernel x1,
  214. which is itself a function of var, mu is an integer.
  215. Uses roots of unity, known solutions,
  216. inverses, together with quadratic, cubic and quartic
  217. formulas, treating other cases as unsolvable. Returns nil;
  218. begin scalar b,c,coeffs,hipow; integer n;
  219. hipow := errorset(solvecoeff(e1, x1),nil,nil);
  220. if atom hipow then return list list(list(e1 . 1),nil,mu);
  221. % solvecoeff problem - no soln.
  222. hipow := car hipow;
  223. n:= !!gcd; % numerical gcd of powers.
  224. for i := 0:hipow do
  225. coeffs := nilchk getelv list('!!cf,i) . coeffs;
  226. if hipow = 1
  227. then return begin scalar lincoeff,y,z;
  228. b:=prepsq quotsq(negsq cadr coeffs,car coeffs);
  229. if n neq 1 then b := list('expt,b,list('quotient,1,n));
  230. % We may need to merge more solutions in the following if
  231. % there are repeated roots.
  232. for k := 0:n-1 do % equation in power of var.
  233. <<lincoeff := simp!* list('times,b,
  234. mkexp list('quotient,list('times,k,2,'pi),n));
  235. if x1=var
  236. then y := solnmerge(list lincoeff,list var,mu,y)
  237. else if not idp (z := car x1)
  238. then typerr(z,"solve operator")
  239. else if z := get(z,'solvefn)
  240. then y := append(apply1(z,list(cdr x1,var,mu,lincoeff))
  241. ,y)
  242. else if (z := get(car x1,'inverse)) % known inverse
  243. then y := append(solvesq(subtrsq(simp!* cadr x1,
  244. simp!* list(z,mk!*sq lincoeff)),
  245. var,mu),y)
  246. else y := list(list subtrsq(simp!* x1,lincoeff),nil,mu)
  247. . y>>;
  248. return y
  249. end
  250. else if hipow=2
  251. then return <<x1 := exptsq(simp!* x1,n); % allows for power variable
  252. for each j in apply('solvequadratic,coeffs)
  253. conc solvesq(subtrsq(x1,j),var,mu)>>
  254. else return begin scalar d,f,rcoeffs;
  255. % At this point, we cannot write down the solution directly, so
  256. % we look for various forms that we know how to solve.
  257. f:=(hipow+1)/2;
  258. d:=exptsq(simp!* x1,n);
  259. rcoeffs := reverse coeffs;
  260. return if solve1test1(coeffs,rcoeffs,f) % coefficients symmetric
  261. then if f+f=hipow+1 % odd
  262. then <<c:=addsq(d, 1 ./ 1);
  263. append(solvesq(c,var,mu),
  264. solvesq(quotsq(e1 ./ 1, c),var,mu))>>
  265. else <<setelv(list('!!cf,0),2 ./ 1);
  266. setelv(list('!!cf, 1), simp!* '!!x);
  267. c:=addsq(multsq(getelv(list('!!cf,f+1)),
  268. getelv(list('!!cf,1))),
  269. getelv(list('!!cf,f)));
  270. for j:=2:f do <<
  271. setelv(list('!!cf, j),
  272. subtrsq(multsq(getelv(list('!!cf,1)),
  273. getelv(list('!!cf,j-1))),
  274. getelv(list('!!cf,j-2))));
  275. c:=addsq(c,multsq(getelv(list('!!cf,j)),
  276. getelv(list('!!cf,f+j))))>>;
  277. for each j in solvesq(c,'!!x,mu) conc
  278. solvesq(addsq(1 ./ 1,multsq(d,subtrsq(d,caar j))),
  279. var,caddr j)>>
  280. else if solve1test2(coeffs,rcoeffs,f)
  281. % coefficients antisymmetric
  282. then <<c:=addsq(d,(-1 ./1));
  283. b := solvesq(c,var,mu);
  284. e1 := quotsq(e1 ./ 1, c);
  285. if f+f = hipow
  286. then <<c := addsq(d,(1 ./ 1));
  287. b := append(solvesq(c,var,mu),b);
  288. e1 := quotsq(e1,c)>>;
  289. append(solvesq(e1,var,mu),b)>>
  290. % equation has no symmetry
  291. else if hipow=3 and null !*micro!-version
  292. then for each j in apply('solvecubic,coeffs)
  293. conc solvesq(subtrsq(d,j),var,mu)
  294. else if hipow=4 and null !*micro!-version
  295. then for each j in apply('solvequartic,coeffs)
  296. conc solvesq(subtrsq(d,j),var,mu)
  297. else if !*solveinterval and univariatep e1
  298. then solveinterval(e1,var,mu)
  299. else list list(list(e1 ./ 1),nil,mu)
  300. % We can't solve quintic and higher
  301. end
  302. end;
  303. symbolic procedure solnmerge(u,varlist,mu,y);
  304. % Merge solutions in case of multiplicities. It may be that this is
  305. % only needed for the trivial solution x=0.
  306. if null y then list list(u,varlist,mu)
  307. else if u = caar y and varlist = cadar y
  308. then list(caar y,cadar y,mu+caddar y) . cdr y
  309. else car y . solnmerge(u,varlist,mu,cdr y);
  310. symbolic procedure nilchk u; if null u then !*f2q u else u;
  311. symbolic procedure solve1test1(coeffs,rcoeffs,f);
  312. % True if equation is symmetric in its coefficients. f is midpoint.
  313. begin integer j;
  314. a: if j>f then return t
  315. else if car coeffs neq car rcoeffs then return nil;
  316. coeffs := cdr coeffs;
  317. rcoeffs := cdr rcoeffs;
  318. j := j+1;
  319. go to a
  320. end;
  321. symbolic procedure solve1test2(coeffs,rcoeffs,f);
  322. % True if equation is antisymmetric in its coefficients. f is
  323. % midpoint.
  324. begin integer j;
  325. a: if j>f then return t
  326. else if numr addsq(car coeffs,car rcoeffs) then return nil;
  327. coeffs := cdr coeffs;
  328. rcoeffs := cdr rcoeffs;
  329. j := j+1;
  330. go to a
  331. end;
  332. symbolic procedure solveabs u;
  333. begin scalar mu,var,lincoeff;
  334. var := cadr u;
  335. mu := caddr u;
  336. lincoeff := cadddr u;
  337. u := simp!* caar u;
  338. return append(solvesq(addsq(u,lincoeff),var,mu),
  339. solvesq(subtrsq(u,lincoeff),var,mu))
  340. end;
  341. put('abs,'solvefn,'solveabs);
  342. symbolic procedure solveexpt u;
  343. begin scalar c,mu,var,lincoeff;
  344. var := cadr u;
  345. mu := caddr u;
  346. lincoeff := cadddr u;
  347. u := car u;
  348. return if freeof(car u,var) % c**(...) = b.
  349. then <<if !*allbranch
  350. then <<!!arbint:=!!arbint+1;
  351. c:=list('times,2,'i,'pi,
  352. list('arbint,!!arbint))>>
  353. else c:=0;
  354. solvesq(subtrsq(simp!* cadr u,
  355. quotsq(addsq(simp!* list('log,mk!*sq lincoeff),
  356. simp!* c),
  357. simp!* list('log,car u))),var,mu)>>
  358. else if freeof(cadr u,var) % (...)**(m/n) = b;
  359. then if ratnump cadr u
  360. then solve!-fractional!-power(u,lincoeff,var,mu)
  361. else << % (...)**c = b.
  362. if !*allbranch
  363. then <<!!arbint:=!!arbint+1;
  364. c := mkexp list('times,
  365. list('arbreal,!!arbint))>>
  366. else c:=1;
  367. solvesq(subtrsq(simp!* car u,
  368. multsq(simp!* list('expt,
  369. mk!*sq lincoeff,
  370. mk!*sq invsq
  371. simp!* cadr u),
  372. simp!* c)),var,mu)>>
  373. % (...)**(...) = b : transcendental.
  374. else list list(list subtrsq(simp!*('expt . u),lincoeff),nil,mu)
  375. end;
  376. symbolic procedure solve!-fractional!-power(u,x,var,mu);
  377. % attempts solution of equation car u**cadr u=x with respect to
  378. % kernel var and with multiplicity mu, where cadr u is a rational
  379. % number.
  380. begin scalar v,w,z;
  381. v := simp!* car u;
  382. w := simp!* cadr u;
  383. z := solvesq(subs2 subtrsq(exptsq(v,numr w),exptsq(x,denr w)),
  384. var,mu);
  385. w := subtrsq(simp('expt . u),x);
  386. z := check!-solutions(z,numr w);
  387. return if z eq 'unsolved then list list(list w,nil,mu) else z
  388. end;
  389. put('expt,'solvefn,'solveexpt);
  390. symbolic procedure solvelog u;
  391. solvesq(subtrsq(simp!* caar u,simp!* list('expt,'e,mk!*sq cadddr u)),
  392. cadr u,caddr u);
  393. put('log,'solvefn,'solvelog);
  394. symbolic procedure solvecos u;
  395. begin scalar c,d,z;
  396. if !*allbranch
  397. then <<!!arbint:=!!arbint+1;
  398. c:=list('times,2,'pi,list('arbint,!!arbint))>>
  399. else c:=0;
  400. c:=subtrsq(simp!* caar u,simp!* c);
  401. d:=simp!* list('acos,mk!*sq cadddr u);
  402. z := solvesq(subtrsq(c,d), cadr u,caddr u);
  403. if !*allbranch
  404. then z := append(solvesq(addsq(c,d), cadr u,caddr u),z);
  405. return z
  406. end;
  407. put('cos,'solvefn,'solvecos);
  408. symbolic procedure solvesin u;
  409. begin scalar c,d,f,z;
  410. if !*allbranch
  411. then <<!!arbint:=!!arbint+1;
  412. f:=list('times,2,'pi,list('arbint,!!arbint))>>
  413. else f:=0;
  414. c:=simp!* caar u;
  415. d:=list('asin,mk!*sq cadddr u);
  416. z := solvesq(subtrsq(c,simp!* list('plus,d,f)),cadr u,caddr u);
  417. if !*allbranch
  418. then z := append(solvesq(subtrsq(c,simp!* list('plus,'pi,
  419. mk!*sq subtrsq(simp!* f,simp!* d))),
  420. cadr u,caddr u),z);
  421. return z
  422. end;
  423. put('sin,'solvefn,'solvesin);
  424. symbolic procedure mkexp u;
  425. list('plus,list('cos,x),list('times,'i,list('sin,x)))
  426. where x = reval u;
  427. symbolic procedure solvecoeff(ex,var);
  428. % ex is a standard form and var a kernel. Puts the coefficients
  429. % (as standard quotients) of var in ex into the elements of !!cf,
  430. % with index equal to the exponent divided by the gcd of all the
  431. % exponents. This GCD is put into !!GCD, and the highest power
  432. % divided by the gcd is put into hipow. Returns hipow. Note that
  433. % !!cf (an array), !!gcd a global.
  434. begin scalar clist,hipow,oldkord;
  435. oldkord := setkorder list var;
  436. clist := reorder ex;
  437. setkorder oldkord;
  438. hipow := ldeg clist;
  439. clist := coeflis clist;
  440. !!gcd := caar clist;
  441. for each x in cdr clist do !!gcd := gcdn(car x, !!gcd);
  442. for i := 0:(car(dimension('!!cf))-1) do setelv(list('!!cf, i), nil);
  443. for each x in clist do setelv(list('!!cf, car x/!!gcd),cdr x ./ 1);
  444. hipow := hipow/!!gcd;
  445. return hipow
  446. end;
  447. symbolic procedure solveinterval(ex,var,mu);
  448. % ex is a standard form, var the relevant variable and mu the root
  449. % multiplicity. Isolates insoluble, real roots of EX in rational
  450. % intervals, returning solutions in terms of INTERVL(Lowlim,Highlim).
  451. begin scalar z;
  452. realroot(prepf ex,prepsq !*k2q mvar ex,'!!interval,'!!exact);
  453. for i := 1:getelv list('!!exact,0) do
  454. z := list(list simp!* getelv list('!!exact,i),list var,mu) . z;
  455. for i := 1:getelv list('!!interval,0,0) do
  456. z := list(list simp!* list('intervl,
  457. getelv list('!!interval,i,1),
  458. getelv list('!!interval,i,2)),
  459. list var,mu). z;
  460. return z
  461. end;
  462. symbolic procedure realroot(u,v,w,x);
  463. rederr("Real root finding not yet implemented");
  464. % ***** Procedures for solving a system of eqns *****
  465. symbolic procedure solvesys(exlist,varlis);
  466. % exlist is a list of standard forms, varlis a list of kernels. If
  467. % the elements of varlis are linear in the elements of exlist, and
  468. % further the system of linear eqns so defined is non-singular, then
  469. % SOLVESYS returns a list of a list of standard quotients which are
  470. % solutions of the system, ordered as in varlis. Otherwise an error
  471. % results.
  472. begin scalar eqtype,oldkord;
  473. oldkord := setkorder varlis;
  474. exlist := for each j in exlist collect reorder j;
  475. % See if equations are linear or non-linear.
  476. eqtype := 'solvelnrsys;
  477. for each ex in exlist do
  478. for each var in varlis do
  479. if not domainp ex and mvar ex=var
  480. then if ldeg ex>1 or not freeofl(lc ex,varlis)
  481. then eqtype := 'solvenonlnrsys
  482. else ex := red ex;
  483. if eqtype eq 'solvenonlnrsys and null !*nonlnr
  484. then rederr "Non linear equation solving not yet implemented";
  485. exlist:=errorset(list(eqtype,mkquote exlist,mkquote varlis),t,t);
  486. setkorder oldkord;
  487. if errorp exlist then error1() else return car exlist
  488. end;
  489. endmodule;
  490. module glsolve; % Routines for solving a general system of linear eqns.
  491. % Author: Eberhard Schruefer.
  492. %**********************************************************************
  493. %*** The number of equations and the number of unknowns are ***
  494. %*** arbitrary i.e. the system can be under- or overdetermined. ***
  495. %*** Method used is Cramer's rule, realized through exterior ***
  496. %*** multiplication. ***
  497. %**********************************************************************
  498. fluid '(kord!*);
  499. global '(!!arbint !*solvesingular);
  500. % algebraic operator arbcomplex; % Already defined in main solve module.
  501. symbolic procedure glsolve!-eval(u,bool);
  502. % This allows glsolve to be called at the user level. I'm not
  503. % sure this is now a good idea, since this code does not check
  504. % for non-linear equations and so on.
  505. begin scalar unknowns,equations,okord,solutions;
  506. if cdr u then
  507. unknowns := for each j in cdadr u collect !*a2k j;
  508. okord := setkorder append(unknowns,kord!*);
  509. equations := for each j in cdar u collect
  510. reorder numr simp!* j;
  511. if null unknowns then unknowns := allkernf equations;
  512. solutions := glnrsolve(equations,unknowns);
  513. setkorder okord;
  514. if null solutions then return '(list); % empty list.
  515. solutions := nil . solutions;
  516. return 'list .
  517. for each j in unknowns collect
  518. list('equal,j,mk!*sq car(solutions := cdr solutions))
  519. end;
  520. symbolic procedure allkernf u;
  521. if null u then nil else union(kernels car u,allkernf cdr u);
  522. put('glsolve,'psopfn,'glsolve!-eval);
  523. symbolic procedure solvelnrsys(u,v);
  524. % This is hook to general solve package. u is a list of polynomials
  525. % (s.f.'s) linear in the kernels of list v. Result is a matrix
  526. % standard form for the solutions.
  527. list glnrsolve(u,v);
  528. symbolic procedure glnrsolve(u,v);
  529. %u is a list of polynomials (s.f.'s) linear in the kernels
  530. %of list v. Result is an untagged list of solutions.
  531. begin scalar arbvars,sgn,x,y;
  532. x := !*sf2ex(car u,v);
  533. u := cdr u;
  534. for each j in u do
  535. if y := extmult(!*sf2ex(j,v),x)
  536. then x := y;
  537. if inconsistency!-chk x
  538. then rederr "SOLVE given inconsistent equations";
  539. arbvars := for each j in setdiff(v,lpow x) collect
  540. j . makearbcomplex();
  541. if arbvars and null !*solvesingular
  542. then rederr "SOLVE given singular equations";
  543. if null red x then return
  544. for each j in v collect
  545. if y := atsoc(j,arbvars) then !*f2q cdr y else nil ./ 1;
  546. sgn := evenp length lpow x;
  547. return for each j in v collect if y := atsoc(j,arbvars)
  548. then !*f2q cdr y
  549. else mkglsol(j,x,sgn := not sgn,arbvars)
  550. end;
  551. symbolic procedure inconsistency!-chk u;
  552. null u or ((nil memq lpow u) and inconsistency!-chk red u);
  553. symbolic procedure mkglsol(u,v,sgn,arbvars);
  554. begin scalar s,x,y;
  555. x := nil ./ 1;
  556. y := lpow v;
  557. for each j on red v do
  558. if s := glsolterm(u,y,j,arbvars)
  559. then x := addsq(cancel(s ./ lc v),x);
  560. return if sgn then negsq x else x
  561. end;
  562. symbolic procedure glsolterm(u,v,w,arbvars);
  563. begin scalar x,y,sgn;
  564. x := lpow w;
  565. a: if null x then return
  566. if null car y then lc w
  567. else multf(cdr atsoc(car y,arbvars),
  568. if sgn then negf lc w else lc w);
  569. if car x eq u then return nil
  570. else if car x memq v then <<x := cdr x;
  571. if y then sgn := not sgn>>
  572. else if y then return nil
  573. else <<y := list car x; x := cdr x>>;
  574. go to a
  575. end;
  576. %**** Support for exterior multiplication ****
  577. % Data structure is lpow ::= list of variables in exterior product
  578. % lc ::= standard form
  579. symbolic procedure !*sf2ex(u,v);
  580. %Converts standardform u into a form distributed w.r.t. v
  581. %*** Should we check here if lc is free of v?
  582. if null u then nil
  583. else if domainp u or null(mvar u memq v) then list nil .* u .+ nil
  584. else list mvar u .* lc u .+ !*sf2ex(red u,v);
  585. symbolic procedure extmult(u,v);
  586. %Special exterior multiplication routine. Degree of form v is
  587. %arbitrary, u is a one-form.
  588. if null u or null v then nil
  589. else (if x then cdr x .* (if car x then negf multf(lc u,lc v)
  590. else multf(lc u,lc v))
  591. .+ extadd(extmult(!*t2f lt u,red v),
  592. extmult(red u,v))
  593. else extadd(extmult(red u,v),extmult(!*t2f lt u,red v)))
  594. where x = ordexn(car lpow u,lpow v);
  595. symbolic procedure extadd(u,v);
  596. if null u then v
  597. else if null v then u
  598. else if lpow u = lpow v then
  599. (lambda x,y; if null x then y else lpow u .* x .+ y)
  600. (addf(lc u,lc v),extadd(red u,red v))
  601. else if ordexp(lpow u,lpow v) then lt u .+ extadd(red u,v)
  602. else lt v .+ extadd(u,red v);
  603. symbolic procedure ordexp(u,v);
  604. if null u then t
  605. else if car u eq car v then ordexp(cdr u,cdr v)
  606. else if null car u then nil
  607. else if null car v then t
  608. else ordop(car u,car v);
  609. symbolic procedure ordexn(u,v);
  610. %u is a single variable, v a list. Returns nil if u is a member
  611. %of v or a dotted pair of a permutation indicator and the ordered
  612. %list of u merged into v.
  613. begin scalar s,x;
  614. a: if null v then return(s . reverse(u . x))
  615. else if u eq car v then return nil
  616. else if u and ordop(u,car v) then
  617. return(s . append(reverse(u . x),v))
  618. else <<x := car v . x;
  619. v := cdr v;
  620. s := not s>>;
  621. go to a
  622. end;
  623. endmodule;
  624. module quartic; % Procedures for solving cubic, quadratic and quartic
  625. % eqns.
  626. % Author: Anthony C. Hearn.
  627. fluid '(!*sub2);
  628. symbolic procedure multfq(u,v);
  629. % Multiplies standard form U by standard quotient V.
  630. begin scalar x;
  631. x := gcdf(u,denr v);
  632. return multf(quotf(u,x),numr v) ./ quotf(denr v,x)
  633. end;
  634. symbolic procedure quotsqf(u,v);
  635. % Forms quotient of standard quotient U and standard form V.
  636. begin scalar x;
  637. x := gcdf(numr u,v);
  638. return quotf(numr u,x) ./ multf(quotf(v,x),denr u)
  639. end;
  640. symbolic procedure cubertq u;
  641. simpexpt list(mk!*sq subs2!* u,'(quotient 1 3));
  642. % SIMPRAD(U,3);
  643. symbolic procedure sqrtq u;
  644. simpexpt list(mk!*sq subs2!* u,'(quotient 1 2));
  645. % SIMPRAD(U,2);
  646. symbolic procedure subs2!* u; <<!*sub2 := t; subs2 u>>;
  647. symbolic procedure solvequadratic(a2,a1,a0);
  648. % a2, a1 and a0 are standard quotients.
  649. % solves a2*x**2+a1*x+a0=0 for x.
  650. % returns a list of standard quotient solutions.
  651. begin scalar d;
  652. d := sqrtq subtrsq(quotsqf(exptsq(a1,2),4),multsq(a2,a0));
  653. a1 := quotsqf(negsq a1,2);
  654. return list(subs2!* quotsq(addsq(a1,d),a2),
  655. subs2!* quotsq(subtrsq(a1,d),a2))
  656. end;
  657. symbolic procedure solvecubic(a3,a2,a1,a0);
  658. % a3, a2, a1 and a0 are standard quotients.
  659. % solves a3*x**3+a2*x**2+a1*x+a0=0 for x.
  660. % returns a list of standard quotient solutions.
  661. % See Abramowitz and Stegun, Sect. 3.8.2, for details.
  662. begin scalar q,r,sm,sp,s1,s2,x;
  663. a2 := quotsq(a2,a3);
  664. a1 := quotsq(a1,a3);
  665. a0 := quotsq(a0,a3);
  666. q := subtrsq(quotsqf(a1,3),quotsqf(exptsq(a2,2),9));
  667. r := subtrsq(quotsqf(subtrsq(multsq(a1,a2),multfq(3,a0)),6),
  668. quotsqf(exptsq(a2,3),27));
  669. x := sqrtq addsq(exptsq(q,3),exptsq(r,2));
  670. s1 := cubertq addsq(r,x);
  671. s2 := if numr s1 then negsq quotsq(q,s1)
  672. else cubertq subtrsq(r,x);
  673. % This optimization only works if s1 is non zero.
  674. sp := addsq(s1,s2);
  675. sm := quotsqf(multsq(simp '(times i (sqrt 3)),subtrsq(s1,s2)),2);
  676. x := subtrsq(sp,quotsqf(a2,3));
  677. sp := negsq addsq(quotsqf(sp,2),quotsqf(a2,3));
  678. return list(subs2!* x,subs2!* addsq(sp,sm),
  679. subs2!* subtrsq(sp,sm))
  680. end;
  681. symbolic procedure solvequartic(a4,a3,a2,a1,a0);
  682. % Solve the quartic equation a4*x**4+a3*x**3+a2*x**2+a1*x+a0 = 0,
  683. % where the ai are standard quotients, using technique described in
  684. % Section 3.8.3 of Abramowitz and Stegun;
  685. begin scalar x,y,z;
  686. % Convert equation to monomial form.
  687. a3 := quotsq(a3,a4);
  688. a2 := quotsq(a2,a4);
  689. a1 := quotsq(a1,a4);
  690. a0 := quotsq(a0,a4);
  691. % Build and solve the resultant cubic equation. We select an
  692. % arbitrary member of its set of solutions. Ideally we should
  693. % only generate one solution, which should be the simplest.
  694. y := subtrsq(exptsq(a3,2),multfq(4,a2));
  695. % note that only first cubic solution is used here. We could save
  696. % computation by using this fact.
  697. x := car solvecubic(!*f2q 1,
  698. negsq a2,
  699. subs2!* subtrsq(multsq(a1,a3),multfq(4,a0)),
  700. subs2!* negsq addsq(exptsq(a1,2),
  701. multsq(a0,y)));
  702. % Now solve the two equivalent quadratic equations.
  703. y := sqrtq addsq(quotsqf(y,4),x);
  704. z := sqrtq subtrsq(quotsqf(exptsq(x,2),4),a0);
  705. a3 := quotsqf(a3,2);
  706. x := quotsqf(x,2);
  707. return append(solvequadratic(!*f2q 1,addsq(a3,y),subtrsq(x,z)),
  708. solvequadratic(!*f2q 1,subtrsq(a3,y),addsq(x,z)))
  709. end;
  710. endmodule;
  711. module solvetab; % Simplification rules for SOLVE.
  712. % Author: David R. Stoutemyer.
  713. % Modifications by: Anthony C. Hearn and Donald R. Morrison;
  714. algebraic operator sol;
  715. put('asin, 'inverse, 'sin);
  716. put('acos, 'inverse, 'cos);
  717. algebraic;
  718. comment Rules for reducing the number of distinct kernels in an
  719. equation;
  720. for all a,b,x such that ratnump c and ratnump d let
  721. sol(a**c-b**d, x) = a**(c*lcm(c,d)) - b**(d*lcm(c,d));
  722. for all a,b,c,d,x such that a freeof x and c freeof x let
  723. sol(a**b-c**d, x) = e**(b*log a - d*log c);
  724. for all a,b,c,d,x such that a freeof x and c freeof x let
  725. sol(a*log b + c*log d, x) = b**a*d**c - 1,
  726. sol(a*log b - c*log d, x) = b**a - d**c;
  727. for all a,b,c,d,f,x such that a freeof x and c freeof x let
  728. sol(a*log b + c*log d + f, x) = sol(log(b**a*d**c) + f, x),
  729. sol(a*log b + c*log d - f, x) = sol(log(b**a*d**c) - f, x),
  730. sol(a*log b - c*log d + f, x) = sol(log(b**a/d**c) + f, x),
  731. sol(a*log b - c*log d - f, x) = sol(log(b**a/d**c) - f, x);
  732. for all a,b,d,f,x such that a freeof x let
  733. sol(a*log b + log d + f, x) = sol(log(b**a*d) + f, x),
  734. sol(a*log b + log d - f, x) = sol(log(b**a*d) - f, x),
  735. sol(a*log b - log d + f, x) = sol(log(b**a/d) + f, x),
  736. sol(a*log b - log d - f, x) = sol(log(b**a/d) - f, x),
  737. sol(log d - a*log b + f, x) = sol(log(d/b**a) + f, x),
  738. sol(log d - a*log b - f, x) = sol(log(d/b**a) - f, x);
  739. for all a,b,c,d,x such that a freeof x and c freeof x let
  740. sol(a*log b + c*log d, x) = b**a*d**c - 1,
  741. sol(a*log b - c*log d, x) = b**a - d**c;
  742. for all a,b,d,x such that a freeof x let
  743. sol(a*log b + log d, x) = b**a*d - 1,
  744. sol(a*log b - log d, x) = b**a - d,
  745. sol(log d - a*log b, x) = d - b**a;
  746. for all a,b,c,x let
  747. sol(log a + log b + c, x) = sol(log(a*b) + c, x),
  748. sol(log a - log b + c, x) = sol(log(a/b) + c, x),
  749. sol(log a + log b - c, x) = sol(log(a*b) - c, x),
  750. sol(log a - log b - c, x) = sol(log(a/b) - c, x);
  751. for all a,c,x such that c freeof x let
  752. sol(log a + c, x) = a - e**c,
  753. sol(log a - c, x) = a - e**(-c);
  754. for all a,b,x let
  755. sol(log a + log b, x) = a*b - 1,
  756. sol(log a - log b, x) = a - b,
  757. sol(cos a - sin b, x) = sol(cos a - cos(pi/2-b), x),
  758. sol(sin a + cos b, x) = sol(sin a - sin(b-pi/2), x),
  759. sol(sin a - cos b, x) = sol(sin a - sin(pi/2-b), x),
  760. sol(sin a + sin b, x) = sol(sin a - sin(-b), x),
  761. sol(sin a - sin b, x) = if !*allbranch then sin((a-b)/2)*
  762. cos((a+b)/2) else a-b,
  763. sol(cos a + cos b, x) = if !*allbranch then cos((a+b)/2)*
  764. cos((a-b)/2) else a+b,
  765. sol(cos a - cos b, x) = if !*allbranch then sin((a+b)/2)*
  766. sin((a-b)/2) else a-b,
  767. sol(asin a - asin b, x) = a-b,
  768. sol(asin a + asin b, x) = a+b,
  769. sol(acos a - acos b, x) = a-b,
  770. sol(acos a + acos b, x) = a+b;
  771. symbolic;
  772. endmodule;
  773. end;