aboutsummaryrefslogtreecommitdiff
path: root/src/interpolate.maple
blob: 2a6d322cba9ddca3e4756050395e39f340675e23 (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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
# interpolate.maple -- compute interpolation formulas/coefficients
# $Header$

#
# <<<representation of numbers, data values, etc>>>
# Lagrange_polynomial_interpolant - compute Lagrange polynomial interpolant
# Hermite_polynomial_interpolant - compute Hermite polynomial interpolant
# coeffs_as_lc_of_data - coefficients of ... (linear combination of data)
#
# print_coeffs__lc_of_data - print C code to compute coefficients
# print_load_data - print C code to load input array chunk into struct data
# print_store_coeffs - print C code to store struct coeffs "somewhere"
# print_interp_cmpt__lc_of_data - print C code for computation of interpolant
#
# coeff_name - name of coefficient of data at a given [m] coordinate
# data_var_name - name of variable storing data value at a given [m] coordinate
#

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

#
# ***** representation of numbers, data values, etc *****
#
# We use RATIONAL(p.0,q.0) to denote the rational number p/q.
#
# We use DATA(...) to represent the data values being interpolated at a
# specified [m] coordinate, where the arguments are the [m] coordinates.
#
# We use COEFF(...) to represent the molecule coefficient at a specified
# [m] coordinate, where the arguments are the [m] coordinates.
#
# For example, the usual 1-D centered 2nd order 1st derivative molecule
# would be written
#	RATIONAL(-1.0,2.0)*DATA(-1) + RATIONA(1.0,2.0)*DATA(1)
# and its coefficients as
#	COEFF(-1) = RATIONAL(-1.0,2.0)
#	COEFF(1) = RATIONAL(1.0,2.0)
#

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

#
# This function computes a Lagrange polynomial interpolant in any
# number of dimensions.
#
# Arguments:
# fn = The interpolation function.  This should be a procedure in the
#      coordinates, having the coefficients as global variables.  For
#      example,
#	  proc(x,y) c00 + c10*x + c01*y end proc
# coeff_list = A list of the interpolation coefficients (coefficients in
#	       the interpolation function), for example [c00, c10, c01].
# coord_list = A list of the coordinates (independent variables in the
#	       interpolation function), for example [x,y].
# posn_list = A list of positions (each a list of numeric values) where the
#	      interpolant is to use data, for example  hypercube([0,0], [1,1]).
#	      Any positions may be used; if they're redundant (as in the
#	      example) the least-squares interpolant is computed.
#
# Results:
# This function returns the interpolating polynomial, in the form of
# an algebraic expression in the coordinates and the data values.
#
Lagrange_polynomial_interpolant :=
proc(
      fn::procedure, coeff_list::list(name),
      coord_list::list(name), posn_list::list(list(numeric))
    )
local posn, data_eqns, coeff_eqns;

# coefficients of interpolating polynomial
data_eqns := {  seq( fn(op(posn))='DATA'(op(posn)) , posn=posn_list )  };
coeff_eqns := linalg[leastsqrs](data_eqns, {op(coeff_list)});
if (has(coeff_eqns, '_t'))
   then error "interpolation coefficients aren't uniquely determined!";
end if;

# interpolant as a polynomial in the coordinates
return subs(coeff_eqns, eval(fn))(op(coord_list));
end proc;

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

#
# This function computes a Hermite polynomial interpolant in any
# number of dimensions.  This is a polynomial which
# * has values which match the given data DATA() at a specified set of
#   points, and
# * has derivatives which match the specified finite-difference derivatives
#   of the given data DATA() at a specified set of points
#
# For the derivative matching, we actually match all possible products
# of 1st derivatives, i.e. in 2-D we match dx, dy, and dxy, in 3-D we
# match dx, dy, dz, dxy, dxz, dyz, and dxyz, etc etc.
#
# Arguments:
# fn = The interpolation function.  This should be a procedure in the
#      coordinates, having the coefficients as global variables.  For
#      example,
#		proc(x,y)
#		+ c03*y^3 + c13*x*y^3 + c23*x^2*y^3 + c33*x^3*y^3
#		+ c02*y^2 + c12*x*y^2 + c22*x^2*y^2 + c32*x^3*y^2
#		+ c01*y   + c11*x*y   + c21*x^2*y   + c31*x^3*y
#		+ c00     + c10*x     + c20*x^2     + c30*x^3
#		end proc;
# coeff_set = A set of the interpolation coefficients (coefficients in
#	       the interpolation function), for example
#			{
#			c03, c13, c23, c33,
#			c02, c12, c22, c32,
#			c01, c11, c21, c31,
#			c00, c10, c20, c30
#			}
# coord_list = A list of the coordinates (independent variables in the
#	       interpolation function), for example [x,y].
# deriv_set = A set of equations of the form
#		{coords} = proc
#	      giving the derivatives which are to be matched, and the
#	      procedures to compute their finite-difference approximations.
#	      Each procedure should take N_dims integer arguments specifying
#	      an evaluation point, and return a suitable linear combination
#	      of the DATA() for the derivative at that point.  For example
#			{
#			  {x}   = proc(i::integer, j::integer)
#				  - 1/2*DATA(i-1,j) + 1/2*DATA(i+1,j)
#				  end proc
#			,
#			  {y}   = proc(i::integer, j::integer)
#				  - 1/2*DATA(i,j-1) + 1/2*DATA(i,j+1)
#				  end proc
#			,
#			  {x,y} = proc(i::integer, j::integer)
#				  - 1/4*DATA(i-1,j+1) + 1/4*DATA(i+1,j+1)
#				  + 1/4*DATA(i-1,j-1) - 1/4*DATA(i+1,j-1)
#				  end proc
#			}
# fn_posn_set = A set of positions (each a list of numeric values)
#		where the interpolant is to match the given data DATA(),
#		for example
#			{[0,0], [0,1], [1,0], [1,1]}
# deriv_posn_set = A list of positions (each a list of numeric values)
#		   where the interpolant is to match the derivatives
#		   specified by  deriv_set , for example
#			{[0,0], [0,1], [1,0], [1,1]}
#
# Results:
# This function returns the interpolating polynomial, in the form of
# an algebraic expression in the coordinates and the data values.
#
Hermite_polynomial_interpolant :=
proc(
      fn::procedure,
      coeff_set::set(name),
      coord_list::list(name),
      deriv_set::set(set(name) = procedure),
      fn_posn_set::set(list(numeric)),
      deriv_posn_set::set(list(numeric))
    )
local fn_eqnset, deriv_eqnset, coeff_eqns, subs_eqnset;


#
# compute a set of equations
#	{fn(posn) = DATA(posn)}
# giving the function values to be matched
#
fn_eqnset := map(
		    # return equation that fn(posn) = DATA(posn)
		    proc(posn::list(integer))
		    fn(op(posn)) = 'DATA'(op(posn));
		    end proc
		  ,
		    fn_posn_set
		);


#
# compute a set of equations
#	{ diff(fn,coords)(posn) = DERIV(coords)(posn) }
# giving the derivative values to be matched, where DERIV(coords)
# is a placeholder for the appropriate derivative
#
map(
       # return set of equations for this particular derivative
       proc(deriv_coords::set(name))
       local deriv_fn;
       fn(op(coord_list));
       diff(%, op(deriv_coords));
       deriv_fn := unapply(%, op(coord_list));
       map(
	      proc(posn::list(integer))
	      deriv_fn(op(posn)) = 'DERIV'(op(deriv_coords))(op(posn));
	      end proc
	    ,
	      deriv_posn_set
	  );
       end proc
     ,
       map(lhs, deriv_set)
   );
deriv_eqnset := `union`(op(%));


#
# solve overall set of equations for coefficients
# in terms of DATA() and DERIV() values
#
coeff_eqns := solve[linear](fn_eqnset union deriv_eqnset, coeff_set);
if (indets(map(rhs,%)) <> {})
   then error "no unique solution for coefficients -- %1 eqns for %2 coeffs",
	      nops(fn_eqnset union deriv_eqnset),
	      nops(coeff_set);
fi;


#
# compute a set of substitution equations
#	{'DERIV'(coords) = procedure}
#
subs_eqnset := map(
		      proc(eqn::set(name) = procedure)
		      'DERIV'(op(lhs(eqn))) = rhs(eqn);
		      end proc
		    ,
		      deriv_set
		  );


#
# compute the coefficients in terms of the DATA() values
#
subs(subs_eqnset, coeff_eqns);
eval(%);

#
# compute the interpolant as a polynomial in the coordinates
#
subs(%, fn(op(coord_list)));
end proc;

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

#
# This function takes as input an interpolating polynomial, expresses
# it as a linear combination of the data values, and returns the coefficeints
# of that form.
# 
# Arguments:
# interpolant = The interpolating polynomial (an algebraic expression
#		in the coordinates and the data values).
# posn_list = The same list of data positions used in the interpolant.
#
# Results:
# This function returns the coefficients, as a list of equations of the
# form   COEFF(...) = value , where each  value  is a polynomial in the
# coordinates.  The order of the list matches that of  posn_list.
#
coeffs_as_lc_of_data :=
proc(
      interpolant::algebraic,
      posn_list::list(list(numeric))
    )
local data_list, interpolant_as_lc_of_data;

# interpolant as a linear combination of the data values
data_list := [ seq( 'DATA'(op(posn)) , posn=posn_list ) ];
interpolant_as_lc_of_data := collect(interpolant, data_list);

# coefficients of the data values in the linear combination
return map(
	      proc(posn::list(numeric))
	      coeff(interpolant_as_lc_of_data, DATA(op(posn)));
	      'COEFF'(op(posn)) = %;
	      end proc
	    ,
	      posn_list
	  );
end proc;

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

#
# This function prints C expressions for the coefficients of an
# interpolating polynomial.  (The polynomial is expressed as linear
# combinations of the data values with coefficients which are
# RATIONAL(p,q) calls.)
#
# Arguments:
# coeff_list = A list of the coefficients, as returned from
#	       coeffs_as_lc_of_data() .
# coeff_name_prefix = A prefix string for the coefficient names.
# temp_name_type = The C type to be used for Maple-introduced temporary
#		   names, eg. "double".
# file_name = The file name to write the coefficients to.  This is
#	      truncated before writing.
#
print_coeffs__lc_of_data :=
proc( coeff_list::list(specfunc(numeric,COEFF) = algebraic),
      coeff_name_prefix::string,
      temp_name_type::string,
      file_name::string )
global `codegen/C/function/informed`;
local coeff_list2, cmpt_list, temp_name_list;

# convert LHS of each equation from a COEFF() call (eg COEFF(-1,+1))
# to a Maple/C variable name (eg coeff_I_m1_p1)
coeff_list2 := map(
		      proc(coeff_eqn::specfunc(numeric,COEFF) = algebraic)
		      local posn;
		      posn := [op(lhs(coeff_eqn))];
		      coeff_name(posn,coeff_name_prefix);
		      convert(%, name);	# codegen[C] wants LHS
					# to be an actual Maple *name*
		      % = fix_rationals(rhs(coeff_eqn));
		      end proc
		    ,
		      coeff_list
		  );

#
# generate the C code
#

# tell codegen[C] not to warn about unknown RATIONAL() and DATA() "fn calls"
# via undocumented :( global table
`codegen/C/function/informed`['RATIONAL'] := true;
`codegen/C/function/informed`['DATA'] := true;

ftruncate(file_name);

# optimized computation sequence for all the coefficients
# (may use local variables t0,t1,t2,...)
cmpt_list := [codegen[optimize](coeff_list2, tryhard)];

# list of the t0,t1,t2,... local variables
temp_name_list := nonmatching_names(map(lhs,cmpt_list), coeff_name_prefix);

# declare the t0,t1,t2,... local variables (if there are any)
if (nops(temp_name_list) > 0)
   then print_name_list_dcl(%, temp_name_type, file_name);
fi;

# now print the optimized computation sequence
codegen[C](cmpt_list, filename=file_name);

fclose(file_name);

NULL;
end proc;

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

#
# This function prints a sequence of C expression to assign the data-value
# variables, eg
#	data->data_m1_p1 = DATA(-1,1);
#
# Arguments:
# posn_list = The same list of positions as was used to compute the
#	      interpolating polynomial.
# data_var_name_prefix = A prefix string for the data variable names.
# file_name = The file name to write the coefficients to.  This is
#	      truncated before writing.
#
print_load_data :=
proc(
      posn_list::list(list(numeric)),
      data_var_name_prefix::string,
      file_name::string
    )

ftruncate(file_name);
map(
       proc(posn::list(numeric))
       fprintf(file_name,
	       "%s = %a;\n",
	       data_var_name(posn,data_var_name_prefix),
	       DATA(op(posn)));
       end proc
     ,
       posn_list
   );
fclose(file_name);

NULL;
end proc;

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

#
# This function prints a sequence of C expression to store the interpolation
# coefficients in  COEFF(...)  expressions, eg
#	COEFF(1,-1) = factor * coeffs->coeff_p1_m1;
#
# Arguments:
# posn_list = The list of positions in the molecule.
# coeff_name_prefix = A prefix string for the coefficient names,
#		      eg "factor * coeffs->coeff_"
# file_name = The file name to write the coefficients to.  This is
#	      truncated before writing.
#
print_store_coeffs :=
proc(
      posn_list::list(list(numeric)),
      coeff_name_prefix::string,
      file_name::string
    )

ftruncate(file_name);
map(
       proc(posn::list(numeric))
       fprintf(file_name,
	       "%a = %s;\n",
	       'COEFF'(op(posn)),
	       coeff_name(posn,coeff_name_prefix));
       end proc
     ,
       posn_list
   );
fclose(file_name);

NULL;
end proc;

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

#
# This function prints a C expression to evaluate a molecule, i.e.
# to compute the molecule as a linear combination of the data values.
#
# Arguments:
# posn_list = The list of positions in the molecule.
# coeff_name_prefix = A prefix string for the coefficient names.
# data_var_name_prefix = A prefix string for the data variable names.
# file_name = The file name to write the coefficients to.  This is
#	      truncated before writing.
#
print_evaluate_molecule :=
proc(
      posn_list::list(list(numeric)),
      coeff_name_prefix::string,
      data_var_name_prefix::string,
      file_name::string
    )

ftruncate(file_name);

# list of "coeff*data_var" terms
map(
       proc(posn::list(numeric))
       sprintf("%s*%s",
	       coeff_name(posn,coeff_name_prefix),
	       data_var_name(posn,data_var_name_prefix));
       end proc
     ,
       posn_list
   );

ListTools[Join](%, "\n  + ");
cat(op(%));
fprintf(file_name, "    %s;\n", %);

fclose(file_name);

NULL;
end proc;

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

#
# This function computes the name of the coefficient of the data at a
# given [m] position, i.e. it encapsulates our naming convention for this.
#
# Arguments:
# posn = (in) The [m] coordinates.
# name_prefix = A prefix string for the coefficient name.
#
# Results:
# The function returns the coefficient, as a Maple string.
#
coeff_name :=
proc(posn::list(numeric), name_prefix::string)
cat(name_prefix, sprint_numeric_list(posn));
end proc;

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

#
# This function computes the name of the variable in which the C code
# will store the input data at a given [m] position, i.e. it encapsulates
# our naming convention for this.
#
# Arguments:
# posn = (in) The [m] coordinates.
# name_prefix = A prefix string for the variable name.
#
# Results:
# The function returns the variable name, as a Maple string.
#
data_var_name :=
proc(posn::list(numeric), name_prefix::string)
cat(name_prefix, sprint_numeric_list(posn));
end proc;