1 /* Author Barton Willis
2 University of Nebraska at Kearney
3 Copyright (C) 2008, 2009, 2010, 2013 Barton Willis
5 This program is free software: you can redistribute it or modify
6 it under the terms of the GNU General Public License version two.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 eval_when(translate,declare_translated(to_poly_solve,to_poly_solve_h, safer_subst,
17 merge_solutions, solution_subst,lambertify, catch_easy_cases, expunge_nonrelevant_cnds));
19 /* Conditionally load to_poly (version 2 or later), grobner, lrats, opsubst, unwind_protect,
20 and basic (push macro). */
22 if get('to_poly,'version) = false then load("to_poly");
23 if get('to_poly,'version) < 2 then error("You need to update 'to_poly' to version 2 or greater");
24 if not(?functionp('poly_reduced_grobner)) then load("grobner");
25 block([inflag : true], if not(member('fullratsubst, map('op, functions))) then load("lrats"));
26 block([inflag : true], if not(member('lratsubst, map('op, functions))) then load("lrats"));
27 if not(?functionp('opsubst)) then load("opsubst");
28 block([inflag : true], if not(member('push, map('op, macros))) then load("basic"));
29 if not(?functionp('simpfuncall)) then load("simplifying");
30 load("to_poly_solve_extra");
32 /* Return a lambda form that is the composition of the functions in the list l.
33 For example, compose_functions([f,g]) --> lambda([gxxxx], f(g(gxxxx))). When
34 a list member isn't a function, generally funmake will signal an error. */
36 compose_functions(l) := block([z : new_variable('general), f],
40 for lk in l do f : funmake(lk, [f]),
41 buildq([z,f], lambda([z], f)))
42 else error("The argument to 'compose_functions' must be a list."));
44 /* The function dfloat is similar to float, but dfloat applies rectform if dfloat fails
45 to produce a complex double float result. Because float is both a function and an option
46 variable (default value is false), it's easier to include dfloat than float in a
49 dfloat(e) := block([ef : float(e)], if complex_number_p(ef) then ef else float(rectform(e)));
51 /* Return true if the argument is either a symbol (but not a system constant such as %pi), a
52 subscripted variable, or a conjugated variable.*/
54 variable_p(e) := block([inflag : true],
55 (symbolp(e) and not(featurep(e,'constant))) or
57 opequal_p(e,'conjugate) and variable_p(first(e)));
59 /* Apply %and to the list or set l. Signal an error when the argument isn't a list or set.*/
61 simp_and(l) := if listp(l) or setp(l) then xreduce("%and",l, true)
62 else error("The argument to 'simp_and' must be a list");
64 /* Return true if the operator of e is a member of f; otherwise return false. */
66 opequal_p(e,[f]) := block([inflag : true], not(mapatom(e)) and member(op(e),f));
68 /* Return a set of nonconstant variables that appear explicitly in the expression e. The variable x
69 and conjugate(x) are distinct variables.*/
71 set_of_variables(e) := block([inflag : true],
72 if variable_p(e) then set(e)
73 else if mapatom(e) then set()
74 else xreduce('union, (map('set_of_variables, args(e))), set()));
76 singular_solutions(eq, v, p, f, maxdepth) := block([cnd, free_vars, sol, vs, ps, acc : %union()],
77 cnd : last(last(triangularize(jacobian(eq,v)))),
78 cnd : first(elim_allbut(cons(cnd,eq),p)),
79 sol : to_poly_solve(append(cnd,eq),append(v,p), 'simpfuncs = f, 'maxdepth = maxdepth),
81 vs : map("=", v, subst(sk, v)),
82 ps : map("=", p, subst(sk, p)),
84 acc : %union(%if(ps, vs, %union()), acc)),
86 cnd : simp_and(map(lambda([s], s # 0),cnd)),
88 vs : map("=", v, subst(sk, v)),
89 acc : %union(%if(cnd, vs, %union()), acc)),
92 /* Return true if the operator of e is "="; otherwise return false. */
93 equation_p(e) := block([inflag : true], not(mapatom(e)) and is(op(e) = "="));
95 /* Return true if the operator of e is %if; otherwise return false. */
96 if_p(e) := block([inflag : true], not(mapatom(e)) and is(op(e) = '%if));
98 /* Return true if the operator of e is %union; otherwise return false. */
99 %union_p(e) := block([inflag : true], not(mapatom(e)) and is(op(e) = '%union));
101 solution_subst(sol, v, f) := (
102 if is(sol = %union()) then sol
103 else if if_p(sol) then %if(first(sol), solution_subst(second(sol),v,f), solution_subst(third(sol),v,f))
104 else v = apply(f, [subst(sol,v)]));
106 /* Substitute l into e. When l or e is a conditional, do the substitution in a way that
107 doesn't unnecessarily signal an error. Also, for substitutions such as safer_subst([x=y,y=z],x)
108 keep doing the substitution until the output stops changing. This can lead to an infinite loop,
109 but the solver should never give safer_subst such an infinite loop... */
111 safer_subst(l, e) := block([algebraic : true, r, cnd, e1, e2],
112 if listp(e) or equation_p(e) or %union_p(e) then map(lambda([s], safer_subst(l,s)), e)
113 else if (e = %union()) or (e = true) or (e = false) or (l = %union()) then e
114 else if if_p(l) then (
115 cnd : simp_inequality(first(l)),
116 e1 : safer_subst(second(l),e),
117 e2 : safer_subst(third(l),e),
118 %union(%if(expunge_nonrelevant_cnds(cnd, e1), e1, %union()),
119 %if(expunge_nonrelevant_cnds(cnd, e2), e2, %union())))
120 else if if_p(e) then (
121 /* When e is a conditional, substitute and simplify the predicate before substituting into the rest of e.*/
122 cnd : simp_inequality(safer_subst(l, first(e))),
123 if is(cnd = false) then safer_subst(l, third(e))
124 else if is(cnd = true) then safer_subst(l, second(e))
125 else %if(cnd, safer_subst(l, second(e)), safer_subst(l, third(e))))
127 r : errcatch(subst(l,e)),
128 while (r # [ ] and e # first(r)) do ( /* iterate to a fixed point */
130 r : errcatch(subst(l,e))),
131 if emptyp(r) then error("Unable to substitute") else first(r)));
133 list_conjugate_vars(e) := block([ ],
134 listify(setify(sublist(xreduce('append, gatherargs(e, 'conjugate)), lambda([s], mapatom(s))))));
136 /* Let e be the output of to_poly_solve that involves the solution for some internal variables
137 and let v be the list of user variables. The function expunge_nonrelevant_solutions deletes
138 the solutions for the internal variables. */
140 expunge_nonrelevant_solutions(e,v) :=
142 else if op(e) = '%union or op(e) = '%if then (
143 map(lambda([s], expunge_nonrelevant_solutions(s,v)), e))
144 else if listp(e) then (
145 sublist(e, lambda([s], member(lhs(s),v))))
148 /* Solve equations that involve both x and conjugate(x). This is not a user-level function.
149 e = list of equations to be solved,
150 v = list of solution variables,
151 vc = list of solution variables that appear in the equations as conjugated.
153 The input vc is unused. */
155 conjugate_solver(esave,v,vc,funs,maxdepth) := block([e, ee, sub, nv, rv, sol, cnd, acc, sx1, sx2, cnd1, cnd2],
156 e : setify(append(esave,conjugate(esave))),
157 vc : list_conjugate_vars(e),
158 sub : map(lambda([s], conjugate(s) = new_variable('complex)), vc),
160 e : listify(subst(sub, e)),
162 sol : to_poly_solve(e, nv, 'maxdepth = maxdepth, 'simpfuncs = funs, 'field = 'complex),
163 if not mapatom(sol) and op(sol) = '%union then (
164 sub : map(lambda([s], rhs(s)-lhs(s)=0),sub),
171 cnd1 : safer_subst(sx1,sub),
172 cnd2 : safer_subst(sx2,sub),
173 cnd1 : simp_and(cnd1),
174 cnd2 : simp_and(cnd2),
175 acc : %union(acc, %if(cnd,
176 %if(cnd1, expunge_nonrelevant_solutions(sx1,v),%union()),
177 %if(cnd2, expunge_nonrelevant_solutions(sx2,v),%union()))))
181 acc : %union(acc, (%if(cnd, expunge_nonrelevant_solutions(sx,v),%union()))))),
183 else simpfunmake('%solve,[esave,v, 'maxdepth = maxdepth, 'simpfuncs = funs]));
186 /* Convert log(constant) --> rectform(log(constant)). */
187 rectform_log_if_constant(e) :=
188 subst(lambda([s], if constantp(s) then rectform(log(s)) else log(s)), 'log, e);
190 /* Convert realpart and imagpart to conjugates. The calls to nounify are required by
191 noun / verb bugs, I think. */
193 real_imagpart_to_conjugate(e) :=
194 subst([nounify('realpart) = lambda([s], (s + conjugate(s))/2),
195 nounify('imagpart) = lambda([s], (s - conjugate(s))/(2 * %i))], e);
197 merge_solutions(sol1, sol2) := block([cnd1, cnd2, a1, a2, b1, b2],
198 if is(sol1 = %union()) then sol2
199 else if is(sol2 = %union()) then sol1
201 [cnd1, a1, b1] : if listp(sol1) then [true, sol1, %union()] else args(sol1),
202 [cnd2, a2, b2] : if listp(sol2) then [true, sol2, %union()] else args(sol2),
203 %if(simp_and([cnd1,cnd2]), append(a1,a2), merge_solutions(b1,b2))));
205 non_symbol_subst (e, v) := block([vars, listconstvars : false],
206 vars : makelist(lambda([s], if variable_p(s) then s = s else s = new_variable('general))(k), k, v),
207 e : fullratsubst(vars, e),
208 [e, map('reverse, vars)]);
210 (one_to_one_reduce[a,b] := false,
211 one_to_one_reduce['sin,'sin] : lambda([a,b],
212 (a - b - 2 * %pi * new_variable('integer)) * (a + b - 2 * %pi * new_variable('integer) + %pi)),
214 one_to_one_reduce['cos,'cos] : lambda([a,b],
215 (a - b - 2 * %pi * new_variable('integer)) * (a + b - 2 * %pi * new_variable('integer))),
217 one_to_one_reduce['sin,'cos] : lambda([a,b],
218 (a/2 - b/2 - %pi/4 - %pi * new_variable('integer))*(a/2 + b/2 - %pi/4 - %pi * new_variable('integer))),
220 one_to_one_reduce['cos,'sin] : lambda([a,b],
221 (b/2 - a/2 - %pi * new_variable('integer) - %pi/4)*(b/2 + a/2 - %pi * new_variable('integer) - %pi/4)),
223 one_to_one_reduce['tan,'tan] : lambda([a,b], (a-b - %pi*new_variable('integer))/(cos(a)*cos(b))),
225 one_to_one_reduce['%exp,'%exp] : lambda([a,b],
226 a - b - 2 * %pi * %i * new_variable('integer)));
228 one_to_one_crunch(e) := block([inflag : true, f, nv : set(), %exp, q, opl, opr],
229 e : subst("^" = lambda([a,b], if a = %e then funmake('%exp, [b]) else a^b),e),
230 e : if opequal_p(e,"+") then first(e) = -rest(e) else e,
231 if opequal_p(e, "=") then (
232 e : block([inflag : false], if not mapatom(lhs(e)) and op(lhs(e)) = "-" then -e else e),
233 opl : if mapatom(lhs(e)) then false else op(lhs(e)),
234 opr : if mapatom(rhs(e)) then false else op(rhs(e)),
235 f : one_to_one_reduce[opl, opr],
237 nv : setify(listofvars(e)),
238 q : one_to_one_crunch(apply(f, [first(lhs(e)),first(rhs(e))])),
240 nv : union(second(q), setdifference(setify(listofvars(e)), nv)))
242 e : lhs(e) - rhs(e))),
243 e : subst(%exp = lambda([a], %e^a),e),
247 (function_inverse[otherwise]:= false,
248 function_inverse['acos] : lambda([s], cos(s)),
249 function_inverse['acosh] : lambda([s], cosh(s)),
250 function_inverse['asin] : lambda([s], sin(s)),
251 function_inverse['asinh] : lambda([s], sinh(s)),
252 function_inverse['atan] : lambda([s], tan(s)),
253 function_inverse['atanh] : lambda([s], tanh(s)),
254 function_inverse['log] : lambda([s], exp(s)));
256 (function_zeros[otherwise] := lambda([], false),
257 function_zeros['abs] : lambda([],0),
258 function_zeros['conjugate] : lambda([],0),
259 function_zeros['sin] : lambda([], %pi*new_variable('integer)),
260 function_zeros['cos] : lambda([], %pi*(2*new_variable('integer)+1)/2),
261 function_zeros['log] : lambda([],1),
262 function_zeros['acos] : lambda([],1),
263 function_zeros['acosh] : lambda([],1),
264 function_zeros['asin] : lambda([],0),
265 function_zeros['asinh] : lambda([],0),
266 function_zeros['atan] : lambda([], 0),
267 function_zeros['atanh] : lambda([],0),
268 function_zeros['acsc] : lambda([], []),
269 function_zeros['acsch] : lambda([], []),
270 function_zeros['acoth] : lambda([],[]),
271 function_zeros['acot] : lambda([],[]));
273 (function_domain[otherwise] := lambda([s],set()),
274 function_domain['log] : lambda([s], set(s # 0)));
276 top_level_ops(e) := block([inflag : false],
277 if mapatom(e) then [ ]
278 else if opequal_p(e,"+","*") then (
279 xreduce('append, map('top_level_ops, args(e))))
282 catch_easy_cases (e, vars) := block([inflag : true, ops, finv, fzero, p, q, a, b,pk,pk_save,
283 ok : true, cnds : set(), nv : set(), listconstvars : false],
284 if opequal_p(e,"*") then (
285 e : map(lambda([s], catch_easy_cases(s,vars)), args(e)),
286 [xreduce("*", map('first, e)), xreduce('union,map('second,e)), xreduce('union,map('third,e))])
288 ops : top_level_ops(e),
290 finv : function_inverse[f],
291 fzero : function_zeros[f],
292 fzero : apply(fzero,[]),
293 nv : union(nv, setify(listofvars(fzero))),
294 if (finv # false) or (fzero # false) then (
296 p : sublist(p, lambda([s], not lfreeof(vars,s))),
297 for pk in p while ok do (
302 if is(equal(b,0)) = true and lfreeof(vars,a) and fzero # false then (
305 if fzero = [] then [1, set(a # 0)]
307 q : catch_easy_cases(first(pk)-fzero, vars),
309 nv : union(nv, third(q)),
310 cnds : union(cnds, second(q), apply(function_domain[f],pk_save)),
311 cnds : adjoin(a # 0, cnds)))
312 else if lfreeof(vars, a) and lfreeof(vars, b) and is(finv # false) then (
315 q : catch_easy_cases(first(args(pk))-apply(finv, [-b/a]),vars),
316 nv : union(nv, setdifference(setify(listofvars(q)), setify(listofvars(pk)))),
318 cnds : adjoin(a # 0, second(q)))))),
321 standardize_inverse_trig(e) :=
322 e : subst(['acot = lambda([s], atan(1/s)),
323 'acsc = lambda([s], asin(1/s)),
324 'asec = lambda([s], acos(1/s)),
325 'acoth = lambda([s], atanh(1/s)),
326 'acsch = lambda([s], asinh(1/s)),
327 'asech = lambda([s], acosh(1/s))], e);
329 /* Lambert Solver -- replace terms of the form w * exp(w) by a %Cxx variable and
330 push %Cxx = lambert_w(w) into subs. Return both e and the list of subs.*/
332 lambertify(e,vars) := block([ee, eee, expargs : set(), cexpargs, nexpargs,
333 %g : new_variable('complex), logargs : set(),
334 unmatched : true, subs : set()],
336 ee : expand(lhs(e)-rhs(e),0,0),
337 expargs : setify(gatherargs(ee, "^")),
338 cexpargs : subset(expargs, lambda([s], lfreeof(vars,first(s)))),
339 /* nexpargs : subset(expargs, lambda([s], is(equal(first(s),second(s))))), */
340 nexpargs : subset(expargs, lambda([s], is(first(s) = second(s)))),
341 logargs : setify(gatherargs(ee,'log)),
343 /* look for matches x * c^x = %g, log(c) * x = lambert_w(%g), where c is constant. */
345 for qk in cexpargs while unmatched do (
346 eee : ratsubst(%g / second(qk), first(qk)^second(qk), ee),
348 if eee # %g and polynomialp(eee,[%g], lambda([s], lfreeof(vars,s))) then (
351 subs : set(lambert_w(%g) = log(first(qk)) * second(qk)))),
353 /* look for matches x^x = %g, x = log(%g) / lambert_w(log(%g)) */
354 for qk in nexpargs while unmatched do (
355 eee : ratsubst(%g, first(qk)^second(qk), ee),
356 if polynomialp(eee,[%g], lambda([s], lfreeof(vars,s))) then (
359 subs : set(log(%g) / lambert_w(log(%g)) = first(qk)))),
361 /* look for matches x * c^(-x) = %g, log(c) * x = lambert_w(-%g), where c is a constant. */
362 for qk in cexpargs while unmatched do (
363 eee : ratsubst(second(qk) / %g, first(qk)^second(qk), ee),
364 eee : ratnumer(eee / second(qk)),
365 if eee # %g and polynomialp(eee,[%g], lambda([s], lfreeof(vars,s))) then (
368 subs : set(-lambert_w(-%g) = log(first(qk)) * second(qk)))),
370 /* look for matches x + log(x) = %g, x = lambert_w(exp(%g)) */
371 for qk in logargs while unmatched do (
373 eee : ratsubst(%g, log(qk) + qk, ee),
375 if eee # %g and polynomialp(eee,[%g], lambda([s], lfreeof(vars,s))) then (
378 subs : set(lambert_w(exp(%g)) = qk))),
380 /* look for matches x * log(x) = %g, x = %g / lambert_w(%g) */
381 for qk in logargs while unmatched do (
383 eee : ratsubst(%g, qk * log(qk), ee),
385 if polynomialp(eee,[%g], lambda([s], lfreeof(vars,s))) then (
388 subs : set(%g / lambert_w(%g) = qk))),
392 alias(%solve, to_poly_solve);
394 simp_%solve(e,v,[extraargs]) := block([vc, all_vars, sol, solvedomain, parameter_list,
395 algexact : assoc('algexact,extraargs,true),
396 alias_solver : false, use_grobner, maxdepth, cntx : false, simpfuncs, ratprint : false],
397 simpfuncs : assoc('simpfuncs, extraargs, []),
398 if not(member('rectform_log_if_constant, simpfuncs)) then
399 simpfuncs : append(simpfuncs,['real_imagpart_to_conjugate,'rectform_log_if_constant]),
400 solvedomain : assoc('field, extraargs, 'complex),
401 use_grobner : assoc('use_grobner, extraargs, false),
402 parameter_list : assoc('parameters, extraargs, false),
403 maxdepth : assoc('maxdepth, extraargs, 5),
404 if is(maxdepth < 0) then (
405 error("Unable to solve"))
406 else maxdepth : maxdepth - 1,
408 /* Convert e and v into sets */
409 [e,v] : map(lambda([s], if listp(s) then setify(s) else if setp(s) then s else set(s)), [e,v]),
411 /* Check that each member of v is a variable. */
412 alias_solver : not every('variable_p, v),
414 /* Decide if we need to dispatch the conjugate_solver. Convert real/imag part to conjugate.*/
416 e : real_imagpart_to_conjugate(e),
417 vc : subset(set_of_variables(e), lambda([s], opequal_p(s,'conjugate))),
418 v : setdifference(v,vc),
420 /* Listify e, v, and vc.*/
421 [e,v,vc] : map('listify,[e,v, vc]),
423 /* Special case dispatcher.*/
425 if some(lambda([s], if_p(s)), e) then (
426 error("Not yet implemented!"))
428 else if some(lambda([s], not(mapatom(s)) and opequal_p(s, "<","<=", "#", ">", ">=")), e) then (
429 sol : fourier_elim(e, v),
430 if opequal_p(sol, "or") then subst("or" = '%union, sol)
431 else if sol = 'emptyset then %union()
432 else if sol = 'universalset then (
433 %union(xreduce('append, makelist([s = new_variable('real)], s, v))))
436 else if alias_solver then (
437 block([ee, nonvar_sub, sol],
438 ee : non_symbol_subst(e,v),
439 [ee, nonvar_sub] : ee,
440 sol : apply('%solve, append([ee, map('lhs, nonvar_sub)], extraargs)),
441 if opequal_p(sol,'%solve) then sol else
442 safer_subst(reverse(nonvar_sub), %solve(ee, map('lhs, nonvar_sub), 'maxdepth = maxdepth))))
444 else if every(lambda([s], polynomialp(rhs(s)-lhs(s), v, lambda([w], lfreeof(v,w)))),e) then (
445 block([freevars : setify(%rnum_list), simpfuncs_lambda : compose_functions(simpfuncs)],
446 if use_grobner then (
447 e : map(lambda([s], rhs(s)-lhs(s)), e),
448 e : poly_reduced_grobner(e,listofvars(e))),
449 if listp(parameter_list) then (
450 singular_solutions(e,v,parameter_list, simpfuncs, maxdepth))
452 sol : errcatch(algsys(e, v)),
453 if emptyp(sol) then (
454 simpfunmake('%solve, cons(e,cons(v,extraargs))))
457 freevars : setdifference(setify(%rnum_list), freevars),
458 for vk in freevars do (
459 sol : subst(vk = new_variable(solvedomain), sol)),
460 sol : map(lambda([s], map("=", map('lhs, s), map(simpfuncs_lambda, map('rhs, s)))), sol),
461 xreduce('%union, sol, %union())))))
463 else if not(emptyp(vc)) then (
464 conjugate_solver(e,v,vc,simpfuncs, maxdepth))
467 sol : errcatch(to_poly_solve_h(e,v, simpfuncs, maxdepth, use_grobner,algexact)),
468 if emptyp(sol) then simpfunmake('%solve, cons(e,(cons(v,extraargs)))) else first(sol)));
470 simplifying('%solve, 'simp_%solve);
472 /* Try to solve the equation(s) e for the the variable(s) v. Both 'e' and 'v' can be
473 a single Maxima expression, a list, or a set. Members of 'e' that aren't equations
474 are (not of the form a = b) assumed to vanish. Each solution gets simplified by
475 the functions in 'simpfuncs.' If simpfuncs contains non-identity transformations,
476 well, it's not my fault.
478 Users should call to_poly_solve, not to_poly_solve_h.
481 to_poly_solve_h(e,v, simpfuncs, maxdepth, grob,use_algexact) := block([eqs : set(), nonalg : set(), listconstvars : false,
482 %gvars, evars, sol, vars, freevars, eqs_vars, non_free, true_sol, %ivar, esave, %sol_vars, %cnds,
483 xsol, nsol, inflag : true, cnds : set(), nonfree, eq, lambert_subs, var_exception, simpfuncs_lambda,
486 /* save a list of the variables in e */
487 evars : set_of_variables(e),
489 /* Convert e and v into sets */
491 e : if listp(e) then setify(e) else if setp(e) then e else set(e),
492 v : if listp(v) then setify(v) else if setp(v) then v else set(v),
495 /* Convert each member of e to polynomial form. The function to_poly extracts numerators and demands
496 that the denominators be nonvanishing.*/
500 [ek, var_exception] : one_to_one_crunch(ek),
501 ek : standardize_inverse_trig(ek),
502 ek : catch_easy_cases(lhs(ek) - rhs(ek),v),
503 var_exception : union(third(ek), var_exception),
504 cnds : union(cnds, second(ek)),
506 ek : lhs(ek) - rhs(ek),
507 ek : trigexpand(logcontract(rationalize(ek))),
508 ek : subst(lambda([a,b], if numberp(a) and not(numberp(b)) then exp(log(a)*b) else a^b), "^", ek),
509 [ek, lambert_subs] : lambertify(ek, v),
510 nonalg : union(nonalg, lambert_subs),
511 ek : errcatch(to_poly(ek,v)),
512 if emptyp(ek) then error("unable to solve") else ek : first(ek),
516 /* Gather equations, conditions, and non-algebraic equations into lists eqs, cnds, and nonalg,
520 eqs : union(eqs, setify(first(ek))),
521 cnds : union(cnds, setify(second(ek))),
522 nonalg : union(nonalg, setify(third(ek)))),
524 /* convert cnds into a boolean expression */
525 cnds : simp_and(cnds),
527 /* nonfree is the union of v (the set of variables to solve for) and the
528 set of variables introduced by to_poly. The set nonfree is the new set
529 of variables that we need to solve for.
532 nonfree : union(setify(v), setdifference(set_of_variables(eqs), evars)),
533 nonfree : setdifference(nonfree, var_exception),
534 nonfree : subset(nonfree, lambda([s], not featurep(s,'integer))),
535 esave : union(set_of_variables(eqs), nonfree),
536 sol : to_poly_solve(eqs, nonfree, 'simpfuncs = simpfuncs, 'maxdepth = maxdepth-1, 'use_grobner = grob, 'algexact=use_algexact),
537 if opequal_p(sol, '%solve) then error("Unable to solve"),
539 /* The solve process introduces new variables. Let %sol_vars be the set
540 of variables introduced by to_poly_solve */
542 %sol_vars : setdifference(set_of_variables(sol), union(esave, var_exception)),
544 /* Update the set of variables we need to solve for */
545 nonfree : union(setify(v), %sol_vars),
547 /* Each member of nonalg looks like %g = exp(blob), %g = log(blob), %g = variable^blob, or
548 inverse_lambert_w(blob) = atom. Convert
549 %g = exp(blob) --> log(%g) + 2 * %i * %pi * %ivar = blob,
550 %g = x^blob --> x = %g^(1/blob),
551 %g = log(blob) --> exp(%g) = blob.
552 where %ivar eventually is replaced by an integer gentemp. */
554 nonalg : map(lambda([s],
555 if opequal_p(rhs(s),"^") and first(rhs(s)) = '%e then (
556 log(lhs(s)) + 2 * %i * %pi * '%ivar - second(rhs(s)))
557 else if opequal_p(rhs(s),"^") and lfreeof(v, second(rhs(s))) then (
558 first(rhs(s)) = lhs(s)^(1/second(rhs(s))) * exp(2 * '%pi * '%i * '%ivar / second(rhs(s))))
559 else if opequal_p(rhs(s),'log) then (
560 exp(lhs(s)) - first(args(rhs(s))))
564 simpfuncs_lambda : compose_functions(simpfuncs),
566 /* Paste each solution into nonalg. The substitution can yield log(0), so use errcatch. */
569 sk : real_imagpart_to_conjugate(sk),
570 /* bug when sk isn't a list! */
572 if emptyp(nonalg) then (
575 eq : errcatch(subst(sk, subst('%ivar = new_variable('integer),nonalg))),
576 eq : if emptyp(eq) then [1=0] else first(eq)),
578 xsol : to_poly_solve(eq, intersection(nonfree, set_of_variables(eq)),
579 'simpfuncs = simpfuncs, 'maxdepth = maxdepth-1, 'use_grobner = grob),
581 if opequal_p(xsol, '%solve) then error("Unable to solve"),
583 xk : real_imagpart_to_conjugate(xk),
584 xk : merge_solutions(sk,xk),
585 nsol : map(lambda([s], solution_subst(xk, s, simpfuncs_lambda)), v),
586 if listp(nsol) and if_p(first(nsol)) then nsol : first(nsol),
587 if if_p(nsol) and not(listp(second(nsol))) then nsol : %if(first(nsol),
588 [second(nsol)], third(nsol)),
589 nsol_vars : set_of_variables(nsol),
590 %cnds : safer_subst(xk, cnds),
591 %cnds : expunge_nonrelevant_cnds(%cnds, nsol),
592 nsol : %if(%cnds, nsol, %union()),
593 true_sol : adjoin(nsol, true_sol))),
594 xreduce('%union, true_sol, %union()));
596 /* Maybe all this should be fixed higher up; for now ...*/
598 expunge_nonrelevant_cnds (cnds, sol) := block([listconstvars : false, sol_vars : listofvars(sol)],
599 if cnds = true or cnds = false then cnds
600 else if disjunction_p(cnds) or conjunction_p(cnds) then (
601 map(lambda([s], expunge_nonrelevant_cnds(s, sol)), cnds))
602 else if lfreeof(sol_vars, cnds) and not constantp(cnds) then true