tps.red 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602
  1. module tps; % Truncated Power Series.
  2. % Authors: Julian Padget & Alan Barnes <barnesa@kirk.aston.ac.uk>.
  3. % Version 1.3 September 1990.
  4. %
  5. % Revisions:
  6. %
  7. % 16 Sep 90. Bugs in PS!:EXPT!-CRULE, PS!:EXPT!-ERULE corrected.
  8. % Treatment of expt generally improved.
  9. % PSREVERSE now only takes one argument: the series to be
  10. % inverted. PSCHANGEVAR added for those who feel the
  11. % need to change the expansion variable.
  12. % Code for pscompose and psreverse generalised to handle
  13. % the point at infinity correctly and to deal with series
  14. % with a negative order correctly.
  15. % Code for simppssum improved to detect non-
  16. % strictly increasing exponents.
  17. %
  18. % 6 Sep 90 PSSUM added to allow definition of series whose
  19. % general term is known.
  20. %
  21. % 8 Aug 90. FUNCTION changed to QUOTE to avoid FUNARG problem when
  22. % interpreting code on SLISP based systems. Need to be
  23. % careful with quoted lambdas!
  24. %
  25. % 26 Jul 90. JHD's smacro's ps!:numberp and ps!:atom added to improve
  26. % behaviour of system after ON BIGFLOAT.
  27. % Still dicky with NUMVAL ON. Need to REMPROP properties
  28. % !:bf!:, !:ft!: etc. from !:ps!: ? (AB)
  29. % 25 Jul 90. Global declaration of s added in ps:unknown-crule. Needed
  30. % in UOLISP, avoids warning messages in some other Lisps.
  31. % simp:ps1 tidied up (now uses selectors to access terms)
  32. % 24 Jan 90. Ps:evaluate corrected (missing assignment for next).
  33. % 7 Jul 89. Explicit compilation and evaluation rules for SQRT and
  34. % EXPT have been added. This avoids the infinite loops that
  35. % could sometimes occur when ps!: unknown!-crule was used to
  36. % generate recurrence relations for rational powers of a
  37. % power series.
  38. % A few bugs have been fixed notably one in difff and the
  39. % efficiency of code has been improved in several places.
  40. % Experimental code has been added to allow the domain mode
  41. % to be set to TPS by typing ON TPS. With this switch on,
  42. % quotients of power series are expanded automatically as
  43. % are (when NUMVAL is on) expressions such as sin a, where a
  44. % is a power series.
  45. % 3 Mar 89. Addition of code for reversion of series. Modification
  46. % of internal form to avoid circular lists as arguments.
  47. % Minor bug fixes.
  48. % A power series object is a tagged tuple of the following form:
  49. %
  50. % (:ps: . [<order>,
  51. % <last-term>,
  52. % <variable>,
  53. % <expansion-point>,
  54. % <value>,
  55. % <terms>,
  56. % <ps-expression])
  57. %
  58. % <order> is the exponent of the first term of the series and is also
  59. % used to modify the index when accessing components of the
  60. % series which are addressed by power
  61. %
  62. % <last-term> the power of the last term generated in the series
  63. % largely for the benefit of the printing routine
  64. %
  65. % <variable> is the dependent variable of this expansion, needed, in
  66. % particular, for printing and when combining two series
  67. %
  68. % <expansion!-point> is self-explanatory except that
  69. % ps!:inf denotes expansion about infinity
  70. %
  71. % <value> is the originating prefix form which is needed to allow for
  72. % power series variables appearing inside other power series
  73. % expressions
  74. %
  75. % <terms> is an alist containing the terms of the series computed so
  76. % far, access is controlled using <order> as an index base.
  77. %
  78. % <ps-expression> is a power series object corresponding to the prefix
  79. % form of which the expansion was requested, the first element
  80. % of which is the ps!:operator and the rest of which are the
  81. % ps!:operands which may themselves be pso's
  82. %
  83. % In addition we have the following degenerate forms of power series
  84. % object:
  85. % (!:ps!: . <identifier>) the value of <identifier> is a vector
  86. % as above(used in automatically generated recurrence relations)
  87. % <number>
  88. % <identifier> 2nd argument of DF, INT
  89. create!-package('(tps tpscomp tpseval tpsdom tpsrev tpsfns tpselfns
  90. tpssum),
  91. '(contrib tps));
  92. fluid '(ps!:exp!-lim knownps ps!:max!-order);
  93. % Some structure selectors and referencers.
  94. symbolic smacro procedure rands e;
  95. cdr e;
  96. symbolic smacro procedure rand1 e;
  97. cadr e;
  98. symbolic smacro procedure rand2 e;
  99. caddr e;
  100. symbolic smacro procedure rator e;
  101. car e;
  102. symbolic smacro procedure ps!:domainp u;
  103. atom u or (car u neq '!:ps!:) and not listp u;
  104. symbolic smacro procedure constantpsp u;
  105. null ps!:depvar u;
  106. symbolic smacro procedure ps!:p u;
  107. pairp u and (car u = '!:ps!:);
  108. symbolic smacro procedure ps!:atom u;
  109. atom u or (car u neq '!:ps!: and get(car u,'dname));
  110. symbolic smacro procedure ps!:numberp u;
  111. numberp u or (car u neq '!:ps!: and get(car u,'dname));
  112. symbolic procedure ps!:getv(ps,i);
  113. if eqcar(ps,'!:ps!:) then
  114. if idp cdr ps then getv(eval cdr ps,i)
  115. else getv(cdr ps,i)
  116. else rerror(tps,1,list("PS:GETV: not a ps",ps));
  117. symbolic procedure ps!:putv(ps,i,v);
  118. if eqcar(ps,'!:ps!:) then
  119. if idp cdr ps then putv(eval cdr ps,i,v)
  120. else putv(cdr ps,i,v)
  121. else rerror(tps,2,list("PS:PUTV: not a ps",ps));
  122. symbolic procedure ps!:order ps;
  123. if ps!:atom ps then 0
  124. else ps!:getv(ps,0);
  125. symbolic smacro procedure ps!:set!-order(ps,n);
  126. ps!:putv(ps,0,n);
  127. symbolic procedure ps!:last!-term ps;
  128. if ps!:atom ps then ps!:max!-order
  129. else ps!:getv(ps,1);
  130. symbolic (ps!:max!-order:= 2147483647);
  131. % symbolic here seems to be essential in Cambridge Lisp systems
  132. symbolic smacro procedure ps!:set!-last!-term (ps,n);
  133. ps!:putv(ps,1,n);
  134. symbolic procedure ps!:depvar ps;
  135. if ps!:atom ps then nil
  136. else ps!:getv(ps,2);
  137. symbolic smacro procedure ps!:set!-depvar(ps,x);
  138. ps!:putv(ps,2,x);
  139. symbolic procedure ps!:expansion!-point ps;
  140. if ps!:atom ps then nil
  141. else ps!:getv(ps,3);
  142. symbolic smacro procedure ps!:set!-expansion!-point(ps,x);
  143. ps!:putv(ps,3,x);
  144. symbolic procedure ps!:value ps;
  145. if ps!:atom ps then if ps then ps else 0
  146. else ps!:getv(ps,4);
  147. symbolic smacro procedure ps!:set!-value(ps,x);
  148. ps!:putv(ps,4,x);
  149. symbolic smacro procedure ps!:terms ps;
  150. if ps!:atom ps then list (0 . ( ps . 1))
  151. else ps!:getv(ps,5);
  152. symbolic smacro procedure ps!:set!-terms(ps,x);
  153. ps!:putv(ps,5,x);
  154. symbolic procedure ps!:expression ps;
  155. if ps!:atom ps then ps
  156. else ps!:getv(ps,6);
  157. symbolic smacro procedure ps!:set!-expression(ps,x);
  158. ps!:putv(ps,6,x);
  159. symbolic smacro procedure ps!:operator ps;
  160. car ps!:getv(ps,6);
  161. symbolic smacro procedure ps!:operands ps;
  162. cdr ps!:getv(ps,6);
  163. symbolic procedure ps!:get!-term(ps,i);
  164. (lambda(psorder,pslast);
  165. if null psorder or null pslast then nil
  166. else if i<psorder then nil ./ 1
  167. else if i>pslast then nil
  168. else begin scalar term;
  169. term:=assoc(i-psorder, ps!:terms ps);
  170. return if term then cdr term
  171. else nil ./ 1;
  172. end)
  173. (ps!:order ps,ps!:last!-term ps);
  174. symbolic procedure ps!:term(ps,i);
  175. begin scalar term;
  176. term:=ps!:get!-term (ps,i);
  177. if term then return term;
  178. for j:=ps!:last!-term(ps)+1:i do
  179. term:= ps!:evaluate(ps,j);
  180. return term
  181. end;
  182. symbolic procedure ps!:set!-term(ps,n,x);
  183. (lambda (psorder,terms);
  184. <<if null psorder then psorder:=ps!:find!-order ps;
  185. if n < psorder then
  186. rerror(tps,3,list (n, "less than the order of ", ps))
  187. else (lambda term;
  188. <<if (n=psorder and x = '(nil . 1)) then
  189. ps!:set!-order(ps,n+1);
  190. if term then rplacd(term,x)
  191. else if atom x or (numr x neq nil) then
  192. if terms then nconc(terms,list((n-psorder).x))
  193. else ps!:set!-terms(ps,list((n-psorder).x))
  194. >>)
  195. assoc(n-psorder,terms)>>)
  196. (ps!:order ps,ps!:terms ps);
  197. put('psexplim, 'simpfn, 'simppsexplim);
  198. symbolic (ps!:exp!-lim := 6); % default depth of expansion
  199. % symbolic here seems to be essential in Cambridge Lisp systems
  200. symbolic procedure simppsexplim u;
  201. begin integer n;
  202. n:=ps!:exp!-lim;
  203. if u then ps!:exp!-lim := ieval carx(u,'psexplim);
  204. return (if n=0 then nil ./ 1 else n ./ 1);
  205. end;
  206. symbolic procedure simpps a;
  207. if length a =3 then apply('simpps1,a)
  208. else rerror(tps,4,
  209. "Args should be <FORM>,<depvar>, and <point>: simpps");
  210. put('ps,'simpfn,'simpps);
  211. symbolic procedure simpps1(form,depvar,about);
  212. if form=nil then
  213. rerror(tps,5,"Args should be <FORM>,<depvar>, and <point>: simpps")
  214. else if not kernp simp!* depvar then
  215. typerr(depvar, "kernel: simpps")
  216. else if smember(depvar,(about:=prepsq simp!* about)) then
  217. rerror(tps,6,"Expansion point depends on depvar: simpps")
  218. else begin scalar knownps;
  219. return ps!:compile(ps!:presimp form,
  220. depvar,
  221. if about='infinity then 'ps!:inf
  222. else about)
  223. ./ 1
  224. end;
  225. put('psterm,'simpfn,'simppsterm);
  226. symbolic procedure simppsterm a;
  227. if length a=2 then apply('simppsterm1,a)
  228. else rerror(tps,7,
  229. "Args should be of form <power series>,<term>: simppsterm");
  230. symbolic procedure simppsterm1(p,n);
  231. << n := ieval n;
  232. p:=prepsq simp!* p;
  233. if ps!:numberp p then
  234. if n neq 0 or p=0 then nil ./ 1 else p ./ 1
  235. else if ps!:p p then
  236. << ps!:find!-order p; ps!:term(p,n)>>
  237. else typerr(p,"power series: simppsterm1")
  238. >>;
  239. put('psorder,'simpfn,'simppsorder);
  240. put('pssetorder,'simpfn,'simppssetorder);
  241. symbolic procedure simppsorder u;
  242. << u:=prepsq simp!* carx(u,'psorder);
  243. if ps!:numberp u then if u=0 then 'undefined else nil
  244. else if ps!:p u then ps!:find!-order u
  245. else typerr(u,"power series: simppsorder")
  246. >> ./ 1;
  247. symbolic procedure simppssetorder u;
  248. (lambda (psord,ps);
  249. if not ps!:p ps then typerr(ps,"power series: simppssetorder")
  250. else if not fixp psord then
  251. typerr(psord, "integer: simppssetorder")
  252. else <<u:=ps!:order ps;
  253. ps!:set!-order(ps,psord);
  254. (if u=0 then nil else u) ./ 1>>)
  255. (prepsq simp!* carx(cdr u,'pssetorder),prepsq simp!* car u);
  256. put('psexpansionpt,'simpfn,'simppsexpansionpt);
  257. symbolic procedure simppsexpansionpt u;
  258. << u:=prepsq simp!* carx(u,'psexpansionpt);
  259. if ps!:numberp u then 'undefined ./ 1
  260. else if ps!:p u then
  261. (lambda about;
  262. if about neq 'ps!:inf then
  263. simp!* about else 'infinity ./ 1)
  264. (ps!:expansion!-point u)
  265. else typerr(u,"power series: simppsexpansionpt")
  266. >>;
  267. put('psdepvar,'simpfn,'simppsdepvar);
  268. symbolic procedure simppsdepvar u;
  269. << u:=prepsq simp!* carx(u,'psdepvar);
  270. if ps!:numberp u then 'undefined
  271. else if ps!:p u then ps!:depvar u
  272. else typerr(u,"power series: simppsdepvar")
  273. >> ./ 1;
  274. put('psfunction,'simpfn,'simppsfunction);
  275. symbolic procedure simppsfunction u;
  276. << u:=prepsq simp!* carx(u,'psfunction);
  277. if ps!:numberp u then u ./ 1
  278. else if ps!:p u then simp!* ps!:value u
  279. else typerr(u,"power series: simppsfunction")
  280. >>;
  281. symbolic procedure ps!:presimp form;
  282. if (pairp form) and
  283. ((rator form = 'expt) or (rator form = 'int)) then
  284. list(rator form, prepsort rand1 form, prepsort rand2 form)
  285. else prepsort form;
  286. symbolic procedure prepsort u;
  287. % Improves log handling if logsort is defined. S.L. Kameny.
  288. if getd 'logsort then logsort u else prepsq simp!* u;
  289. % symbolic procedure tag!-if!-float n;
  290. % if floatp n then int!-equiv!-chk mkfloat n else n;
  291. symbolic procedure !*pre2dp u;
  292. begin scalar x;
  293. u:=simp!* u;
  294. return if fixp denr u
  295. % then if denr u = 1 and domainp(x:= tag!-if!-float numr u)
  296. % then x
  297. then if denr u = 1 and domainp(x:= numr u) then x
  298. else if fixp numr u then mkrn(numr u, denr u)
  299. end;
  300. flag('(!:ps!:),'full);
  301. put('!:ps!:,'simpfn,'simp!:ps!:);
  302. symbolic procedure simp!:ps!: ps; simp!:ps1 ps ./1;
  303. symbolic procedure simp!:ps1 ps;
  304. if ps!:atom ps or idp cdr ps then ps
  305. else
  306. <<if not atom ps!:expression ps then
  307. begin scalar i, term, simpfn;
  308. if (ps!:operator ps ='psgen) then
  309. simpfn:= 'simp!:ps1
  310. else simpfn:= 'resimp;
  311. i:=ps!:order ps;
  312. while term:=ps!:get!-term(ps,i) do
  313. << ps!:set!-term(ps,i,apply1(simpfn,term)); i:=i+1>>
  314. end;
  315. if atom ps!:expression ps
  316. or null ps!:depvar ps or (ps!:operator ps ='ps!:summation)
  317. then nil
  318. else<<ps!:set!-expression(ps,
  319. (ps!:operator ps) . mapcar(ps!:operands ps,
  320. 'simp!:ps1));
  321. ps!:set!-value(ps,prepsq simp!* ps!:value ps)>>;
  322. ps>>;
  323. % symbolic procedure simp!:ps1 ps;
  324. % if ps!:atom ps or idp cdr ps then ps
  325. % else
  326. % <<if not atom ps!:expression ps then
  327. % begin scalar i, term, simpfn;
  328. % if (ps!:operator ps ='psgen) then
  329. % simpfn:= 'simp!:ps1
  330. % else simpfn:= 'resimp;
  331. % i:=ps!:order ps;
  332. % while term:=ps!:get!-term(ps,i) do
  333. % << ps!:set!-term(ps,i,apply1(simpfn,term)); i:=i+1>>
  334. % end;
  335. % if atom ps!:expression ps or null ps!:depvar ps then nil
  336. % else<<ps!:set!-expression(ps,
  337. % (ps!:operator ps) . mapcar(ps!:operands ps,
  338. % 'simp!:ps1));
  339. % ps!:set!-value(ps,prepsq simp!* ps!:value ps)>>;
  340. % ps>>;
  341. put('!:ps!:,'domain!-diff!-fn,'ps!:diff!:);
  342. put('pschangevar,'simpfn,'simppschangevar);
  343. symbolic procedure simppschangevar u;
  344. (lambda (newvar,ps, oldvar);
  345. if not ps!:p ps then typerr(ps,"power series: simppschangevar")
  346. else if not kernp newvar then
  347. typerr(prepsq newvar, "kernel: simppschangevar")
  348. else <<oldvar:=ps!:depvar ps; newvar:=prepsq newvar;
  349. if smember(newvar,ps!:value ps) and newvar neq oldvar then
  350. rerror(tps,8,"Series depends on new variable")
  351. else if oldvar then <<
  352. ps!:set!-depvar(ps,newvar);
  353. ps!:set!-value(ps,subst(newvar,oldvar,ps!:value ps));
  354. ps ./ 1>>
  355. else rerror(tps,9,"Can't change variable of constant series")>>)
  356. (simp!* carx(cdr u,'pschangevar),prepsq simp!* car u,nil);
  357. endmodule;
  358. module tpscomp; % Compile prefix expression into network of
  359. % communicating power series.
  360. % Authors: Julian Padget & Alan Barnes
  361. % The compiler is rule driven by looking for a compilation rule (crule)
  362. % property on the property list of the operator. If a rule does not
  363. % exist the expression is differentiated to get an expression which is
  364. % amenable to compilation but the process takes care to check for the
  365. % existence of cycles in the derivatives e.g. sine and cosine.
  366. %
  367. % The result is an power series object which can be evaluated by the
  368. % power series evaluator.
  369. fluid '(unknowns !*exp psintconst knownps ps!:max!-order);
  370. symbolic procedure ps!:compile(form,depvar,about);
  371. if idp form then
  372. make!-ps!-id(form,depvar,about)
  373. else if ps!:numberp form then form
  374. else if ps!:p form then
  375. if((ps!:expansion!-point form=about)and(ps!:depvar form=depvar))
  376. then form
  377. else ps!:compile(ps!:value form,depvar,about)
  378. else if get(car form,'ps!:crule) then
  379. apply(get(car form,'ps!:crule),list(form,depvar,about))
  380. else (if tmp then '!:ps!: . cdr tmp % ******was eval cdr tmp
  381. % get(cdr tmp,'!:ps!:)
  382. else ps!:unknown!-crule((car form) . foreach arg in cdr form collect
  383. ps!:compile(arg,depvar,about),
  384. depvar,about))
  385. where tmp = assoc(form,knownps);
  386. symbolic procedure make!-ps!-id(id,depvar,about);
  387. begin scalar ps;
  388. ps:=make!-ps(id,id,depvar,about);
  389. if about neq 'ps!:inf then
  390. <<ps!:set!-term(ps,0,ps!:identifier!-erule(id,depvar,0,about));
  391. ps!:set!-term(ps,1,ps!:identifier!-erule(id,depvar,1,about))>>
  392. else % expansion about infinity
  393. <<ps!:set!-order(ps,-1);
  394. ps!:set!-term(ps,-1,
  395. ps!:identifier!-erule(id,depvar,-1,about))>>;
  396. ps!:set!-last!-term(ps,ps!:max!-order);
  397. return ps
  398. end;
  399. symbolic procedure make!-ps(form,exp,depvar,about);
  400. % if f is a trivial expression (it can only ever be a number,
  401. % an identifier or a ps) then it is a 'constant' and stands for
  402. % itself, otherwise a larger ps is composed from it
  403. begin scalar ps;
  404. ps:=get('tps,'tag) . mkvect 6;
  405. ps!:set!-order(ps,0);
  406. ps!:set!-expression(ps,form);
  407. ps!:set!-value(ps,exp);
  408. ps!:set!-depvar(ps,depvar);
  409. ps!:set!-expansion!-point(ps,about);
  410. ps!:set!-last!-term(ps,-1);
  411. return ps
  412. end;
  413. %symbolic procedure ps!:plus!-crule(a,d,n);
  414. % if atom cdddr a then
  415. % make!-ps('plus . list(ps!:compile(rand1 a,d,n),
  416. % ps!:compile(rand2 a,d,n)),
  417. % ps!:arg!-values a,d,n)
  418. % else
  419. % make!-ps('plus . list(ps!:compile(rand1 a,d,n),
  420. % ps!:plus!-crule('plus . cddr a,d,n)),
  421. % ps!:arg!-values a,d,n);
  422. % put('plus,'ps!:crule,'ps!:plus!-crule);
  423. put('plus,'ps!:crule,'ps!:nary!-crule);
  424. % symbolic procedure ps!:minus!-crule(a,d,n);
  425. % make!-ps(list('minus,ps!:compile(rand1 a,d,n)),
  426. % 'minus . ps!:arg!-values a,d,n);
  427. % put('minus,'ps!:crule, 'ps!:minus!-crule);
  428. symbolic procedure ps!:unary!-crule(a,d,n);
  429. make!-ps(list(rator a,ps!:compile(rand1 a,d,n)),
  430. ps!:arg!-values a,d,n);
  431. put('minus,'ps!:crule, 'ps!:unary!-crule);
  432. symbolic procedure ps!:binary!-crule(a,d,n);
  433. make!-ps((rator a) . list(ps!:compile(rand1 a,d,n),
  434. ps!:compile(rand2 a,d,n)),
  435. ps!:arg!-values a,d,n);
  436. put('difference,'ps!:crule,'ps!:binary!-crule);
  437. symbolic procedure ps!:nary!-crule(a,d,n);
  438. if null cdddr a then
  439. make!-ps((rator a) . list(ps!:compile(rand1 a,d,n),
  440. ps!:compile(rand2 a,d,n)),
  441. ps!:arg!-values a,d,n)
  442. else
  443. make!-ps((rator a) . list(ps!:compile(rand1 a,d,n),
  444. ps!:nary!-crule((rator a) . cddr a,d,n)),
  445. ps!:arg!-values a,d,n);
  446. put('times,'ps!:crule,'ps!:nary!-crule);
  447. % put('times,'ps!:crule,'ps!:times!-crule);
  448. symbolic procedure ps!:quotient!-crule(a,d,n);
  449. % forms such as (quotient (expt <x> <y>) (expt <x> <z>)) are
  450. % detected here and transformed into (expt <x>(difference <y> <z>)) to
  451. % help avoid certain essential singularities
  452. if eqcar(rand1 a,'expt) and eqcar(rand2 a,'expt) and
  453. ((rand1 rand1 a)=(rand1 rand2 a)) then
  454. ps!:compile(list('expt,
  455. rand1 rand1 a,
  456. list('difference, rand2 rand1 a,
  457. rand2 rand2 a)),d,n)
  458. else ps!:binary!-crule(a,d,n);
  459. % make!-ps('quotient . list(ps!:compile(rand1 a,d,n),
  460. % ps!:compile(rand2 a,d,n)),
  461. % ps!:arg!-values a,d,n);
  462. put('quotient,'ps!:crule,'ps!:quotient!-crule);
  463. flag('(psintconst), 'share);
  464. algebraic (psintconst:= 0 );
  465. symbolic procedure ps!:int!-crule(a,d,n);
  466. begin scalar r,arg1, psord;
  467. if not idp rand2 a then
  468. typerr(rand2 a, "kernel: ps!:int!-crule");
  469. if rand2 a=d and smember(d,prepsq simp!* psintconst) then
  470. rerror(tps,10,list("psintconst depends on depvar: ",d));
  471. arg1:=ps!:compile(prepsq simp!* rand1 a,d,n);
  472. r:= make!-ps(list('int,arg1,rand2 a),ps!:arg!-values a,d,n);
  473. psord:= ps!:find!-order arg1;
  474. if d=rand2 a then
  475. if ps!:expansion!-point(arg1) neq 'ps!:inf then
  476. if (psord < 0) and (ps!:term(arg1,-1) neq (nil ./ 1)) then
  477. rerror(tps,11,"Logarithmic Singularity")
  478. else <<ps!:set!-term(r,0,simp!* psintconst);
  479. ps!:find!-order r>>
  480. else % expansion about infinity
  481. if (psord < 2) and (ps!:term(arg1,1) neq (nil ./ 1)) then
  482. rerror(tps,12,"Logarithmic Singularity")
  483. else <<ps!:set!-term(r,0,simp!* psintconst);
  484. ps!:find!-order r>>
  485. else <<ps!:set!-term(r,0,
  486. simpint list(prepsq ps!:term(arg1,0),
  487. rand2 a));
  488. ps!:find!-order r>>;
  489. return r;
  490. end;
  491. put('int,'ps!:crule,'ps!:int!-crule);
  492. symbolic procedure ps!:arg!-values funct;
  493. (rator funct) . (foreach arg in rands funct collect
  494. if ps!:atom arg then arg
  495. else if ps!:p arg then ps!:value arg
  496. else ps!:arg!-values arg);
  497. symbolic procedure ps!:unknown!-crule(a,d,n);
  498. % unknowns is an alist structure, the CAR of which is the
  499. % form which was differentiated and the CDR is a dotted pair whose
  500. % CDR is a gensym'ed identifier which is used to build
  501. % the cyclic structures used to represent a recurrence relation.
  502. % Minor improvements by Stan Kameny, July 1990.
  503. (lambda (dfdx,unknowns,tmp);
  504. if (tmp:=assoc(caar unknowns,cdr unknowns)) then cdr tmp
  505. else
  506. (lambda(r,s); <<
  507. tmp:=ps!:arg!-values a;
  508. ps!:set!-value(r,tmp);
  509. % intern s; % apparently not needed, useful for debugging.
  510. global list s; % This is definitely needed in UOLISP.
  511. set(s,cdr r);
  512. knownps:=(tmp . s) . knownps;
  513. ps!:set!-order(r,0); % screws up if order is negative
  514. if n= 'ps!:inf then n:=0; % expansion about infinity
  515. a:=(rator a).(foreach arg in rands a collect
  516. if ps!:p arg then
  517. if ps!:find!-order arg < 0
  518. then rerror(tps,13,
  519. "Essential Singularity")
  520. else prepsq ps!:term(arg,0)
  521. else subst(n,d,arg));
  522. ps!:set!-term(r,0,simp!* a);
  523. ps!:set!-last!-term(r,0);
  524. r
  525. >> )
  526. (make!-ps(list('int, ps!:compile(dfdx,d,n),d),a,d,n),
  527. cddar unknowns)
  528. )
  529. (ps!:differentiate(a,d),(a . ('!:ps!: . gensym())) . unknowns,nil);
  530. symbolic procedure ps!:differentiate(a,v);
  531. % note the binding of !*factor to t; this ensures the result of the
  532. % differentiation will be factored, which prevents us looping
  533. % infinitely because we can't see the cycle in the derivative.
  534. % *********not necessary so removed March 1989 AB
  535. (lambda x;
  536. if eqcar(x,'df) then
  537. rerror(tps,14,
  538. list("ps:differentiate: cannot differentiate ",a))
  539. else
  540. x)
  541. % the default method catches forms which are defined by patterns
  542. % (eg Bessel functions)
  543. % ((lambda (!*factor,!*exp);
  544. ((lambda (!*exp);
  545. if get(car a,'dfn) then prepsq diffsq(simp!* a,v)
  546. else prepsq simp!* list ('df,a,v))
  547. % (t,nil));
  548. (nil));
  549. endmodule;
  550. module tpseval; % Evaluator for truncated power series.
  551. % Authors: Julian Padget & Alan Barnes
  552. % The evaluator interprets the results of the compilation phase and
  553. % is also rule driven until I get round to getting the compilation
  554. % phase to produce directly executable code
  555. % The evaluation functions live on the erule property of the name.
  556. fluid '(ps psintconst ps!:order!-limit);
  557. %symbolic (orig!*:= 0);
  558. % % symbolic here seems to be essential in Cambridge Lisp systems
  559. symbolic procedure ps!:prin!: p;
  560. (lambda (first,u,delta,symbolic!-exp!-pt,about,atinf);
  561. << if !*nat and posn!*<20 then orig!*:=posn!*;
  562. atinf:=(about='ps!:inf);
  563. ps!:find!-order p;
  564. delta:=prepf((ps!:depvar p) .** 1 .*1 .+
  565. (negf if atinf then nil
  566. % expansion about infinity
  567. else if idp about then !*k2f about
  568. else if ps!:numberp about then !*n2f about
  569. else if (u:=!*pre2dp about) then !*n2f u
  570. else !*k2f(symbolic!-exp!-pt:= compress
  571. append(explode ps!:depvar p, explode '0))));
  572. if symbolic!-exp!-pt then prin2!* "[";
  573. prin2!* "{";
  574. for i:=(ps!:order p): ps!:exp!-lim do
  575. << u:=ps!:term(p,i);
  576. if not null numr u then
  577. <<if minusf numr u then <<u:=negsq u; prin2!* " - ">>
  578. else if not first then prin2!* " + ";
  579. first := nil;
  580. if posn!*>55 then <<terpri!* nil;prin2!* " ">>;
  581. if denr u neq 1 then prin2!* "(";
  582. if u neq '(1 . 1) then
  583. maprint(prepsq u,get('times,'infix))
  584. else if i=0 then prin2!* 1;
  585. if denr u neq 1 then prin2!* ")";
  586. if i neq 0 and u neq '(1 . 1) then prin2!* "*";
  587. if i neq 0 then
  588. xprinf(!*p2f mksp(delta,
  589. if atinf then -i else i),nil,nil)
  590. >>
  591. >>;
  592. if first then prin2!* "0";
  593. if posn!*>55 then terpri!* nil;
  594. u:=ps!:exp!-lim +1;
  595. if (u=1) and not atinf and (about neq 0) then
  596. prin2!* " + O"
  597. else prin2!* " + O(";
  598. xprinf(!*p2f mksp(delta,if atinf then -u else u),nil,nil);
  599. if (u=1) and not atinf and (about neq 0) then
  600. prin2!* "}"
  601. else prin2!* ")}";
  602. if symbolic!-exp!-pt then
  603. << if posn!*>45 then terpri!* nil;
  604. prin2!* " where ";
  605. prin2!* symbolic!-exp!-pt;
  606. prin2!* " = ";
  607. maprin about;
  608. prin2!* "]"
  609. >>;
  610. terpri!* nil;
  611. >>)
  612. (t,nil,nil,nil,ps!:expansion!-point p,nil);
  613. symbolic procedure ps!:id!-order ps;
  614. (lambda u;
  615. if numberp u then u
  616. else rerror(tps,15,
  617. list("Can't find the order of ",ps!:value ps)))
  618. ps!:order ps;
  619. symbolic procedure ps!:find!-order ps;
  620. if null ps then 0
  621. else if idp ps then ps % second arg of DF etc are identifiers
  622. else if numberp ps then 0
  623. else if eqcar(ps,'!:ps!:) then <<
  624. if idp cdr ps then ps!:id!-order ps
  625. else if atom ps!:expression ps or null ps!:depvar ps then
  626. ps!:order ps
  627. else ps!:find!-order1(ps) >>
  628. else if get(car ps,'dname) then 0
  629. else rerror(tps,16,"Unexpected form in ps!:find!-order");
  630. symbolic procedure ps!:find!-order1(ps);
  631. begin scalar psoperator,psord,pslast;
  632. psoperator:=ps!:operator ps;
  633. psord:=ps!:order ps;
  634. pslast:=ps!:last!-term ps;
  635. if psord leq pslast then
  636. if psoperator neq 'int then return psord
  637. else if (psord neq 0) or (pslast neq 0) then return psord;
  638. psord:=apply(get(psoperator,'ps!:order!-fn),
  639. mapcar(ps!:operands ps,
  640. 'ps!:find!-order));
  641. ps!:set!-order(ps,psord);
  642. if psoperator= 'int and psord=0 then nil
  643. else ps!:set!-last!-term(ps,psord-1);
  644. if ps!:value ps =0 then
  645. <<psord:=0;ps!:set!-last!-term(ps,1000000)>>
  646. % prevents infinite loop if we have exact cancellation
  647. else while ps!:evaluate(ps,psord)=(nil ./ 1 ) do
  648. % in case we have finite # of cancellations in a sum or difference
  649. <<psord:=psord+1;
  650. if psord > ps!:order!-limit then
  651. rerror(tps,17,list("Expression ",ps!:value ps,
  652. " has zero expansion to order ",
  653. psord))
  654. % We may not always be able to recognise zero.
  655. % Anyway give up after 15 iterations.
  656. >>;
  657. return psord
  658. end;
  659. symbolic (ps!:order!-limit:=15);
  660. % symbolic here seems to be essential in Cambridge Lisp systems
  661. put('psordlim, 'simpfn, 'simppsordlim);
  662. symbolic procedure simppsordlim u;
  663. begin integer n;
  664. n:=ps!:order!-limit;
  665. if u then ps!:order!-limit := ieval carx(u,'psordlim);
  666. return (if n=0 then nil ./ 1 else n ./ 1);
  667. end;
  668. put('times,'ps!:order!-fn, 'plus2);
  669. put('quotient,'ps!:order!-fn, 'difference);
  670. put('plus,'ps!:order!-fn, 'min2);
  671. put('difference,'ps!:order!-fn, 'min2);
  672. put('minus,'ps!:order!-fn, '(lambda (u) u));
  673. put('int,'ps!:order!-fn,'ps!:int!-orderfn);
  674. put('df,'ps!:order!-fn,'ps!:df!-orderfn);
  675. symbolic procedure ps!:int!-orderfn(u,v);
  676. if (ps!:order ps=0) and (u geq 0) then 0
  677. else if v=ps!:depvar ps then
  678. if ps!:expansion!-point ps neq 'ps!:inf then
  679. if u=-1 then rerror(tps,18,"Logarithmic Singularity")
  680. else u+1
  681. else % expansion about infinity
  682. if u=1 then rerror(tps,19,"Logarithmic Singularity")
  683. else u-1
  684. else u;
  685. symbolic procedure ps!:df!-orderfn (u,v);
  686. if v=ps!:depvar ps then
  687. if ps!:expansion!-point ps neq 'ps!:inf then
  688. if u=0 then 0 else u-1
  689. else if u=0 then 2 else u+1 % expansion about infinity
  690. else u;
  691. symbolic procedure ps!:number!-erule(n,i);
  692. % << n:=(if numberp n then tag!-if!-float n else cdr n);
  693. <<n := if numberp n then n else cdr n;
  694. if i =0 and n neq 0 then n ./ 1 else nil ./ 1>>;
  695. symbolic procedure ps!:identifier!-erule(v,d,n,about);
  696. if v=d then
  697. if about='ps!:inf then if n=-1 then 1 ./ 1 else nil ./ 1
  698. % expansion about infinity
  699. else if n=0 then
  700. if idp about then !*k2q about
  701. % else if ps!:numberp about then !*n2f tag!-if!-float about ./ 1
  702. else if ps!:numberp about then !*n2f about ./ 1
  703. else simp!* about
  704. else if n=1 then
  705. 1 ./ 1
  706. else
  707. nil ./ 1
  708. else
  709. if n=0 then
  710. !*k2q v
  711. else
  712. nil ./ 1;
  713. symbolic procedure ps!:evaluate(ps,n);
  714. % ps can come in two flavours (one of which is degenerate):
  715. % (i) a number
  716. % (ii) a power series object
  717. % in the last case the appropriate evaluation rule for the operator
  718. % in the ps is selected and invoked
  719. if n leq ps!:last!-term ps then
  720. ps!:get!-term(ps,n)
  721. else (lambda next; <<
  722. if n < ps!:order ps then nil
  723. else <<
  724. ps!:set!-term(ps,n,next:=simp!* prepsq next);
  725. ps!:set!-last!-term(ps,n)
  726. >>;
  727. next>>)
  728. apply(get(ps!:operator ps,'ps!:erule),
  729. list(ps!:expression ps,n));
  730. symbolic procedure ps!:plus!-erule(a,n);
  731. addsq(ps!:evaluate(rand1 a,n),
  732. ps!:evaluate(rand2 a,n));
  733. put('plus,'ps!:erule,'ps!:plus!-erule);
  734. symbolic procedure ps!:minus!-erule(a,n);
  735. negsq ps!:evaluate(rand1 a,n);
  736. put('minus,'ps!:erule,'ps!:minus!-erule);
  737. symbolic procedure ps!:difference!-erule(a,n);
  738. addsq(ps!:evaluate(rand1 a,n),
  739. negsq ps!:evaluate(rand2 a,n));
  740. put('difference,'ps!:erule,'ps!:difference!-erule);
  741. symbolic procedure ps!:times!-erule(a,n);
  742. begin scalar aa,b,x,y,y1,z;
  743. aa:=rand1 a; b:= rand2 a; z:= nil ./ 1;
  744. x:=ps!:order(aa);
  745. y:=ps!:order(ps); % order of product ps
  746. y1 := ps!:order b;
  747. % Next "if" suggested by A.C. Norman to avoid different tan df rule
  748. % The problem with tan was in fact due to ps!:find!-order! - AB
  749. for i := 0:n-y do if n-x-i>=y1 then
  750. z:= addsq(z,multsq(ps!:evaluate(aa,i+x),
  751. ps!:evaluate(b,n-x-i)));
  752. return z
  753. end;
  754. put('times,'ps!:erule,'ps!:times!-erule);
  755. symbolic procedure ps!:quotient!-erule(a,n);
  756. begin scalar aa,b,x,y,z;
  757. aa:=rand1 a; b:=rand2 a; z:= nil ./ 1;
  758. y:=ps!:order(b);
  759. x:=ps!:order(ps); %order of quotient ps
  760. for i:=1:n-x do
  761. z:=addsq(z,multsq(ps!:evaluate(b,i+y),
  762. ps!:evaluate(ps,n-i)));
  763. return quotsq(addsq(ps!:evaluate(aa,n+y),negsq z),
  764. ps!:evaluate(b,y))
  765. end;
  766. put('quotient,'ps!:erule,'ps!:quotient!-erule);
  767. symbolic procedure ps!:differentiate!-erule(a,n);
  768. if rand2 a =ps!:depvar rand1 a then
  769. if ps!:expansion!-point rand1 a neq 'ps!:inf then
  770. multsq((n+1) ./ 1,ps!:evaluate(rand1 a,n+1))
  771. else multsq((1-n) ./ 1,ps!:evaluate(rand1 a,n-1))
  772. else diffsq(ps!:evaluate(rand1 a,n),rand2 a);
  773. put('df,'ps!:erule,'ps!:differentiate!-erule);
  774. symbolic procedure ps!:int!-erule(a,n);
  775. if rand2 a=ps!:depvar ps then
  776. if n=0 then simp!* psintconst
  777. else if ps!:expansion!-point ps neq 'ps!:inf then
  778. quotsq(ps!:evaluate(rand1 a,n-1),n ./ 1)
  779. else quotsq(ps!:evaluate(rand1 a,n+1),-n ./ 1)
  780. else simpint list(prepsq ps!:evaluate(rand1 a,n),rand2 a);
  781. put('int,'ps!:erule,'ps!:int!-erule);
  782. endmodule;
  783. module tpsdom; % Domain definitions for truncated power series.
  784. % Authors: Julian Padget & Alan Barnes.
  785. fluid '(ps!:exp!-lim ps!:max!-order);
  786. global '(domainlist!*);
  787. symbolic (domainlist!*:=union('(!:ps!:),domainlist!*));
  788. % symbolic here seems to be essential in Cambridge Lisp systems
  789. put('tps,'tag,'!:ps!:);
  790. put('!:ps!:,'dname,'tps);
  791. flag('(!:ps!:),'field);
  792. put('!:ps!:,'i2d,'i2ps);
  793. put('!:ps!:,'minusp,'ps!:minusp!:);
  794. put('!:ps!:,'plus,'ps!:plus!:);
  795. put('!:ps!:,'times,'ps!:times!:);
  796. put('!:ps!:,'difference,'ps!:difference!:);
  797. put('!:ps!:,'quotient,'ps!:quotient!:);
  798. put('!:ps!:,'zerop,'ps!:zerop!:);
  799. put('!:ps!:,'onep,'ps!:onep!:);
  800. put('!:ps!:,'prepfn,'ps!:prepfn!:);
  801. put('!:ps!:,'specprn,'ps!:prin!:);
  802. put('!:ps!:,'prifn,'ps!:prin!:);
  803. put('!:ps!:,'intequivfn,'psintequiv!:);
  804. % conversion functions
  805. put('!:ps!:,'!:mod!:,mkdmoderr('!:ps!:,'!:mod!:));
  806. % put('!:ps!:,'!:gi!:,mkdmoderr('!:ps!:,'!:gi!:));
  807. % put('!:ps!:,'!:bf!:,mkdmoderr('!:ps!:,'!:bf!:));
  808. % put('!:ps!:,'!:rn!:,mkdmoderr('!:ps!:,'!:rn!:));
  809. put('!:rn!:,'!:ps!:,'!*d2ps);
  810. put('!:ft!:,'!:ps!:,'cdr);
  811. put('!:bf!:,'!:ps!:,'!*d2ps);
  812. put('!:gi!:,'!:ps!:,'!*d2ps);
  813. put('!:gf!:,'!:ps!:,'!*d2ps);
  814. symbolic procedure psintequiv!: u;
  815. if idp cdr u or ps!:depvar u or length (u:=ps!:expression u)>2
  816. then nil
  817. else int!-equiv!-chk rand1 u;
  818. symbolic procedure i2ps u;
  819. if dmode!*='!:ps!: then !*d2ps u else u;
  820. symbolic procedure !*d2ps u;
  821. begin scalar ps;
  822. ps:=get('tps,'tag) . mkvect 6;
  823. ps!:set!-order(ps,0);
  824. ps!:set!-expression(ps,list ('psconstant,u));
  825. ps!:set!-value(ps,prepsq( u ./ 1));
  826. ps!:set!-last!-term(ps,ps!:max!-order);
  827. ps!:set!-terms(ps,list ( 0 . ( u ./ 1)));
  828. return ps
  829. end;
  830. %symbolic procedure ps!:compatiblep(u,v);
  831. % if (constantpsp u or constantpsp v ) then t
  832. % else if (ps!:depvar u) neq (ps!:depvar v) then nil
  833. % then nil
  834. % else if (ps!:expansion!-point u) neq (ps!:expansion!-point v)
  835. % else t;
  836. symbolic procedure ps!:minusp!: u;
  837. nil;
  838. symbolic procedure ps!:plus!:(u,v);
  839. ps!:operator!:('plus,u,v);
  840. symbolic procedure ps!:difference!:(u,v);
  841. ps!:operator!:('difference,u,v);
  842. symbolic procedure ps!:times!:(u,v);
  843. ps!:operator!:('times,u,v);
  844. symbolic procedure ps!:quotient!:(u,v);
  845. ps!:operator!:('quotient,u,v);
  846. symbolic procedure ps!:diff!:(u,v);
  847. (( if idp deriv then
  848. ps!:compile (deriv,ps!:depvar u,ps!:expansion!-point u)
  849. else if numberp deriv then
  850. if zerop deriv then nil
  851. else deriv
  852. else make!-ps(list('df,u,v), deriv,
  853. ps!:depvar u,ps!:expansion!-point u))
  854. ./ 1)
  855. where (deriv = prepsq simp!* list('df, ps!:value u,v));
  856. symbolic procedure ps!:operator!:(op,u,v);
  857. begin scalar value,x,x0,y;
  858. x:=ps!:depvar u;
  859. y:=ps!:depvar v;
  860. if null x then
  861. if null y then
  862. return << if ps!:p u then u:=rand1 ps!:expression u;
  863. if ps!:p v then v:=rand1 ps!:expression v;
  864. if op='quotient and atom u and atom v then
  865. if null u then nil
  866. else
  867. <<op:=gcdn(u,v);u:=u/op;v:=v/op;
  868. if v=1 then u else '!:rn!: . (u . v)>>
  869. else dcombine!*(u,v,op)>>
  870. else << x:= y; x0:=ps!:expansion!-point v>>
  871. else if null y then
  872. x0:=ps!:expansion!-point u
  873. else if ((x0:=ps!:expansion!-point u) neq ps!:expansion!-point v)
  874. or (x neq y) then
  875. rerror(tps,20,
  876. list("ps!:operator: incompatible power series in ",
  877. op));
  878. value:= simp!* list(op,ps!:value u,ps!:value v);
  879. if denr value=1 and domainp numr value then return numr value;
  880. return make!-ps(list(op,u,v), prepsq value,x,x0)
  881. end;
  882. symbolic procedure ps!:zerop!: u;
  883. (numberp v and zerop v) where v=ps!:value u;
  884. symbolic procedure ps!:onep!: u;
  885. onep ps!:value u;
  886. symbolic procedure ps!:prepfn!: u;
  887. u;
  888. initdmode 'tps;
  889. endmodule;
  890. module tpsrev; % Power Series Reversion & Composition
  891. % Author: Alan Barnes November 1988
  892. %
  893. % If y is a power series in x then psreverse expresses x as a power
  894. % series in y-y0 where y0 is zero order term of y.
  895. % This is known as power series reversion (functional inverse)
  896. % pscompose functionally composes two power series
  897. %
  898. %Two new prefix operators are introduced PSREV and PSCOMP.
  899. %These appear in the expression part of the power series objects
  900. %generated by calls to psreverse and pscompose respectively.
  901. %The argument of PSREV is the 'generating series' of the
  902. %series (PS1 say) to be inverted. This is a generalised power series
  903. %object which looks like a standard power series object except that
  904. %each of its terms is itself a power series (rather than a standard
  905. %quotient), the nth term being the power series of the nth power of
  906. %PS1. The expression part of the generating series is (PSGEN <PS1>).
  907. %
  908. %When power series PS1 and PS2 are composed (i.e. PS2 is substituted
  909. %for the expansion variable of PS1 and the result expressed as a power
  910. %series in the expansion variable of PS2), the expression part of
  911. %the power series object generated is
  912. % (PSCOMP <PS1> <generating-series of PS2>)
  913. %The generating series should only appear inside the operators PSREV
  914. %and PSGEN and not at 'top level'. It cannot sensibly be printed with
  915. %the power series print function. Special functions are needed to
  916. %access and modify terms of the generating series, although these
  917. %are simply defined in terms of the functions for manipulating
  918. %standard power series objects.
  919. %% The algorithms used are based on those described in
  920. %Feldmar E & Kolbig K S, Computer Physics Commun. 39, 267-284 (1986).
  921. fluid '(ps);
  922. put('psreverse, 'simpfn, 'simppsrev);
  923. symbolic procedure simppsrev a;
  924. if length a=1 then apply('simppsrev1,a)
  925. else rerror(tps,21,"Wrong number of arguments to PSREVERSE");
  926. symbolic procedure simppsrev1(series);
  927. begin scalar rev,psord, depvar,about;
  928. % if not kernp simp!* revvar then
  929. % typerr(revvar,"kernel: simppsrev");
  930. series:=prepsq simp!* series;
  931. if not ps!:p series then
  932. rerror(tps,22,
  933. "Argument should be a <POWER SERIES>: simppsrev");
  934. ps!:find!-order series;
  935. depvar:=ps!:depvar series;
  936. if (psord:=ps!:order series)=1 then
  937. about:=0
  938. else if (psord=0) and (ps!:term(series,1) neq (nil ./ 1)) then
  939. about := prepsq ps!:get!-term(series,0)
  940. else if psord =-1 then about:='ps!:inf
  941. else rerror(tps,23,"Series cannot be inverted: simppsrev");
  942. rev:=ps!:compile(list('psrev,series),depvar,about);
  943. if ps!:expansion!-point series = 'ps!:inf then
  944. return make!-ps(list('quotient,1,rev),
  945. ps!:value rev,depvar,about) ./ 1
  946. else return rev ./ 1
  947. end;
  948. symbolic procedure ps!:generating!-series(a,psord,inverted);
  949. begin scalar ps, depvar,point;
  950. depvar:=ps!:depvar a;
  951. point:= ps!:expansion!-point a;
  952. ps:=make!-ps(list('psgen, a,inverted),ps!:value a,
  953. depvar, point);
  954. ps!:set!-order(ps,psord);
  955. ps!:set!-last!-term(ps,psord);
  956. a:=ps!:compile(list('expt,a,if inverted then -psord else psord),
  957. depvar,point);
  958. ps!:find!-order a;
  959. ps!:set!-term(ps,psord,a);
  960. return ps
  961. end;
  962. symbolic smacro procedure ps!:get!-rthpow(genseries,r);
  963. ps!:get!-term(genseries,r);
  964. symbolic procedure ps!:set!-rthpow(genseries,r);
  965. begin scalar rthpow, series, power;
  966. series:=ps!:expression genseries;
  967. power:= if rand2 series then -r else r;
  968. series:=rand1 series;
  969. rthpow:=ps!:compile(list('expt,series,power),
  970. ps!:depvar series,ps!:expansion!-point series);
  971. ps!:find!-order rthpow;
  972. ps!:set!-term(genseries,r,rthpow);
  973. return rthpow
  974. end;
  975. symbolic procedure ps!:term!-rthpow(genseries,r,n);
  976. begin scalar term,series;
  977. series:= ps!:get!-rthpow(genseries,r);
  978. if null series then <<for i:=ps!:last!-term genseries +1:r do
  979. series:=ps!:set!-rthpow(genseries,i);
  980. ps!:set!-last!-term(genseries,r)>>;
  981. term:=ps!:get!-term(series,n);
  982. if null term then for j:=ps!:last!-term(series)+1:n do
  983. term:= ps!:evaluate(series,j);
  984. return term
  985. end;
  986. put('psrev,'ps!:crule,'ps!:rev!-crule);
  987. symbolic procedure ps!:rev!-crule(a,d,n);
  988. begin scalar series;
  989. series :=rand1 a;
  990. if (n neq 'ps!:inf) and (n neq 0) then
  991. series:=ps!:remove!-constant series;
  992. return
  993. make!-ps(list('psrev,
  994. ps!:generating!-series(series,1,
  995. if n='ps!:inf then t
  996. else nil)),
  997. list('psrev,ps!:value rand1 a),d,n)
  998. end;
  999. symbolic procedure ps!:remove!-constant(ps);
  1000. << ps:=ps!:compile(list('difference,ps,prepsq ps!:term(ps,0)),
  1001. ps!:depvar ps,
  1002. ps!:expansion!-point ps);
  1003. ps!:find!-order ps;
  1004. ps >>;
  1005. % symbolic procedure ps!:reciprocal(ps);
  1006. % << ps:=ps!:compile(list('quotient,1,ps),
  1007. % ps!:depvar ps,
  1008. % ps!:expansion!-point ps);
  1009. % ps!:find!-order ps;
  1010. % ps >>;
  1011. put('psrev,'ps!:erule,'ps!:rev!-erule);
  1012. put('psrev,'ps!:order!-fn,'ps!:rev!-orderfn);
  1013. symbolic procedure ps!:rev!-orderfn u;
  1014. if (u:=ps!:expansion!-point
  1015. ps!:get!-rthpow(rand1 ps!:expression ps,1))=0
  1016. then 1
  1017. else if u = 'ps!:inf then 1
  1018. else 0;
  1019. symbolic procedure ps!:rev!-erule(a,n);
  1020. begin scalar genseries,x,z;
  1021. z:=nil ./ 1; genseries:=rand1 a;
  1022. if n=0 then
  1023. if (x:=ps!:expansion!-point ps!:get!-rthpow(genseries,1))='ps!:inf
  1024. then return (nil ./ 1)
  1025. else return simp!* x;
  1026. if n=1 then
  1027. return invsq ps!:term!-rthpow(genseries,1,1);
  1028. for i:=1:n-1 do
  1029. z:=addsq(z,multsq(ps!:evaluate(ps,i),
  1030. ps!:term!-rthpow(genseries,i,n)));
  1031. return quotsq(negsq z,ps!:term!-rthpow(genseries,n,n))
  1032. end;
  1033. put('pscomp,'ps!:crule,'ps!:comp!-crule);
  1034. put('pscomp,'ps!:erule,'ps!:comp!-erule);
  1035. put('pscomp,'ps!:order!-fn,'ps!:comp!-orderfn);
  1036. symbolic procedure ps!:comp!-orderfn(u,v);
  1037. if u=0 then 0
  1038. else ps!:find!-order(ps!:get!-rthpow(rand2 ps!:expression ps,u));
  1039. symbolic procedure ps!:comp!-crule(a,d,n);
  1040. begin scalar series1,series2,n1;
  1041. series1:=rand1 a; series2:=rand2 a;
  1042. n1 := ps!:expansion!-point series1;
  1043. if (n1 neq 0) and (n1 neq 'ps!:inf) then
  1044. series2:=ps!:remove!-constant series2;
  1045. return
  1046. make!-ps(list('pscomp,series1,
  1047. ps!:generating!-series(series2,
  1048. ps!:order series1,
  1049. if n1='ps!:inf then t
  1050. else nil)),
  1051. ps!:arg!-values a,d,n)
  1052. end;
  1053. symbolic procedure ps!:comp!-erule(a,n);
  1054. begin scalar aa,genseries,z,psord1;
  1055. z:=nil ./ 1; aa:=rand1 a; genseries:=rand2 a;
  1056. psord1:=ps!:order aa;
  1057. % if n=0 then return ps!:term(aa,0);
  1058. for i:=psord1:n do
  1059. z:=addsq(z,multsq(ps!:evaluate(aa,i),
  1060. ps!:term!-rthpow(genseries,i,n)));
  1061. return z
  1062. end;
  1063. put('pscompose, 'simpfn, 'simppscomp);
  1064. symbolic procedure simppscomp a;
  1065. if length a=2 then apply('simppscomp1,a)
  1066. else rerror(tps,24,
  1067. "Args should be <POWER SERIES>,<POWER SERIES>: simppscomp");
  1068. symbolic procedure simppscomp1(ps1,ps2);
  1069. begin scalar x,d,n1,n;
  1070. ps1:=prepsq simp!* ps1;
  1071. if ps!:numberp ps1 then
  1072. return ((if zerop ps1 then nil else ps1) ./ 1);
  1073. if not ps!:p ps1 or not ps!:p(ps2:=prepsq simp!* ps2) then
  1074. rerror(tps,25,
  1075. "Args should be <POWER SERIES>,<POWER SERIES>: simppscomp");
  1076. ps!:find!-order ps1;
  1077. x:=ps!:find!-order ps2;
  1078. d:= ps!:depvar ps2;
  1079. n1:= ps!:expansion!-point ps1;
  1080. n:= ps!:expansion!-point ps2;
  1081. if (x >0 and n1 = 0) or
  1082. (x <0 and n1 = 'ps!:inf) or
  1083. (x=0 and n1=prepsq ps!:term(ps2,0))
  1084. then return ps!:compile(list('pscomp,ps1,ps2),d,n) ./ 1
  1085. else rerror(tps,26,"Series cannot be composed: simppscomp");
  1086. end;
  1087. algebraic operator psrev,pscomp;
  1088. endmodule;
  1089. module tpsfns;
  1090. % Expansion of elementary functions as power series using DOMAINVALCHK
  1091. % TPS & NUMVAL must be on for these functions to be invoked
  1092. % Example sin a where a is a power series will now be expanded
  1093. % Author: Alan Barnes, March 1989
  1094. fluid '(!*numval);
  1095. put('exp, '!:ps!:, 'ps!:exp!:);
  1096. put('log, '!:ps!:, 'ps!:log!:);
  1097. put('sin, '!:ps!:, 'ps!:sin!:);
  1098. put('cos, '!:ps!:, 'ps!:cos!:);
  1099. put('tan, '!:ps!:, 'ps!:tan!:);
  1100. put('asin, '!:ps!:, 'ps!:asin!:);
  1101. put('acos, '!:ps!:, 'ps!:acos!:);
  1102. put('atan, '!:ps!:, 'ps!:atan!:);
  1103. put('sinh, '!:ps!:, 'ps!:sinh!:);
  1104. put('cosh, '!:ps!:, 'ps!:cosh!:);
  1105. put('tanh, '!:ps!:, 'ps!:tanh!:);
  1106. put('asinh, '!:ps!:, 'ps!:asinh!:);
  1107. put('acosh, '!:ps!:, 'ps!:acosh!:);
  1108. put('atanh, '!:ps!:, 'ps!:atanh!:);
  1109. put('expt, '!:ps!:, 'ps!:expt!:);
  1110. % the above is grotty but necessary as unfortunately DOMAINVALCHK
  1111. % passes arglist of sin (rather than sin . arglist) to ps!:sin!: etc
  1112. symbolic procedure ps!:expt!:(base,exp);
  1113. begin scalar !*numval,depvar,about;
  1114. % NB binding of !*numval avoids infinite loop
  1115. depvar:= ps!:depvar base;
  1116. if null depvar then <<
  1117. depvar:=ps!:depvar exp;
  1118. about:= ps!:expansion!-point exp>>
  1119. else about:= ps!:expansion!-point base;
  1120. if null depvar then error(999,"constantps**constantps formed");
  1121. return ps!:compile(list('expt, base,exp),depvar,about)
  1122. end;
  1123. symbolic procedure ps!:unary!:fn(fn, arg);
  1124. begin scalar !*numval; % NB binding of !*numval avoids infinite loop
  1125. return ps!:compile(list(fn, arg),
  1126. ps!:depvar arg,
  1127. ps!:expansion!-point arg)
  1128. end;
  1129. symbolic procedure ps!:cos!: arg;
  1130. ps!:unary!:fn('cos,arg);
  1131. symbolic procedure ps!:sin!: arg;
  1132. ps!:unary!:fn('sin,arg);
  1133. symbolic procedure ps!:tan!: arg;
  1134. ps!:unary!:fn('tan,arg);
  1135. symbolic procedure ps!:log!: arg;
  1136. ps!:unary!:fn('log,arg);
  1137. symbolic procedure ps!:exp!: arg;
  1138. ps!:unary!:fn('exp,arg);
  1139. symbolic procedure ps!:cosh!: arg;
  1140. ps!:unary!:fn('cosh,arg);
  1141. symbolic procedure ps!:sinh!: arg;
  1142. ps!:unary!:fn('sinh,arg);
  1143. symbolic procedure ps!:tanh!: arg;
  1144. ps!:unary!:fn('tanh,arg);
  1145. symbolic procedure ps!:asin!: arg;
  1146. ps!:unary!:fn('asin,arg);
  1147. symbolic procedure ps!:acos!: arg;
  1148. ps!:unary!:fn('acos,arg);
  1149. symbolic procedure ps!:atan!: arg;
  1150. ps!:unary!:fn('atan,arg);
  1151. symbolic procedure ps!:asinh!: arg;
  1152. ps!:unary!:fn('asinh,arg);
  1153. symbolic procedure ps!:acosh!: arg;
  1154. ps!:unary!:fn('acosh,arg);
  1155. symbolic procedure ps!:atanh!: arg;
  1156. ps!:unary!:fn('atanh,arg);
  1157. endmodule;
  1158. module tpselfns;
  1159. % Specific compilation and evaluation rules for elementary functions
  1160. % Automatically generated recurrence relations can sometimes lead to
  1161. % infinite loops.
  1162. % Also helps in the detection of branch point singularities
  1163. % Author: Alan Barnes. March 1989
  1164. fluid '(ps!:max!-order ps);
  1165. put('sqrt,'ps!:crule,'ps!:unary!-crule);
  1166. put('sqrt,'ps!:order!-fn,'ps!:sqrt!-orderfn);
  1167. put('sqrt,'ps!:erule,'ps!:sqrt!-erule);
  1168. symbolic procedure ps!:sqrt!-orderfn u;
  1169. (if v*2=u then v else rerror(tps,27,"Branch Point in Sqrt"))
  1170. where v=u/2;
  1171. symbolic procedure ps!:sqrt!-erule(a,n);
  1172. begin scalar aa,x,y,z;
  1173. aa:=rand1 a; z:= nil ./ 1;
  1174. y:=ps!:order aa;
  1175. x:=ps!:order(ps); %order of sqrt ps
  1176. if n=x then return simpexpt(list(prepsq ps!:evaluate(aa,y),
  1177. '(quotient 1 2)));
  1178. for k:=1:n-x do
  1179. z:=addsq(z,
  1180. multsq(((lambda y; if y=0 then nil else y)
  1181. (k*3-2*n+y)) ./ 1,
  1182. multsq(ps!:evaluate(aa,k+y),
  1183. ps!:evaluate(ps,n-k))));
  1184. return quotsq(z,multsq(2*(n-x) ./ 1,ps!:evaluate(aa,y)))
  1185. end;
  1186. % alternative algorithm (for order 0 only)
  1187. % for i:=1:n-1 do
  1188. % z:=addsq(z,multsq(multsq( i ./ 1,ps!:evaluate(ps,i)),
  1189. % ps!:evaluate(ps,n-i)));
  1190. % z:=multsq(z, 1 ./ (n+1));
  1191. % return quotsq(addsq(ps!:evaluate(aa,n),negsq z),
  1192. % multsq(2 ./ 1,ps!:evaluate(b,x)))
  1193. symbolic procedure ps!:expt!-erule(a,n);
  1194. begin scalar base,exp,x,y,z,p,q;
  1195. base:= rand1 a;
  1196. if length a=3 then
  1197. <<exp:=rand2 a;p:=exp;q:=1>>
  1198. else <<
  1199. p:=rand2 a; q:=cadddr a;
  1200. exp:=list('quotient,p,q)>>;
  1201. % exp:=ps!:value rand2 a;
  1202. % exponent is always p or (QUOTIENT p q) with p,q integers
  1203. x:= nil ./ 1;
  1204. y:=ps!:order(base);
  1205. z:= ps!:order ps; % order of exponential
  1206. if n=z then
  1207. return simpexpt(list(prepsq ps!:evaluate(base,y),exp))
  1208. else <<for k:=1:n-z do
  1209. x:=addsq(x,
  1210. multsq(((lambda num; if num=0 then nil else num)
  1211. (k*p+q*(k-n+z))) ./ q,
  1212. multsq(ps!:evaluate(base,k+y),
  1213. ps!:evaluate(ps,n-k))));
  1214. return quotsq(x,multsq((n-z) ./ 1,ps!:evaluate(base,y)))
  1215. >>;
  1216. end;
  1217. put('expt,'ps!:erule, 'ps!:expt!-erule);
  1218. put('expt,'ps!:crule,'ps!:expt!-crule);
  1219. put('expt,'ps!:order!-fn,'ps!:expt!-orderfn);
  1220. symbolic procedure ps!:expt!-crule(a,d,n);
  1221. % we will assume that forms like (expt (expt <x> <y>) <z>) will
  1222. % continue to be transformed by SIMP!* into (expt <x> (times <y> <z>))
  1223. % this is very important for the avoidance of essential singularities
  1224. begin scalar exp1,exp2,b,psvalue;
  1225. exp1:=rand2 a;
  1226. if (ps!:p exp1 and constantpsp exp1) then exp1:=ps!:value exp1;
  1227. begin scalar dmode!*, alglist!*;
  1228. exp2:=simp!* exp1;
  1229. exp1:=prepsq exp2
  1230. end;
  1231. if (atom numr exp2 and atom denr exp2) then
  1232. <<exp1:=numr exp2; exp2:=denr exp2>>
  1233. else return
  1234. ps!:unknown!-crule(list('expt,'e,
  1235. ps!:compile(if rand1 a='e then exp1
  1236. else list('times, exp1,
  1237. list('log,rand1 a)),
  1238. d,n)),
  1239. d,n);
  1240. b:=ps!:compile(rand1 a,d,n);
  1241. psvalue:=ps!:arg!-values a;
  1242. if exp2=1 then
  1243. if exp1=nil then
  1244. return if ps!:zerop!: b
  1245. then rerror(tps,28,"0**0 formed: ps:expt-crule")
  1246. else 1
  1247. else if exp1=1 then return b
  1248. else if exp1=2 then
  1249. return make!-ps(list('times,b,b),psvalue,d,n)
  1250. else if exp1 = -1 then
  1251. return make!-ps(list('quotient,1,b),psvalue,d,n)
  1252. else return make!-ps(list('expt,b,exp1),psvalue,d,n)
  1253. else return make!-ps(list('exp3,b,exp1,exp2),psvalue,d,n);
  1254. end;
  1255. symbolic procedure ps!:expt!-orderfn(u,v);
  1256. u*rand2 ps!:expression ps;
  1257. symbolic procedure ps!:exp3!-orderfn(u,v,w);
  1258. begin scalar expres;
  1259. expres:=ps!:expression ps;
  1260. v:= rand2 expres; w:=cadddr expres;
  1261. if cdr(v:=divide(u * v,w))=0 then
  1262. return car v
  1263. else rerror(tps,29,"Branch Point in EXPT")
  1264. end;
  1265. put('exp3,'ps!:order!-fn,'ps!:exp3!-orderfn);
  1266. put('exp3,'ps!:erule,'ps!:expt!-erule);
  1267. endmodule;
  1268. module tpssum;
  1269. % Written by Alan Barnes. September 1990
  1270. % Allows power series whose general term is given to be manipulated.
  1271. %
  1272. % pssum(<sumvar>=<lowlim>, <coeff>, <depvar>, <about>, <power>);
  1273. %
  1274. % <sumvar> summation variable (a kernel)
  1275. % <lowlim> lower limit of summation (an integer)
  1276. % <coeff> general coefficient of power series (algebraic)
  1277. % <depvar> expansion variable of series (a kernel)
  1278. % <about> expansion point of series (algebraic)
  1279. % <power> general exponent of power series (algebraic)
  1280. % <power> must be a strictly increasing function of <sumvar>
  1281. % this is now partially checked by the system
  1282. symbolic procedure ps!:summation!-erule(a,n);
  1283. begin scalar power, coeff,sumvar,current!-index,last!-exp,current!-exp;
  1284. current!-index:= rand2 a;
  1285. sumvar:= rand1 a; coeff := cdddr a;
  1286. power:= cadr coeff; coeff:=car coeff;
  1287. last!-exp:= ieval reval subst(current!-index,sumvar,power);
  1288. repeat <<
  1289. current!-index:=current!-index+1;
  1290. current!-exp:= ieval reval subst(current!-index,sumvar,power);
  1291. if current!-exp leq last!-exp then
  1292. rerror(tps,30,"Exponent not strictly increasing: ps:summation");
  1293. if current!-exp < n then <<
  1294. ps!:set!-term(ps,current!-exp,
  1295. simp!* subst(current!-index,sumvar,coeff));
  1296. ps!:set!-last!-term(ps,current!-exp);
  1297. rplaca(cddr a,current!-index)>>;
  1298. last!-exp:=current!-exp>>
  1299. until current!-exp geq n;
  1300. return if current!-exp = n then <<
  1301. rplaca(cddr a,current!-index);
  1302. simp!* subst(current!-index,sumvar,coeff) >>
  1303. else (nil ./ 1)
  1304. end;
  1305. put('ps!:summation, 'ps!:erule, 'ps!:summation!-erule);
  1306. put('ps!:summation, 'simpfn, 'simpiden);
  1307. put('pssum, 'simpfn, 'simppssum);
  1308. symbolic procedure simppssum a;
  1309. begin scalar !*nosubs,from,sumvar,lowlim,coeff,
  1310. power,depvar,about,psord,term;
  1311. if length a neq 5 then rerror(tps,31,
  1312. "Args should be <FROM>,<coeff>,<depvar>,<point>,<power>: simppssum");
  1313. !*nosubs := t; % We don't want left side of eqns to change.
  1314. from := reval car a;
  1315. !*nosubs := nil;
  1316. if not eqexpr from then
  1317. errpri2(car a,t)
  1318. else <<sumvar:= cadr from;
  1319. if not kernp simp!* sumvar then
  1320. typerr(sumvar, "kernel: simppssum");
  1321. lowlim:= ieval caddr from >>;
  1322. coeff:= prepsq simp!* cadr a;
  1323. a:= cddr a;
  1324. depvar := car a; about:=prepsq simp!* cadr a;
  1325. if about = 'infinity then about := 'ps!:inf;
  1326. power:= prepsq simp!* caddr a;
  1327. if not kernp simp!* depvar then
  1328. typerr(depvar, "kernel: simppssum")
  1329. else if depvar=sumvar then rerror(tps,32,
  1330. "Summation and expansion variables are the same: simppssum")
  1331. else if smember(depvar,about) then
  1332. rerror(tps,33,"Expansion point depends on depvar: simppssum")
  1333. else if smember(sumvar,about) then rerror(tps,34,
  1334. "Expansion point depends on summation var: simppssum")
  1335. else if not smember(sumvar,power) then rerror(tps,35,
  1336. "Exponent does not depend on summation variable: simppssum");
  1337. lowlim:=lowlim-1;
  1338. repeat <<
  1339. lowlim:=lowlim+1;
  1340. psord:= ieval reval subst(lowlim,sumvar,power)>>
  1341. until (term:=simp!* subst(lowlim,sumvar,coeff)) neq '(nil . 1);
  1342. ps:=make!-ps(list('ps!:summation,sumvar,lowlim,coeff,power),
  1343. list('ps!:summation,from,coeff,depvar,about,power),
  1344. depvar, about);
  1345. ps!:set!-order(ps,psord);
  1346. ps!:set!-term(ps,psord, term);
  1347. ps!:set!-last!-term(ps,psord);
  1348. return (ps ./ 1)
  1349. end;
  1350. endmodule;
  1351. end;