symint.red 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. module symint; % Improved simplification of symbolic integrals
  2. % Author: Francis J. Wright
  3. % An extension of simpint1 in module driver (by Mary Ann Moore, Arthur
  4. % C. Norman and John P. Fitch) to provide better simplification of
  5. % integrals of symbolic derivatives and integrals. (Originally
  6. % motivated by the needs of the CRACK package.)
  7. % Change Log:
  8. % 7/1/98: Partial integration for integrals of integrals
  9. % 10/1/98: Extended partial integration for integrals of integrals
  10. % 11/1/98: Commutation of integrals
  11. % 21/2/98: df(y,x,2) etc. handling corrected
  12. fluid '(!*failhard !*IntDfFound);
  13. switch CommuteInt; % off by default (for now)
  14. deflist('((CommuteInt ((t (rmsubs))))), 'simpfg);
  15. % If the switch CommuteInt is turned on then the top-level integration
  16. % in a symbolic integral is commuted into the integrand to try to
  17. % simplify it, and if that fails and the result is a symbolic multiple
  18. % integral then it is left in canonical nesting order (as is already
  19. % done automatically for multiple derivatives). However, an
  20. % integrable nested derivative is integrated regardless of this
  21. % switch.
  22. switch PartialInt, PartialIntDf, PartialIntInt; % off by default
  23. deflist('((PartialInt ((t (on '(PartialIntDf PartialIntInt)))
  24. (nil (off '(PartialIntDf PartialIntInt)))))
  25. (PartialIntDf ((t (rmsubs))))
  26. (PartialIntInt ((t (rmsubs))))), 'simpfg);
  27. % If the switch PartialIntDf is turned on then integration by parts is
  28. % performed if the result simplifies in the sense that it integrates a
  29. % symbolic derivative and does not introduce new symbolic derivatives.
  30. % However, because the initial integral contains an unevaluated
  31. % derivative then the result must still contain an unevaluated
  32. % integral.
  33. % If the switch PartialIntInt is turned on then integration by parts
  34. % is performed if the result simplifies in the sense that it removes a
  35. % symbolic integral from the integrand and does not introduce new
  36. % symbolic integrals. However, because the initial integral contains
  37. % an unevaluated integral then the result must still contain an
  38. % unevaluated integral.
  39. % The switch PartialInt is just a convenience to turn both the above
  40. % switches on or off together.
  41. switch XPartialInt, XPartialIntDf, XPartialIntInt; % off by default
  42. deflist('((XPartialInt ((t (on '(XPartialIntDf XPartialIntInt)))
  43. (nil (off '(XPartialIntDf XPartialIntInt)))))
  44. (XPartialIntDf ((t (rmsubs))))
  45. (XPartialIntInt ((t (rmsubs))))), 'simpfg);
  46. % These switches control extended partial integration of integrals of
  47. % the form int( int(u(x,z),z) * v(x), x ), which is experimental,
  48. % somewhat heuristic and may be slow.
  49. symbolic procedure symint u;
  50. % u has the form (int y x).
  51. % At this point linearity has been applied.
  52. begin scalar v, y, x;
  53. y := cadr u; x := caddr u;
  54. % Check for a directly integrable derivative:
  55. if (v := NestedIntDf(y,x,nil)) then return mksq(v,1);
  56. if !*failhard then rerror(int,4,"FAILHARD switch set");
  57. if (!*PartialIntDf or !*PartialIntInt) and
  58. % Integrate by parts if the result simplifies:
  59. % DO WE NEED TO CALL SIMPINT1 RECURSIVELY ON THE RESULT?
  60. (v := PartialInt(y,x)) then return mksq(v,1);
  61. if (!*XPartialIntDf or !*XPartialIntInt) and
  62. % EXPERIMENTAL! Try extended partial integration:
  63. (v := XPartialInt(y,x)) then return mksq(v,1);
  64. return mksq(u,1)
  65. end;
  66. %% symbolic procedure NestedIntDf(y, x);
  67. %% %% int( ... df(f,A,x,B) ..., x) -> ... df(f,A,B) ...
  68. %% %% Find a df(f,A,x,B) among possibly nested int's and df's within
  69. %% %% the integrand y in int(y,x), and return the whole structure y
  70. %% %% but with the derivative integrated; otherwise return nil.
  71. %% %% [A,B are arbitrary sequences of kernels.]
  72. %% not atom y and
  73. %% begin scalar car_y, nested;
  74. %% return
  75. %% if (car_y := car y) eq 'df and memq(x, cddr y) then
  76. %% %% int( df(f, A, x, B), x ) -> df(f, A, B)
  77. %% 'df . cadr y . delete(x, cddr y)
  78. %% %% use delete for portability!
  79. %% %% deleq is defined in CSL, delq in PSL -- oops!
  80. %% else if memq(car_y, '(df int)) and
  81. %% (nested := NestedIntDf(cadr y, x)) then
  82. %% %% int( df(int(df(f, A, x, B), c), C), x ) ->
  83. %% %% df(int(df(f, A, B), c), C)
  84. %% %% int( int(df(f, A, x, B), c), x ) ->
  85. %% %% int(df(f, A, B), c)
  86. %% car_y . nested . cddr y
  87. %% end;
  88. symbolic procedure NestedIntDf(y, x, !*recursive);
  89. %% In order to simplify a symbolic integral int(y,x), commute the
  90. %% integral through integrals and derivatives in the integrand to
  91. %% try to find an integrable integrand. Return the result if
  92. %% successful; otherwise return nil. [A,B are arbitrary sequences
  93. %% of kernels or "kernels followed by integers".] If the integral
  94. %% does not simplify, optionally commute multiple integrals into
  95. %% canonical nesting order, as is done in the standard
  96. %% differentiator code for multiple derivatives. !*recursive is
  97. %% nil in the top-level call, t in recursive calls. [NB: The
  98. %% top-level call of this procedure makes redundant the let rule in
  99. %% the standard integrator code to integrate derivatives.]
  100. not atom y and
  101. begin scalar fn, nested;
  102. return
  103. if (fn := car y) eq 'df then % integrating a derivative
  104. if (nested := IntDf(y, x)) then nested
  105. %% int( ... df(f, A, x, B) ... , x ) -> df(f, A, B)
  106. else if (nested := NestedIntDf(cadr y, x, t)) then
  107. %% recursing into the integrand
  108. fn . nested . cddr y
  109. else nil
  110. else if !*failhard then nil % give up!
  111. else if fn eq 'int then % integrating an integral
  112. if eq(x, caddr y) then
  113. %% int( ... int(f, x) ... , x ) -> stop
  114. nil
  115. else if (nested := NestedIntDf(cadr y, x, t)) then
  116. %% recursing into the integrand
  117. fn . nested . cddr y
  118. else if !*CommuteInt and ordp(x, caddr y) then
  119. %% Commute integrals into canonical nesting order:
  120. %% int( ... int(f, b) ... , a ) ->
  121. %% int( ... int(f, a) ... , b )
  122. %% Successive calls of the integrator by the simplifier
  123. %% to integrate nested integrals causes this code to
  124. %% sort the integrands into canonical order.
  125. {'int, {'int, cadr y, x}, caddr y}
  126. else nil
  127. else if !*recursive and !*CommuteInt and
  128. %% y is not an integral or a derivative -- try to
  129. %% integrate it unless at top level:
  130. not eqcar(nested := reval {'int,y,x}, 'int) then nested
  131. end;
  132. symbolic procedure IntDf(y, x);
  133. % y = df(f, u, nu, v, nv, ...) where nu, nv, ... optional
  134. % if x = u, v, ... then return int(y, x)
  135. begin scalar !*IntDfFound;
  136. x := IntDfVars(cddr y, x);
  137. if !*IntDfFound then return
  138. if x then 'df . cadr y . x else cadr y
  139. end;
  140. symbolic procedure IntDfVars(y, x);
  141. if y then
  142. if car y eq x then
  143. begin scalar n;
  144. !*IntDfFound := t;
  145. return
  146. if (y := cdr y) and fixp(n := car y) then <<
  147. y := cdr y;
  148. if n > 2 then y := (n-1) . y;
  149. x . y
  150. >> else y
  151. end
  152. else car y . IntDfVars(cdr y, x);
  153. symbolic procedure PartialInt(y, x);
  154. %% Integrate by parts if the resulting integral simplifies;
  155. %% otherwise return nil. Split integrand into a derivative or
  156. %% integral and a second factor and call the appropriate procedure.
  157. %% Try all possible allowed partial integrations in turn.
  158. not atom y and
  159. begin scalar denlist, faclist, facs, df_or_int, result;
  160. % Process any quotient:
  161. if car y eq 'quotient then <<
  162. denlist := cddr y;
  163. % y := numerator:
  164. if atom(y := cadr y) then return % no derivative or integral
  165. >>;
  166. % y := list of factors:
  167. if car y eq 'times then y := cdr y
  168. else if denlist or !*PartialIntInt then y := y . nil
  169. % Can do double integral int(int(u(x),x),x) as a special case
  170. else return;
  171. faclist := y;
  172. % Loop through all integrable derivatives or differentiable
  173. % integrals among the factors:
  174. continue:
  175. while faclist and ( atom(df_or_int := car faclist) or
  176. not (memq(car df_or_int, '(df int)) and
  177. memq(x, cddr df_or_int)) ) do
  178. faclist := cdr faclist;
  179. % Finally, break the loop if there is no integrable derivative
  180. % or differentiable integral:
  181. if null faclist then return;
  182. facs := delete(df_or_int, y); % list of factors
  183. facs := if null facs then 1
  184. else if cdr facs then 'times . facs else car facs;
  185. if denlist then facs := 'quotient . facs . denlist;
  186. if car df_or_int eq 'df then
  187. (if !*PartialIntDf and
  188. (result := PartialIntDf(facs, df_or_int, x))
  189. then return result)
  190. else
  191. (if !*PartialIntInt and
  192. (result := PartialIntInt(df_or_int, facs, x))
  193. then return result);
  194. % Continue the loop through the factors in faclist:
  195. faclist := cdr faclist;
  196. goto continue
  197. end;
  198. symbolic procedure PartialIntDf(u, df_v, x);
  199. %% int(u(x)*df(v(x),x), x) -> u(x)*v(x) - int(df(u(x),x)*v(x), x)
  200. %% Integrate by parts if the resulting integral simplifies [to
  201. %% avoid infinite loops], which means that df(u(x),x) may not
  202. %% contain any unevaluated derivatives; otherwise return nil.
  203. begin scalar v;
  204. v := IntDf(df_v, x);
  205. % Check that df(u(x),x) simplifies:
  206. if smemq('df, df_v := reval {'df,u,x}) then return;
  207. return reval {'difference,
  208. {'times,u,v}, {'int, {'times, df_v, v}, x}}
  209. end;
  210. symbolic procedure PartialIntInt(int_u, v, x);
  211. %% int(int(u(x),x) * v(x), x) ->
  212. %% int(u(x),x) * int(v(x),x) - int( u(x) * int(v(x),x), x )
  213. %% Integrate by parts if the resulting integral simplifies [to
  214. %% avoid infinite loops], which means that int(v(x),x) may not
  215. %% remain an unevaluated integral; otherwise return nil.
  216. begin scalar u;
  217. u := cadr int_u; % kernel being integrated
  218. % Check that int(v(x),x) simplifies:
  219. if eqcar(v := reval {'int,v,x}, 'int) then return;
  220. return reval {'difference,
  221. {'times,int_u,v}, {'int, {'times,u,v}, x}}
  222. end;
  223. symbolic procedure XPartialInt(y, x);
  224. %% Extended partial integration. This code is somewhat heuristic
  225. %% and may be slow. The problem is to try to simplify
  226. %% int( int(u(x,z),z) * v(x), x ).
  227. %% Integrate by parts if the resulting integral simplifies;
  228. %% otherwise return nil. Split integrand into an integral NOT wrt
  229. %% x and a second factor and call the appropriate procedure. Try
  230. %% all possible allowed partial integrations in turn.
  231. not atom y and
  232. begin scalar denlist, faclist, facs, int, result;
  233. % Process any quotient:
  234. if car y eq 'quotient then <<
  235. denlist := cddr y;
  236. % y := numerator:
  237. if atom(y := cadr y) then return % no derivative or integral
  238. >>;
  239. % y := list of factors:
  240. if car y eq 'times then y := cdr y
  241. else if denlist then y := y . nil
  242. else return;
  243. faclist := y;
  244. % Loop through all integrals among the factors:
  245. continue:
  246. while faclist and ( not eqcar(int := car faclist, 'int) or
  247. eq(x, caddr int) ) do
  248. faclist := cdr faclist;
  249. % Finally, break the loop if there is no appropriate integral:
  250. if null faclist then return;
  251. facs := delete(int, y); % list of factors
  252. facs := if null facs then 1
  253. else if cdr facs then 'times . facs else car facs;
  254. if denlist then facs := 'quotient . facs . denlist;
  255. if facs = 1 then return; % ?????
  256. if (result :=
  257. (!*XPartialIntDf and XPartialIntDf(int, facs, x)) or
  258. (!*XPartialIntInt and XPartialIntInt(int, facs, x)))
  259. then return result;
  260. % Continue the loop through the factors in faclist:
  261. faclist := cdr faclist;
  262. goto continue
  263. end;
  264. symbolic procedure XPartialIntDf(int_u, v, x);
  265. %% int(int(u(x,z),z)*v(x), x) ->
  266. %% int(int(u(x,z),x),z) * v(x) -
  267. %% int( int(int(u(x,z),x),z) * df(v(x),x), x )
  268. %% provided df(v(x),z) and int(u(x,z),x) simplify.
  269. begin scalar df_v, z;
  270. % Check that df(v(x),x) simplifies:
  271. if smemq('df, df_v := reval {'df, v, x}) then return;
  272. z := caddr int_u;
  273. int_u := reval {'int, cadr int_u, x}; % int(u(x,z),x)
  274. % Check that int(u(x,z),x) simplifies:
  275. if eqcar(int_u, 'int) then return;
  276. int_u := reval {'int, int_u, z}; % int(int(u(x,z),x),z)
  277. return reval {'difference,
  278. {'times,int_u,v}, {'int, {'times, int_u, df_v}, x}}
  279. end;
  280. symbolic procedure XPartialIntInt(int_u, v, x);
  281. %% int(int(u(x,z),z) * v(x), x) ->
  282. %% int(u(x,z),z) * int(v(x),x) -
  283. %% int( int(df(u(x,z),x),z) * int(v(x),x), x )
  284. %% provided int(v(x),x) and int(df(u(x,z),x),z) simplify.
  285. %% If x = z then this reduces to PartialIntInt.
  286. begin scalar u;
  287. u := cadr int_u; % kernel being integrated
  288. % Check that int(v(x),x) simplifies:
  289. if eqcar(v := reval {'int, v, x}, 'int) then return;
  290. % Check that df(u(x),x) simplifies:
  291. if smemq('df, u := reval {'df, u, x}) then return;
  292. % Check that int(df(u(x,z),x),z) simplifies:
  293. if eqcar(u := reval {'int, u, caddr int_u}, 'int) then return;
  294. return reval {'difference,
  295. {'times,int_u,v}, {'int, {'times,u,v}, x}}
  296. end;
  297. endmodule;
  298. end;