aboutsummaryrefslogtreecommitdiff
path: root/src/maple/Diff.maple
blob: 10a66ee4b34dba5c4b5b56d1905eeec199717f8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# Diff.maple -- Diff() and friends
# $Header$

#
# simplify/Diff - simplify(...) function for expressions containing Diff()
# Diff - "inert" derivative function (low-level simplifications)
# Diff/gridfn - simplifications that know about gridfns
#

################################################################################
################################################################################
################################################################################

#
# This function is called automatically by  simplify()  whenever
#  simplify()'s  argument contains a  Diff()  call.  This interface
# to  simplify()  is described in
#	Michael B. Monagan
#	"Tips for Maple Users"
#	The Maple Technical Newsletter
#	Issue 10 (Fall 1993), p.10-18, especially p.17-18
# but not in the Maple library manual or the online help, which both
# imply that  simplify(..., Diff)  is needed for this procedure to be
# invoked).
#
# This function recurses over the expression structure.  For each
#  Diff() call, it calls  Diff()  itself to do "basic" derivative
# simplifications, then calls `Diff/gridfn`() to do further simplifications
# based on gridfn properties.
#
# expr = (in) An expression to be simplified.
#
# Results:
# The function returns the "simplified" expression.
#
`simplify/Diff` :=
proc(expr)
option remember;		# performance optimization
local temp;

# recurse on tables, equations, lists, sets, sums, products, powers
if type(expr, {table, `=`, list, set, `+`, `*`, `^`})
   then return map(simplify, expr);
end if;

# return unchanged on numbers, names, procedures
if type(expr, {numeric, name, procedure})
   then return expr;
end if;

# function call ==> simplify  Diff()  calls, recurse on others
if type(expr, function)
   then
	if (op(0, expr) = 'Diff')
	   then return Diff(op(expr));	# "basic" derivative simplifications
	   else
		temp := map(simplify, [op(expr)]);
		op(0,expr); return '%'(op(temp));
	end if;
end if;

# unknown type
ERROR(`expr has unknown type`, `whattype(expr)=`, whattype(expr));

end proc;

################################################################################

#
# This function implements some of the more useful simplification
# rules of algebra and calculus for Maple's "inert" differentiation
# function  Diff() .  It then calls  `Diff/gridfn`()  to do any
# further simplifications based on gridfn properties.
#
# In particular:
# - It distributes over sums.
# - It distributes over leading numeric factors.  (Maple's built-in
#   simplifier will always make numeric factors leading.)
# - It knows that derivatives of numeric constants are zero.
# - It knows that the derivative of a name with respect to itself is one.
# - It knows the product rule
#	Diff(f*g,x) = Diff(f,x)*g + f*Diff(g,x)
# - It knows the power rule
#	Diff(f^n,x) = n * f^(n-1) * Diff(f,x)
# - It flattens multi-level "Diff() trees", i.e. it makes the transformation
#	Diff(Diff(f,x),y) = Diff(f,x,y);
# - It canonicalizes the order of the things we're differentiating with
#   respect to, so that  simplify()  will recognize that derivatives
#   commute, i.e. that
#	Diff(f,x,y) = Diff(f,y,x)
#
# Arguments:
# operand = (in) The thing to be differentiated.
# var_seq = (in) (varargs) An expression sequence of the variables to
#			   differentiate with respect to.
#
unprotect('Diff');
Diff :=
proc(operand)			# varargs
option remember;		# performance optimization
local var_list,
      nn, nderiv,
      op_cdr,
      f, g, x, x_car, x_cdr, temp,
      n,
      inner_operand, inner_var_list, k,
      sorted_var_list,
      operand2, var_seq2;

var_list := [args[2..nargs]];

nn := nops(operand);
nderiv := nops(var_list);

##print(`Diff():  nn=`,nn,`  nderiv=`,nderiv,
##      `  operand=`,operand,`  var_list=`,var_list);

# distribute (recurse) over sums
if (type(operand, `+`))
   then return simplify(
		  sum('Diff(op(k, operand), op(var_list))', 'k'=1..nn)
		       );
end if;

# distribute (recurse) over leading numeric factors
if (type(operand, `*`) and (nn >= 1) and type(op(1,operand), numeric))
   then
	op_cdr := product('op(k,operand)', 'k'=2..nn);
	return simplify(  op(1,operand) * Diff(op_cdr, op(var_list))  );
end if;

# derivatives of numbers are zero
if (type(operand, numeric))
   then return 0;
end if;

# derivative of a name with respect to itself is one
if (type(operand, name) and (var_list = [operand]))
   then return 1;
end if;

# implement the product rule
# ... we actually implement it only for a 1st derivative of the
#     product of two factors; for fancier cases we recurse
if (type(operand, `*`))
   then
	if (nn = 0)
	   then # empty product = 1
		return 0;		# ==> Diff(1) = 0

	elif (nn = 1)
	   then # singleton product is equal to the single factor
		return simplify(  Diff(op(1,operand), op(var_list))  );

	elif (nn >= 2)
	   then # nontrivial product rule case

		# note that if we have more than 2 factors, g will
		# be the product of the cdr of the factor list, so
		# our later  Diff(g,...)  calls will recurse as
		# necessary to handle this case

		f := op(1,operand);
		g := product('op(k,operand)', 'k'=2..nn);

		if (nderiv = 1)
		   then # product rule for 1st derivatives:
			#   Diff(f*g,x) = Diff(f,x)*g + f*Diff(g,x)
			x := var_list[1];

			return simplify(  Diff(f,x)*g  +  f*Diff(g,x)  );

		   else # product rule for 2nd (and higher) derivatives
			# ==> recurse on derivatives
			x_car := var_list[1];
			x_cdr := var_list[2..nderiv];
			temp := simplify(  Diff(f*g, x_car)  );
			return simplify(  Diff(temp, op(x_cdr))  );
		end if;
	else ERROR(`impossible value of nn in product rule!`,
		   `(this should never happen!)`,
		   `   operand=`,operand,`   nn=`,nn);
	end if;
end if;

# implement the power rule
# ... we actually implement it only for 1st derivatives; for higher
#     derivatives we recurse
if (type(operand, `^`))
   then
	f := op(1,operand);
	n := op(2,operand);

	if (nderiv = 1)
	   then # power rule for 1st derivatives:
		#   Diff(f^n,x) = n * f^(n-1) * Diff(f,x)
		x := var_list[1];

		return simplify(  n * f^(n-1) * Diff(f, x)  );

	   else # power rule for 2nd (and higher) derivatives:
		# ==> recurse on derivatives
		x_car := var_list[1];
		x_cdr := var_list[2..nderiv];
		temp := simplify(  Diff(f^n, x_car)  );

		return simplify(  Diff(temp, op(x_cdr))  );
	end if;
end if;

# flatten (recurse on) multi-level "Diff() trees"
if (type(operand, function) and (op(0, operand) = 'Diff'))
   then
	inner_operand := op(1, operand);
	inner_var_list := [op(2..nn, operand)];

	return simplify(
		  Diff(inner_operand, op(inner_var_list), op(var_list))
		       );
end if;

# canonicalize ordering of derivatives
sorted_var_list := sort_var_list(var_list);

# simplifications based on gridfn properties known here...
temp := `Diff/gridfn`(operand, op(sorted_var_list));

# more simplifications based on gridfn properties known in other directories
if ( type(`Diff/gridfn2`, procedure)
     and type(temp, function) and (op(0,temp) = 'Diff') )
   then operand2 := op(1,temp);
	var_seq2 := op(2..nops(temp), temp);
	temp := `Diff/gridfn2`(operand2, var_seq2);
fi;

return temp;
end proc;

################################################################################

#
# This function implements further simplification rules for  Diff()
# based on gridfn properties (or at least those known here).
#
# It currently knows about the following simplifications:
# - Diff(X_ud[u,i], x_xyz[j]) --> X_udd[u,i,j]
#
# Anything else is returned unchanged.  (To avoid infinite recursion,
# such a return is *unevaluated*.)
#
# Arguments:
# operand = (in) The thing to be differentiated.
# var_seq = (in) (varargs) An expression sequence of the variables to
#			   differentiate with respect to.
#
`Diff/gridfn` :=
proc(operand)			# varargs
option remember;		# performance optimization
global
  @include "../maple/coords.minc",
  @include "../maple/gfa.minc",
  @include "../gr/gr_gfas.minc";
local var_list, posn;

var_list := [args[2..nargs]];

if ( type(operand, indexed) and (op(0,operand) = 'X_ud')
     and (nops(var_list) = 1) and member(var_list[1],x_xyz_list,'posn') )
   then return X_udd[op(operand), posn];
end if;

# unevaluated return to avoid infinite recursion
return 'Diff'(operand, op(var_list));
end proc;