summaryrefslogtreecommitdiff
path: root/series.mac
blob: 581f88c594ea282ddb9dd46ea41ccb1033a27451 (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
/* Term generation */
exp_taylor(expr, n) := expand(subst([ξ=expr], taylor(exp(ξ), ξ, 0, n)));
neg_pow_series(c, r, a, b) := sum(c[i] / r^i, i, a, b);


/* Term filtering */

filter_order(expr, x, a, b) := block([acc, len],
  acc : 0,
  len : if atom(expr) or not(op(expr) = "+") then 1 else length(expr),

  for i thru len do block([deg, p],
    p : if len=1 then expr else part(expr, i),
    deg : hipow(partition([p], x)[2], x),
    if (deg >= a and deg <= b) then acc : acc + p),

  acc);

/* Speed up by expanding and mapping only once over the list (check operator is +) */
keep_upto_order(expr, var, start, end) := block([parts],
  expr : expand(expr),
  parts : makelist(coeff(expr, var, i) * r^i, i, start, end),
  apply("+", parts));


/* Term generation with filtering */

/*
Calculate the exponential series expansion evaluated at `expr' up to `n'th order
in `x'
*/
exp_series(expr, x, n, a, b) := block([acc, xn, len],
  expr : expand(expr),
  xn : 1,
  acc : 1,
  len : if atom(expr) or not(is(op(expr)="+")) then 1 else length(expr),
  /* Assume a, b < 0 for now */
  for i thru n do block([subexpr],
    /* Discard terms that would end up creating terms outside range */
    /*
    subexpr : 0,
    for j thru len do block([term, deg],
      term : if len=1 then expr else part(expr, j),
      deg : hipow(term, x),
      if not((deg + lopow(xn, x)) > b or (deg + hipow(xn, x)) < a)
      then
      subexpr : subexpr + term),
    */
    /**/
    xn : filter_order(ratexpand(xn * expr / i), x, a, b),
    acc : acc + xn),
  acc);

horner_exp_series(expr, x, n, a, b) := block([acc],
  /* expand(1 + x*(1 + x*(1 + x*(1+x/4)/3)/2)) */
  expr : expand(expr),
  acc : 1,
  for i thru n do acc : keep_upto_order(1 + expr*acc/(n-i+1), x, a, b),
  acc);


/* Multiply series expansions upto nth order */

matchdeclare(m_freeof_r, lambda([e], freeof(r, e)))$
matchdeclare(m_small, lambda([e], e < -expansion_order))$
defrule(r_simp_small, m_freeof_r*r^m_small, 0)$

__simple_filter(series, max_order) := block([ret, tmp],
  tmp : expansion_order,
  expansion_order : max_order,
  ret : apply1(series, r_simp_small),
  expansion_order : tmp,
  ret);

__table_size(factors) := block(
  [nfactors, ranges, t_min, t_max],
  nfactors : length(factors),
  ranges : make_array(any, nfactors, 2),
  /* Fill table with maximal and minimal powers */
  for i : 0 thru nfactors-1 do block(
    ranges[i, 0] : lopow(factors[i+1], r),
    ranges[i, 1] : hipow(factors[i+1], r)),
  /* Find greatest and smallest r powers */
  t_min : ranges[0, 0],
  t_max : ranges[0, 1],
  for i : 0 thru nfactors-1 do (
    t_min : min(t_min, ranges[i, 0]),
    t_max : max(t_max, ranges[i, 1])),
  [t_min, t_max]);

__fill_term_table(table, factors, t_min, t_max, len) := block(
  [nfactors],
  nfactors : length(factors),
  /* Split factors into term table */
  for i : 0 thru nfactors-1 do (
    /* Initialize table row with 0s */
    for j : 0 thru len-1 do table[i, j] : 0,
    /**/
    if not atom(factors[i+1]) and op(factors[i+1])="+" then (
      for term in args(factors[i+1]) do block(
        [j, n],
        n : lopow(term, r),
        j : n - t_min,
        table[i, j] : table[i, j] + coeff(term, r, n)))
    else block(
      [j, n, term],
      term : factors[i+1],
      n : lopow(term, r),
      j : n  - t_min,
      table[i, j] : coeff(term, r, n))));

__mul_series_level(table, max_order, i, stack, nfactors, len, t_min) := block(
  [],
  if is(i = nfactors) then block(
    [order, acc],
    order : 0,
    for j : 0 thru nfactors-1 do order : order + stack[j] + t_min,
    if order >= -max_order then (
      acc : 1,
      for j : 0 thru nfactors-1 do acc : acc * table[j, stack[j]],
      acc * r^order)
    else 0)
  else block([acc],
    /* TODO: omit external 0s */
    acc : 0,
    for j : 0 thru len-1 do (
      stack[i] : j,
      acc : acc + __mul_series_level(
        table, max_order, i+1, stack, nfactors, len, t_min)),
    acc));

/*
Multiply list of factors, each expressed as terms of series, only upto max_order
*/
mul_series(factors, max_order) := block(
  [term_table, len, t_min, t_max, stack, nfactors],
  nfactors : length(factors),

  /* If there is nothing to multiple, still filter higher terms */
  if is(nfactors=1) then (
    return(__simple_filter(factors[1], max_order))),

  /* Allocate stack and table for ranges */
  stack : make_array(any, nfactors),

  /* Compute table size */
  block([pair],
    pair : __table_size(factors),
    t_min : pair[1],
    t_max : pair[2]),
  len : abs(t_max - t_min) + 1,

  /* Allocate table for terms */
  term_table : make_array(any, nfactors, len),
  __fill_term_table(term_table, factors, t_min, t_max, len),

  __mul_series_level(term_table, max_order, 0, stack, nfactors, len, t_min))$


/**/

β_expr_val(x, n) := block(
  if x < 0 then (
    exp(x*H)*horner_exp_series(abs(x)*(H-rhs(main_ansatz[2])), r, n, -n, 2))
  else (
    exp(x*H)*horner_exp_series(x*(rhs(main_ansatz[2])-H), r, n, -n, 2)));

γ_expr_val(x, n) := horner_exp_series(x*rhs(main_ansatz[1]), r, n, -n, 2);

matchdeclare(m_any1, lambda ([e], freeof(β, γ, e)));
matchdeclare(m_any2, lambda ([e], freeof(β, γ, e)));
matchdeclare(m_any3, lambda ([e], freeof(β, γ, e)));
matchdeclare(m_beta, lambda ([e], not freeof(β, e) and freeof(γ, e)));
matchdeclare(m_gamma, lambda ([e], not freeof(γ, e) and freeof(β, e)));

/* Replace β and γ expressions into a symbolic representation */
defrule(r_wrap_exp_β, exp(m_any1*m_beta), β_expr(m_any1));
defrule(r_wrap_exp_γ, exp(m_any1*m_gamma), γ_expr(m_any1));
defrule(r_wrap_exp_βγ, exp(m_any1*m_beta + m_any2*m_gamma),
  β_expr(m_any1)*γ_expr(m_any2));
defrule(r_wrap_exp_βγ2, exp(m_any1*m_beta + m_any2*m_gamma + m_any3),
  exp(m_any3)*β_expr(m_any1)*γ_expr(m_any2));

/* Apply wrap rules to expression */
wrap_exp(expr) := (
  apply1(expr, r_wrap_exp_β, r_wrap_exp_γ, r_wrap_exp_βγ, r_wrap_exp_βγ2));

/* Replace symbolic representations of β and γ expressions into their their
expansions */
defrule(r_exp_β_val, β_expr(m_any1), β_expr_val(m_any1, expansion_order));
defrule(r_exp_γ_val, γ_expr(m_any1), γ_expr_val(m_any1, expansion_order));

unwrap_exp(expr) := apply1(expr, r_exp_β_val, r_exp_γ_val);


/**/

freeof_many(expr, funcs) := apply(freeof, append(funcs, [expr]));

/* Split product in factors containing any funcs and factors free of them */
split_prod(expr, funcs) := block(
  [deps_on_funcs : []],
  /* If not a product, assume product with single factor */
  for f in funcs do block(
    if atom(expr) then (
      if member(expr, funcs) then (
        push(expr, deps_on_funcs),
        expr : 1),
      return())
    elseif op(expr)="*" then block(
      [split : partition(expr, f)],
      expr : split[1],
      push(split[2], deps_on_funcs))
    elseif not freeof_many(expr, funcs) then (
      push(expr, deps_on_funcs),
      expr : 1,
      return())
    else (
      return())),
  [expr, apply("*", deps_on_funcs)]);

/* Substitute functions by their series expansions and apply substitutions in
coeffs */
__expand_term_subst(expr, coeffs) := block(
  expr : apply1(expr, r_exp_β_val, r_exp_γ_val),
  expr : subst(append(main_ansatz, coeffs), expr),
  ev(expr, eval, diff, expand));

/* Substitute product of functions by result of mul_series */
__expand_term_prod(expr, coeffs) := block(
  [free, factors],
  expr : wrap_exp(expr),
  /* Split product into list of factors containing funcs and those independent
  of funcs*/
  block([split : split_prod(expr, funcs)],
    free : split[1],
    if not atom(split[2]) and op(split[2])="*" then (
      factors : args(split[2]))
    else (
      factors : [split[2]])),
  /* Substitute funcs factors by expansion */
  factors : __expand_term_subst(factors, coeffs),
  /* Total product using series optimization */
  free * mul_series(factors, expansion_order + lopow(free, r)));

/*
Split product factors into list substitute series expansions, substitute
coefficients and finally multiply only upto expansion_order.
*/
expand_term(expr, coeffs) := (
  if atom(expr) then (
    apply1(__expand_term_subst(wrap_exp(expr), coeffs), r_simp_small))
  /* TODO: Recursive call instead of error */
  elseif op(expr) = "+" then (
    error("Term is itself a sum!"))
  /* For single negation just call recursively */
  elseif op(expr) = "-" then (
    if length(args(expr)) # 1 then (
      error("Unexpected '-' operator with more than 1 argument")),
    expand(- expand_term(part(expr, 1), coeffs)))
  elseif op(expr) = "*" or op(expr) = "^" then (
    expand(__expand_term_prod(expr, coeffs)))
  elseif op(expr) = "/" then block(
    [n : part(expr, 1), d : part(expr, 2), expansion_order : expansion_order],
    expansion_order : expansion_order - lopow(d, r),
    expand(expand_term(n, coeffs) / d))
  else (
    apply1(__expand_term_subst(wrap_exp(expr), coeffs), r_simp_small)));


/* Solve main equations term by term */

print_progress : false;

/* TODO: Divide and conquer on term list for long expansions */
/* coeffs: facts already known about series expansions */

/* Series expansion inside each instance of a value in the terms of an equation
*/
expand_equation(expr, coeffs) := (
  expr : rhs(expr) - lhs(expr),
  0 = if atom(expr) then (
    expr)
  elseif op(expr)="+" then block(
    [i : 0, len : length(args(expr))],
    apply("+", map(lambda([term],
          i : i+1,
          if print_progress then (
            printf(true, "expand_equation [~a/~a]~%", i, len)),
          expand_term(term, coeffs)),
        args(expr))))
  else (
    expand_term(expr, coeffs)))$


/* Misc */

/* For an equation of polynomials, returns the term by term list of equations of
the coefficients */
coeff_equations(eq) := block([start, end],
  eq : expand(eq),
  start : lopow(eq, r),
  end : hipow(eq, r),
  reverse(makelist([r^i, coeff(eq, r, i)], i, start, end)));