2 /******************************************************************************/
4 /* SOLVER - THE NEXT GENERATION */
6 /* Copyright (C) 2000 : Eckhard Hennig, Ralf Sommer */
7 /* This library is free software; you can redistribute it and/or modify it */
8 /* under the terms of the GNU Library General Public License as published */
9 /* by the Free Software Foundation; either version 2 of the License, or (at */
10 /* your option) any later version. */
12 /* This library is distributed in the hope that it will be useful, but */
13 /* WITHOUT any WARRANTY; without even the implied warranty of */
14 /* MERCHANTABILITY or FITNESS for A PARTICULAR PURPOSE. See the GNU */
15 /* Library General Public License for more details. */
17 /* You should have received a copy of the GNU Library General Public */
18 /* License along with this library; if not, write to the Free Software */
19 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
20 /******************************************************************************/
21 /* Credits: This program is based on many ideas from Henning Trispel's work */
22 /* on the EASY-Solver in 1991. */
24 /******************************************************************************/
25 /* Author(s) : Eckhard Hennig, Ralf Sommer */
26 /* Project start: 19.01.1994 */
27 /* Completed : 16.07.1994 */
28 /* last change : 29.06.1995 */
30 /******************************************************************************/
31 /* Changes : ||||| ||||| ||||| ||| */
32 /******************************************************************************/
33 /* Modified by : Dan Stanger dan.stanger@ieee.org to work under maxima */
34 /******************************************************************************/
35 load( "solver/linsolve.mac" )$
36 load( "solver/slvrtbox.mac" )$
37 load( "solver/slvrmsgs.mac" )$
38 load( "solver/misc.mac" )$
40 put( 'SOLVER, 1, 'Version )$
45 'DESCRIPTION = "Symbolic solver for parametric systems of equations.",
46 'AUTHORS = "Eckhard Hennig, Ralf Sommer",
48 'LASTCHANGE = "29.06.1995",
50 'PLAN = "Add a-priori transforms"
54 /******************************************************************************/
55 /* last change: 29.06.1995 */
57 /* By : Eckhard Hennig */
58 /* Description: Option variable Solver_Valuate_All_Nonlin_Vars added. */
59 /* Bug in ValuationSolver removed: rhs variables of solutions */
60 /* were not added to the list of variables. */
61 /******************************************************************************/
62 /* last change: 28.05.1995 */
64 /* By : Eckhard Hennig */
65 /* Description: Solver break test added. */
66 /******************************************************************************/
67 /* last change: 18.05.1995 */
69 /* By : Eckhard Hennig */
70 /* Description: Name conflict with AI keyword PARAMS removed. */
71 /******************************************************************************/
72 /* last change: 30.01.1995 */
74 /* By : Eckhard Hennig */
75 /* Description: Removal of map( 'num, ... ) has revealed some side effects on */
76 /* the list of equations in ValuationSolver. Bug repaired by */
77 /* inserting two additional copylist calls. */
78 /* Arguments to both load commands above now written in lower- */
79 /* case letters to avoid problems with UNIX versions. */
80 /******************************************************************************/
81 /* last change: 24.01.1995 */
83 /* By : Eckhard Hennig */
84 /* Description: mapping 'num to expressions caused some invalid solutions, */
85 /* therefore removed. Version property added. */
86 /******************************************************************************/
87 /* last change: 19.01.1995 */
89 /* By : Eckhard Hennig, Ralf Sommer */
90 /* Description: Bug in ImmediateAssignments corrected, Function AppendImmed */
92 /******************************************************************************/
93 /* last change: 19.01.1995 */
95 /* By : Eckhard Hennig, Ralf Sommer */
96 /* Description: SLVRTBOX and SLVRMSGS now autoloading. */
97 /******************************************************************************/
98 /* last change: 17.01.1995 */
100 /* By : Eckhard Hennig, Ralf Sommer */
101 /* Description: Option variables modified (underscores inserted according to */
102 /* R. Petti's suggestions) */
103 /* Function SetValuation added. */
104 /******************************************************************************/
105 /* last change: 25.10.1994 */
107 /* By : Eckhard Hennig */
108 /* Description: errorMsg renamed to ErrMsg. */
109 /******************************************************************************/
110 /* last change: 12.09.1994 */
112 /* By : Eckhard Hennig */
113 /* Description: errcatch wrapped around final fullratsimp. */
114 /******************************************************************************/
115 /* last change: 05.09.1994 */
117 /* By : Eckhard Hennig */
118 /* Description: mode_identity's inserted. */
119 /******************************************************************************/
120 /* last change: 05.09.1994 */
122 /* By : Eckhard Hennig */
123 /* Description: Number of linear equations is now correctly displayed. */
124 /* Bug in DumpToFile corrected. */
125 /* Numbers of solution sets are now printed by the postprocessor.*/
126 /******************************************************************************/
127 /* last change: 30.08.1994 */
129 /* By : Eckhard Hennig */
130 /* Description: Single inconsistent solution paths are no longer returned. */
131 /******************************************************************************/
132 /* last change: 29.08.1994 */
134 /* By : Eckhard Hennig */
135 /* Description: Slight modification of Immediate Assignment Solver. */
136 /* Valuation property for sqrt added. */
137 /* New option variable SolverDefaultValuation. */
138 /* Change in Valuation. Now making use of the above option var. */
139 /******************************************************************************/
140 /* last change: 26.08.1994 */
142 /* By : Eckhard Hennig */
143 /* Description: Bug in Linear Solver removed. */
144 /******************************************************************************/
145 /* last change: 25.08.1994 */
147 /* By : Eckhard Hennig */
148 /* Description: New option variable SolverRatSimpSols. */
149 /* Linear Solver now calls the consistency check. */
150 /* Capabilities of the transforms in ValuationSolver strongly */
152 /******************************************************************************/
153 /* last change: 24.08.1994 */
155 /* By : Eckhard Hennig */
156 /* Description: Bug fixed in postprocessor: compound expressions are now cor- */
157 /* rectly evaluated. */
158 /* Linear Solver now checks for remaining equations & variables, */
159 /* and removes those "linear" eqs and vars which do not really */
160 /* belong to the "true" linear subsystem. */
161 /******************************************************************************/
162 /* last change: 11.08.1994 */
164 /* By : Eckhard Hennig */
165 /* Description: Variable SolverDelEq2VarPref replaced by function variable */
166 /* SolverDelEq. Heuristic algorithm for linear equation */
167 /* extraction improved. */
168 /******************************************************************************/
169 /* last change: 19.07.1994 */
171 /* By : Eckhard Hennig */
172 /* Description: Bug in post processor corrected. */
173 /******************************************************************************/
176 /******************************************************************************/
177 /* Global variables for Solver */
178 /******************************************************************************/
180 /******************************************************************************/
181 /* If Solver_Immed_Assign is true then the Solver searches the equations */
182 /* for immediate assignments of the form variable = constant and immediately */
183 /* inserts these constraints into the remaining equations. */
184 /******************************************************************************/
186 define_variable( Solver_Immed_Assign, true, boolean )$
189 /******************************************************************************/
190 /* Solver_Repeat_Immed controls whether the search for immediate assignments */
191 /* is performed repeatedly until no more of them are found. */
192 /******************************************************************************/
194 define_variable( Solver_Repeat_Immed, true, boolean )$
197 /******************************************************************************/
198 /* Solver_Subst_Powers controls whether the Solver substitutes powers of a */
199 /* variable by new symbols in one of the following cases: */
200 /* 1. var^n appears raised to exactly one power n */
201 /* 2. for all var^m in the equations : m=n*k , k integer */
202 /******************************************************************************/
204 define_variable( Solver_Subst_Powers, false, boolean )$
207 /******************************************************************************/
208 /* Solver_Incons_Params controls whether the Solver terminates when a non- */
209 /* trivial equation containing only parameters is encountered. for example, */
210 /* if A and B are defined as parameters and the solution process yields */
211 /* A = B^2 then the Solver stops if Solver_Incons_Params is = 'BREAK. If set */
212 /* to 'ASK the Solver asks whether A - B^2 is zero and continues if it is. */
213 /* If set to 'IGNORE the Solver quietly assumes that the expression is zero */
214 /* if it does not directly contradict with any of the assumptions made before.*/
215 /******************************************************************************/
217 define_variable( Solver_Incons_Params, 'ASK, any_check )$
220 'Solver_Incons_Params,
222 if not member( x, [ 'ASK, 'BREAK, 'IGNORE ] ) then
223 ErrorHandler("InvIncPar", x, 'Fatal )
229 /******************************************************************************/
230 /* Solver_Linear controls whether the Solver tries to find and solve blocks */
231 /* of linear equations before submitting the remaining equations to the */
232 /* heuristic valuation solver. This is useful if the equations to be solved */
233 /* are known to contain a large linear part. */
234 /******************************************************************************/
236 define_variable( Solver_Linear, true, boolean )$
239 /******************************************************************************/
240 /* If Solver_Repeat_Linear is true then the LinearSolver will be called */
241 /* repeatedly until no more linear equations are found. If false then */
242 /* LinearSolver will be called only once. */
243 /******************************************************************************/
245 define_variable( Solver_Repeat_Linear, true, boolean )$
248 /******************************************************************************/
249 /* If Solver_Find_All_Linear_Vars is true then LinearSolver will try to find */
250 /* linear equations with respect to all available variables and solve these */
251 /* equations simultaneously. If false then LinearSolver will only search for */
252 /* linear equations with respect to the variables passed over in the function */
254 /******************************************************************************/
256 define_variable( Solver_Find_All_Linear_Vars, true, boolean )$
259 /******************************************************************************/
260 /* Solver_Assumptions contains constraints on the parameters which should be */
261 /* checked by the user after the termination of Solver. These constraints */
262 /* result from the parameter consistency check the behavior of which is */
263 /* controlled by the setting of the option variable Solver_Incons_Params. */
264 /* Any numerical solution of the equations obtained by assigning numerical */
265 /* values to symbolic parameters should be checked for consistency with all */
266 /* expressions in Solver_Assumptions. */
267 /******************************************************************************/
269 define_variable( Solver_Assumptions, [], list )$
272 /******************************************************************************/
273 /* Solver_Del_Eq holds the name of a function which controls the behavior of */
274 /* the heuristic search algorithm which extract linear equations from the */
275 /* entire system of equations. */
276 /******************************************************************************/
278 define_variable( Solver_Del_Eq, 'MakeSquareLinearBlocks, any )$
281 /******************************************************************************/
282 /* If Solver_Valuate_All_Nonlin_Vars is true then the ValuationSolver will */
283 /* valuate the equations w.r.t. all remaining variables and not only w.r.t */
284 /* variables which are being searched for at the current step. */
285 /******************************************************************************/
287 define_variable( Solver_Valuate_All_Nonlin_Vars, false, boolean )$
290 /******************************************************************************/
291 /* Solver_Valuation_Strategy holds the name of the equation valuation */
292 /* strategy called by the valuation solver to determine the order by which */
293 /* the equations are to be solved. */
294 /******************************************************************************/
296 define_variable( Solver_Valuation_Strategy, 'MinVarPathsFirst, any_check )$
299 'Solver_Valuation_Strategy,
302 if not FunctionP( x ) then
303 ErrorHandler( "UndefStrat", x, 'Fatal )
309 /******************************************************************************/
310 /* Solver_Default_Valuation contains the default valuation for arithmetic */
311 /* operators. Whenever an operator is encountered for which no valuation has */
312 /* been defined by SetProp( <operator>, 'Valuation, <valuation> ) this value */
313 /* is taken for the formula complexity calculations. */
314 /******************************************************************************/
316 define_variable( Solver_Default_Valuation, 10, fixnum )$
319 /******************************************************************************/
320 /* Solver_Max_Len_Val_Order limits the length of the list of candidates for */
321 /* the solve calls in ValuationSolver. A low value will usually increase the */
322 /* efficiency of the valuation solver since, in general, the first or second */
323 /* attempt to solve an equation (hopefully) succeeds. */
324 /******************************************************************************/
326 define_variable( Solver_Max_Len_Val_Order, 5, fixnum )$
329 /******************************************************************************/
330 /* Solver_Transforms is a list containing the names of functions which can be */
331 /* applied to an equation after a failed solve call. These functions must */
332 /* take three arguments: the equation to be transformed, the variable to be */
333 /* solved for, and a list of (probably implicit) solutions the Solver has */
334 /* already found for the equation. */
335 /******************************************************************************/
337 define_variable( Solver_Transforms, [], list )$
340 /******************************************************************************/
341 /* If Solver_Postprocess is set to false no postprocessing of the results will*/
342 /* be done. Instead, the solutions are displayed in the internal hierarchical */
343 /* list format. Useful for debugging purposes. */
344 /******************************************************************************/
346 define_variable( Solver_Postprocess, true, boolean )$
349 /******************************************************************************/
350 /* Solver_Backsubst controls the output format of Solver. If Solver_Backsubst */
351 /* is true then the result will be displayed with fully evaluated right-hand */
352 /* sides for each variable. If the option variable is set to false then the */
353 /* right-hand sides of the solutions may still contain references to some */
354 /* of the other variables which have been solved for. */
355 /******************************************************************************/
357 define_variable( Solver_Backsubst, true, boolean )$
360 /******************************************************************************/
361 /* With Solver_Disp_All_Sols set to true all solutions will be displayed */
362 /* including those for the variables which have been solved for in the */
363 /* solution process but have not been explicitly asked for. */
364 /******************************************************************************/
366 define_variable( Solver_Disp_All_Sols, false, boolean )$
369 /******************************************************************************/
370 /* If Solver_RatSimp_Sols is true then the Solver Postprocessor will */
371 /* fullratsimp the solutions before returning them. */
372 /******************************************************************************/
374 define_variable( Solver_RatSimp_Sols, true, boolean )$
377 /******************************************************************************/
378 /* If Solver_Dump_To_File is true then the ValuationSolver writes the */
379 /* solutions and yet unsolved equations to a file after each iteration. This */
380 /* might help if the Solver crashes. */
381 /******************************************************************************/
383 define_variable( Solver_Dump_To_File, false, boolean )$
386 /******************************************************************************/
387 /* Solver_Dump_File contains the name of the file to which the dump is */
389 /******************************************************************************/
391 define_variable( Solver_Dump_File, "SOLVER.DMP", any )$
394 /******************************************************************************/
395 /* Solver_Break_Test holds the name of a function which is called immediately */
396 /* before attempting to solve an equation. This function then decides whether */
397 /* Solver should try to solve the equation or whether it should stop, e.g. */
398 /* because the problem has become too complex. The arguments passed to the */
399 /* Solver_Break_Test are 1. the equation, 2. the variable, 3. the valuation. */
400 /* The Solver stops if the Solver_Break_Test returns true, it continues if */
401 /* the return value is false. */
402 /******************************************************************************/
404 define_variable( Solver_Break_Test, 'SolverJustDoIt, any )$
407 /******************************************************************************/
408 /* Solver, main program */
409 /******************************************************************************/
411 Solver( Equations, [ SolverParams ] ) := (
414 [ Equations, SolverParams ], list
420 Variables, /* List of variables to be solved for */
421 UserVars, /* List of variables specified by user */
422 Parameters, /* List of symbols to be used as parameters */
423 Expressions, /* List of compound expressions to be solved for */
424 PowerSubst, /* List of substituted symbols for powers */
426 Solutions, /* List of solutions found by Solver */
427 RemainingEqs, /* List of remaining equations */
429 /* Assumptions made in linear solver should be local */
430 Solver_Assumptions : [],
437 Variables, UserVars, Parameters, Expressions, PowerSubst,
443 /* No assumptions to start with */ErrorHandlerSolver_Assumptions : [],
445 /* Initialize list of solutions */
448 /* Do all necessary preprocessing */
452 'Equations, 'SolverParams, 'Variables, 'Parameters,
453 'Expressions, 'PowerSubst, 'UserVars
455 SetupSolver( Equations, SolverParams )
458 if MsgLevel = 'DEBUG then
460 Equations, SolverParams, Variables, Parameters, Expressions,
461 PowerSubst, UserVars, Solutions
467 /* Search for and apply immediate assignments */
469 Active : Solver_Immed_Assign,
473 [ 'Active, 'Solutions, 'Equations, 'Variables ],
474 ImmediateAssignments( Solutions, Equations, Variables, Parameters )
477 Active : Active and Solver_Repeat_Immed,
479 if MsgLevel = 'DEBUG then
481 Equations, SolverParams, Variables, Parameters, Expressions,
482 PowerSubst, UserVars, Solutions
486 if Empty( Equations ) or Empty( Variables ) then
492 fullratsimp( lhs( Eq ) - rhs( Eq ) )
497 /* Find and solve linear equations */
499 Active : Solver_Linear,
503 [ 'Active, 'Solutions, 'Equations, 'Variables ],
504 LinearSolver( Solutions, Equations, Variables, Parameters )
507 Active : Active and Solver_Repeat_Linear,
509 if MsgLevel = 'DEBUG then
511 Equations, SolverParams, Variables, Parameters, Expressions,
512 PowerSubst, UserVars, Solutions
516 if Empty( Equations ) or Empty( Variables ) then
520 if Solver_Valuate_All_Nonlin_Vars then
522 SetDifference( listofvars( Equations ), Parameters ),
526 /* apply valuation strategies to solve the nonlinear equations. */
529 [ 'Active, 'Solutions, 'Equations, 'Variables ],
530 ValuationSolver( Solutions, Equations, Variables, Parameters )
536 /* Return the solutions and the unsolved equations */
538 if Solver_Postprocess then
539 return( PostProcess( Solutions, UserVars, Expressions, PowerSubst ) )
546 /******************************************************************************/
547 /* TerminateSolver terminates the Solver. */
548 /******************************************************************************/
550 TerminateSolver() := error( ErrMsg["SolvrTerm"] )$
553 /******************************************************************************/
554 /* SetupSolver preprocesses the equations and optional parameters before */
555 /* submitting them to the Solver. The equations are checked if they are */
556 /* really equations, and all equations of the form NUMBER = NUMBER or */
557 /* f( PARAMETERS ) = g( PARAMETERS ) are checked for consistency and then */
559 /******************************************************************************/
561 SetupSolver( Equations, SolverParams ) := (
564 [ Equations, SolverParams ], list
570 i, AllVars, Var, SubstSym, Power,
571 Variables, Parameters, Expressions,
578 AllVars, Power, Variables, Parameters, Expressions,
581 [ Var, SubstSym ], any
590 /* Make sure that Equations is a list. Abort if it is not. */
592 if not listp( Equations ) then
593 ErrorHandler( "EqsNotLst", Equations, 'Fatal ),
595 /* delete all entries from Equations which are not equations */
597 Equations : sublist( Equations, 'EquationP ),
599 /* Convert all floating-point numbers to rational numbers. If this was */
600 /* not done then rounding errors could fool the consistency check. */
602 Equations : map( 'rat, Equations ),
604 /* Process the optional arguments to Solver */
606 if not Empty( SolverParams ) then (
608 Variables : SolverParams[1],
610 /* Make sure that Variables is a list. Abort if it is not. */
612 if not listp( Variables ) then
613 ErrorHandler( "VarNotLst", Variables, 'Fatal ),
615 /* delete multiple occurrences of identical symbols */
617 Variables : Setify( Variables ),
619 PrintMsg( 'DETAIL, SolverMsg["VarsAre"], Variables ),
621 if length( SolverParams ) > 1 then (
623 Parameters : SolverParams[2],
625 /* Make sure that Parameters is a list. Abort if it is not. */
627 if not listp( Parameters ) then
628 ErrorHandler( "ParNotLst", Parameters, 'Fatal ),
630 /* delete multiple occurrences of identical symbols */
632 Parameters : Setify( Parameters ),
634 PrintMsg( 'DETAIL, SolverMsg["ParsAre"], Parameters )
640 /* Check if Variables and Parameters are disjoint sets of symbols */
642 if not DisjointP( Variables, Parameters ) then
644 "VarParConfl", Intersection( Variables, Parameters ), 'Fatal
648 /* Make a list of all variables to be solved for. This list may contain */
649 /* more symbols than Variables when either no SolverParams have been */
650 /* given or when the user has specified only a subset of all existing */
653 AllVars : SetDifference( listofvars( Equations ), Parameters ),
655 /* solve for all available variables if Variables is empty. */
657 if Empty( Variables ) then
661 /* Check all those equations which contain only equations of the form */
662 /* NUMBER = NUMBER or which contain only parameters and none of the */
663 /* variables of interest for consistency. Remove consistent equations */
664 /* and store the assumptions made in the list Solver_Assumptions. */
666 Equations : ParamConsistency( Equations, Parameters ),
668 /* Abort if no equations are left after the above step. */
670 if Empty( Equations ) or Empty( Variables ) then (
671 PrintMsg( 'SHORT, SolverMsg["NoEqOrVar"] ),
672 return( [ [], SolverParams, Variables, Parameters, [], [], [] ] )
675 /* If there are compound expressions to be solved for then the solver */
676 /* tries to solve for the variables contained in them first. */
677 /* Subsequently the expressions are rebuilt from the solutions of */
678 /* these variables and the parameters. */
680 for i thru length( Variables ) do
682 /* Search the variable list for non-atomic expressions. */
684 if not atom( Var : Variables[i] ) then (
686 /* append expression to the expression list */
687 Expressions : endcons( Var, Expressions ),
689 /* Insert the variables in the expression into the list of variables */
690 /* but keep the parameters out. */
691 Variables[i] : SetDifference( listofvars( Var ), Parameters ),
693 PrintMsg( 'SHORT, SolverMsg["TrySolve4"], Variables[i] ),
694 PrintMsg( 'SHORT, SolverMsg["Solve4Exp"], Var )
697 /* Make a list of all the variables the user actually wants to know */
698 UserVars : sublist( Variables, 'atom ),
700 /* Flatten the list of variables and make it a set. */
701 Variables : Setify( Flatten( Variables ) ),
704 /* If Solver_Subst_Powers is true then substitute powers by new symbols */
706 if Solver_Subst_Powers then (
708 PrintMsg( 'SHORT, SolverMsg["SubstPwrs"] ),
710 for Var in AllVars do (
712 /* get all powers of Var in Equations. Convert negative powers to */
714 Power : Setify( abs( ListOfPowers( Equations, Var ) ) ),
716 /* If there is more than one power of Var then substitute each */
717 /* var^m only if all m are integer multiples of the lowest */
718 /* power > 0. The check is done by examining the modulus of all */
719 /* powers with respect to the lowest power. */
722 not member( 'false, map( 'integerp, Power ) )
727 if ( length( Power ) = 1 ) or
730 [ Modulus : Power[1] ],
733 map( 'ZeroP, totaldisrep( rat( rest( Power ) ) ) )
738 /* Make a new symbol for var^power */
739 SubstSym : concat( Var, "^", Power[1] ),
741 /* Store a reference to the original term in an assoc list */
742 PowerSubst : endcons( SubstSym = Var^Power[1], PowerSubst ),
744 /* Substitute the new symbol for the original term */
745 Equations : ratsubst( SubstSym, Var^Power[1], Equations ),
746 Variables : subst( SubstSym, Var, Variables ),
749 PrintMsg( 'DETAIL, SolverMsg["subst"], Var^Power )
752 ) /* END for Var in AllVars */
754 ), /* END if Solver_Subst_Powers */
758 Equations, SolverParams, Variables, Parameters, Expressions,
766 /******************************************************************************/
767 /* ParamConsistency checks equations of the form NUMBER = NUMBER and equa- */
768 /* tions which contain only parameters for consistency. If the system cannot */
769 /* determine whether a parametric expression is zero then the user is optio- */
770 /* nally asked to supply the required information. The assumptions made are */
771 /* stored in the list Solver_Assumptions. */
772 /******************************************************************************/
774 ParamConsistency( Eqs, Pars, [ Action ] ) := (
783 [ Eq, lhsminusrhs, i, consistent ],
786 [ Eq, lhsminusrhs ], any,
791 PrintMsg( 'SHORT, SolverMsg["ConsChk"] ),
793 if Empty( Action ) then
800 for i thru length( Eqs ) do (
804 /* Does the equation contain only numbers or parameters? */
806 SetDifference( listofvars( Eq ), Pars )
810 /* If so, check for consistency */
811 lhsminusrhs : expand( lhs( Eq ) - rhs( Eq ) ),
813 /* Test if difference of both rhs's is zero */
814 if lhsminusrhs # 0 then
816 if SolverAssumeZero( lhsminusrhs ) then
817 PrintMsg( 'SHORT, SolverMsg["Assum"], lhsminusrhs = 0 )
819 if Action = 'BREAK then (
820 /* Abort if difference is non-zero */
821 PrintMsg( 'SHORT, SolverMsg["Incons"], lhs( Eq ) = rhs( Eq ) ),
825 return( consistent : false ),
827 /* Kill the now redundant equation */
828 if Action = 'BREAK then
836 PrintMsg( 'SHORT, SolverMsg["NoneFnd"] ),
838 if Action = 'BREAK then
839 return( delete( [], Eqs ) )
846 /******************************************************************************/
847 /* SolverAssumeZero checks whether Expression is (assumed to be) equal to */
848 /* zero. If it isn't, the function returns false or asks the user for his */
849 /* decision. New assumptions are appended to the list Solver_Assumptions. */
850 /******************************************************************************/
852 SolverAssumeZero( Expression ) := (
860 [ AssumptionExists, i ],
864 AssumptionExists, boolean
867 /* Return false immediately if Expression is a number # 0 or if */
868 /* Solver_Incons_Params is set to 'BREAK. */
871 Empty( listofvars( Expression ) )
872 or ( Solver_Incons_Params = 'BREAK )
876 /* Do a simple check to find out whether assumption already exists: */
877 /* Zero is substituted for Expression in the stored assumption. If the */
878 /* result is zero then the assumption already exists. */
880 AssumptionExists : false,
881 for i thru length( Solver_Assumptions ) while not AssumptionExists do
884 ratsubst( 0, Expression, lhs( Solver_Assumptions[i] ) )
887 AssumptionExists : true,
889 /* If the expression is not yet assumed to be equal to zero, ask the user */
890 /* to decide whether it is. */
892 if not AssumptionExists then
895 ( Solver_Incons_Params = 'ASK )
897 ( AskZeroNonzero( Expression ) # 'zero )
901 /* If difference is equal to zero or assumed to be so then store */
902 /* the constraint in the global list Solver_Assumptions. So the user*/
903 /* has access to all assumptions made during the solution process */
904 /* and can check any numerical solutions for consistency with the */
906 Solver_Assumptions : endcons( Expression = 0, Solver_Assumptions )
909 PrintMsg( 'DETAIL, SolverMsg["AssmFnd"], Expression = 0 ),
915 AskZeroNonzero( Expression ) :=
916 if equal( x, 0 ) = true then 'zero
919 while s#'zero and s#'nonzero do (
920 s : read( "Is", Expression, "zero or nonzero?" )
925 /******************************************************************************/
926 /* ListOfPowers returns the list of powers # 0 of a variable in a set of */
928 /******************************************************************************/
930 ListOfPowers( Eqs, Var ) := (
944 Powers( expand( lhs( Eq ) - rhs( Eq ) ), Var )
953 /******************************************************************************/
954 /* ImmediateAssignments directly applies all immediate assignments of the */
955 /* form var = rhs... before the actual Solver is called. */
956 /******************************************************************************/
958 ImmediateAssignments( Solutions, RemainingEqs, Variables, Parameters ) := (
961 [ Solutions, RemainingEqs, Variables, Parameters ], list
966 [ i, Vars, AssignmentMade, Left, Right ],
971 AssignmentMade, boolean,
975 PrintMsg( 'SHORT, SolverMsg["SrchImmed"] ),
977 AssignmentMade : false,
979 for i thru length( RemainingEqs ) do block(
982 Left : lhs( RemainingEqs[i] ),
983 Right : rhs( RemainingEqs[i] ),
985 /* Scan the equations for simple assignments of the form X = Expr or */
986 /* Expr = X and apply this assignment only if X is not a parameter and */
987 /* if Expr contains only numbers or parameters. */
988 /* Remark: Equations containing only parameters have already been remo- */
989 /* ved from the equation list in SetupSolver. */
991 if symbolp( Left ) then (
993 Vars : listofvars( Right ),
995 /* Check if lhs is an isolated variable and make sure that there are */
996 /* only parameters on the right-hand side. */
998 if freeof( Left, Right )
999 and Empty( SetDifference( Vars, Parameters ) )
1002 if assoc( Left, Solutions ) = false then (
1005 SolverMsg["Assign"], totaldisrep( Left = Right )
1008 if member( Left, map( lhs, Solutions ) ) then (
1009 if assoc( Left, Solutions ) # Right then (
1010 PrintMsg( 'SHORT, SolverMsg["Incons"], Left = Right ),
1015 Solutions : endcons( Left = Right, Solutions ),
1016 RemainingEqs[i] : [],
1018 AssignmentMade : true
1021 /* This return prevents Macsyma from entering the next if statement */
1022 /* which must only be executed when the current outer if statement */
1023 /* has not been entered. */
1028 if symbolp( Right ) then (
1030 Vars : listofvars( Left ),
1032 if freeof( Right, Left )
1033 and Empty( SetDifference( Vars, Parameters ) )
1036 if assoc( Right, Solutions ) = false then (
1039 SolverMsg["Assign"], totaldisrep( Right = Left )
1042 if member( Right, map( lhs, Solutions ) ) then (
1043 if assoc( Right, Solutions ) # Left then (
1044 PrintMsg( 'SHORT, SolverMsg["Incons"], Left = Right ),
1049 Solutions : endcons( Right = Left, Solutions ),
1050 RemainingEqs[i] : [],
1052 AssignmentMade : true
1057 ), /* END for i thru length */
1059 if AssignmentMade then (
1061 /* delete the used equations from the list */
1062 RemainingEqs : delete( [], RemainingEqs ),
1064 if not Empty( RemainingEqs ) then (
1066 /* Remove all the variables from the working list which have been */
1067 /* determined by an immediate assignment. */
1068 Variables : SetDifference( Variables, map( 'lhs, Solutions ) ),
1070 /* Evaluate the remaining equations with the constraints */
1071 RemainingEqs : ev( RemainingEqs, Solutions ),
1073 /* Do a parameter consistency check */
1074 RemainingEqs : ParamConsistency( RemainingEqs, Parameters )
1078 PrintMsg( 'SHORT, SolverMsg["NoEqTerm"] )
1082 PrintMsg( 'SHORT, SolverMsg["NoImmed"] ),
1084 /* END if AssignmentMade */
1086 return( [ AssignmentMade, Solutions, RemainingEqs, Variables ] )
1091 /******************************************************************************/
1092 /* LinearSolver extracts blocks of linear equations from an arbitrary system */
1093 /* of equations by a heuristic searching strategy and solves the linear block */
1094 /* if there is one. */
1095 /******************************************************************************/
1097 LinearSolver( Solutions, Equations, Variables, Parameters ) := (
1100 [ Solutions, Equations, Variables, Parameters ], list
1106 CoeffMatrix, ValuationMatrix, ActiveVars,
1107 LinEqNos, LinVarNos, NewVars, LinSolVars,
1108 LinearEqs, LinearVars, LinearSolutions,
1109 i, j, me, mv, NumVars, NumEqs,
1110 MaxValVar, MaxValEq,
1111 EqValuation, VarValuation,
1114 linsolvewarn : false,
1115 linsolve_params : false,
1116 Solve_Inconsistent_Error : false,
1122 [ CoeffMatrix, ValuationMatrix ], any,
1124 LinEqNos, LinVarNos, ActiveVars, LinearEqs, LinearVars, LinSolVars,
1125 EqValuation, VarValuation, LinearSolutions, NewVars, rhsExpressions
1127 [ i, j, me, mv, NumVars, NumEqs, MaxValVar, MaxValEq ], fixnum
1130 if Empty( Equations ) then (
1132 if Empty( Variables ) then
1133 PrintMsg( 'SHORT, SolverMsg["AllSolved"] )
1135 PrintMsg( 'SHORT, SolverMsg["NoEqLeft"], Variables ),
1137 return( [ false, Solutions, Equations, Variables ] )
1139 else if Empty( Variables ) then (
1140 PrintMsg( 'SHORT, SolverMsg["EqLeft"] ),
1141 return( [ false, Solutions, Equations, Variables ] )
1144 PrintMsg( 'SHORT, SolverMsg["SrchLinEq"] ),
1146 /* Make a list of all remaining variables */
1147 NewVars : listofvars( Equations ),
1149 if Solver_Find_All_Linear_Vars then (
1151 /* The following rather weird commands put the variables into the order */
1152 /* [ variables to be currently solved for, other variables ]. If the */
1153 /* current variables are linear variables then it is more likely that */
1154 /* they will have explicit solutions if they are located in the left */
1155 /* half of the system of equations. Otherwise the will more likely be */
1156 /* used as parameters of the null space (if there is one). */
1158 ActiveVars : append(
1159 Intersection( Variables, NewVars ),
1160 SetDifference( NewVars, append( Variables, Parameters ) )
1162 LinearEqs : Equations,
1166 ActiveVars : Intersection( Variables, NewVars ),
1168 for i thru length( Equations ) do
1170 not Empty( Intersection( listofvars( Equations[i] ), ActiveVars ) )
1172 LinearEqs : endcons( Equations[i], LinearEqs ),
1175 Equations : delete( [], Equations )
1178 PrintMsg( 'SHORT, SolverMsg["wrt"], ActiveVars ),
1180 /* Set up the complete coefficient matrix w.r.t. all variables */
1181 CoeffMatrix : ComplCoeffMatrix( LinearEqs, ActiveVars ),
1183 /* The valuation matrix contains a 1 at each Position where a variable */
1184 /* appears in a nonlinear form, and 0's otherwise. */
1186 ValuationMatrix : matrixmap(
1197 /* EqValuation contains the number of nonlinear variables for each eq. */
1199 lambda( [ Row ], apply( "+", Row ) ),
1200 ListMatrix( ValuationMatrix )
1203 /* VarValuation contains for all vars the number of equations in which */
1204 /* var[i] appears in a nonlinear form. */
1206 lambda( [ Row ], apply( "+", Row ) ),
1207 ListMatrix( transpose( ValuationMatrix ) )
1210 LinEqNos : makelist( i, i, 1, NumEqs : length( EqValuation ) ),
1211 LinVarNos : makelist( i, i, 1, NumVars : length( VarValuation ) ),
1214 /* Remove nonlinear equations and/or variables until only a linear */
1215 /* block remains, i.e. for all i, j VarValuation[i] = 0 and */
1216 /* EqValuation[j] = 0. */
1219 ( apply( "+", VarValuation ) # 0 )
1221 ( apply( "+", EqValuation ) # 0 )
1224 /* Determine maximum equation valuation and number of corresponding */
1229 for i thru NumEqs do
1230 if EqValuation[i] > MaxValEq then (
1232 MaxValEq : mode_identity( fixnum, EqValuation[i] )
1235 /* Determine maximum variable valuation and number of corresponding */
1240 for j thru NumVars do
1241 if VarValuation[j] > MaxValVar then (
1243 MaxValVar : mode_identity( fixnum, VarValuation[j] )
1247 if apply( Solver_Del_Eq, [ MaxValEq, MaxValVar ] ) then (
1250 for j thru NumVars do (
1251 VarValuation[j] : VarValuation[j] - ValuationMatrix[i, j],
1252 ValuationMatrix[i, j] : 0
1255 /* Mark equation as deleted */
1262 for i thru NumEqs do (
1263 EqValuation[i] : EqValuation[i] - ValuationMatrix[i, j],
1264 ValuationMatrix[i, j] : 0
1267 /* Mark variable as deleted */
1268 VarValuation[j] : 0,
1274 /* Make list of linear equations */
1275 LinEqNos : delete( 0, LinEqNos ),
1277 /* Make list of linear variables */
1278 LinVarNos : delete( 0, LinVarNos ),
1280 if Empty( LinEqNos ) or Empty( LinVarNos ) then (
1281 PrintMsg( 'SHORT, SolverMsg["NoLinEqs"] ),
1282 return( [ false, Solutions, append( LinearEqs, Equations ), Variables ] )
1285 /* Extract linear equations. append all nonlinear equations to Equations */
1287 for i thru length( LinearEqs ) do
1288 if not member( i, LinEqNos ) then (
1289 Equations : endcons( LinearEqs[i], Equations ),
1293 LinearEqs : delete( [], LinearEqs ),
1296 /* Extract linear variables. Since the extraction of linear equations may */
1297 /* also have removed linear variables (the coefficients are now 0) it is */
1298 /* necessary to intersect the set of the linear variables with the set of */
1299 /* those variables which actually appear in the linear equations. */
1301 LinearVars : Intersection(
1302 map( lambda( [ i ], ActiveVars[i] ), LinVarNos ),
1303 listofvars( LinearEqs )
1306 /* By analogy, the same applies to the linear equations. Thus, keep only */
1307 /* those equations which still contain any of the linear variables. */
1309 for i thru length( LinearEqs ) do
1310 if DisjointP( listofvars( LinearEqs[i] ), LinearVars ) then (
1311 Equations : endcons( LinearEqs[i], Equations ),
1315 LinearEqs : delete( [], LinearEqs ),
1317 /* Return if no linear equations are left */
1319 if Empty( LinearVars ) or Empty( LinearEqs ) then (
1320 PrintMsg( 'SHORT, SolverMsg["NoLinEqs"] ),
1321 return( [ false, Solutions, append( LinearEqs, Equations ), Variables ] )
1326 SolverMsg["Found"], length( LinearEqs ), SolverMsg["LinEqs"],
1327 length( LinearVars ), SolverMsg["LinVars"]
1329 PrintMsg( 'SHORT, SolverMsg["VarsAre"], LinearVars ),
1330 PrintMsg( 'DETAIL, SolverMsg["EqsAre"], LinearEqs ),
1331 PrintMsg( 'SHORT, SolverMsg["SolvLinEq"] ),
1333 rhsExpressions : [],
1334 Solve_Inconsistent_Eqn_Nos : [ 0 ],
1336 while not Empty( Solve_Inconsistent_Eqn_Nos ) do (
1338 /* solve the linear equations */
1339 LinearSolutions : LinsolveM( LinearEqs , LinearVars ),
1341 /* Check for "inconsistent" equations. */
1342 if not Empty( Solve_Inconsistent_Eqn_Nos ) then (
1344 PrintMsg( 'DEBUG, SolverMsg["Incons"], Solve_Inconsistent_Eqn_Nos ),
1346 /* Remove "inconsistent" equations */
1347 for i in Solve_Inconsistent_Eqn_Nos do
1350 LinearEqs : delete( [], LinearEqs ),
1352 /* append rhs = 0 to Solver_Assumptions if rhs contains only */
1354 rhsExpressions : ParamConsistency(
1355 Solve_Inconsistent_Terms, Parameters
1362 /* append rhs's which have led to "inconsistencies" but still */
1363 /* contain variables to the list of equations. */
1364 Equations : append( Equations, rhsExpressions ),
1366 PrintMsg( 'DETAIL, SolverMsg["Solutions"], LinearSolutions ),
1368 /* Insert the solutions from linsolve into the remaining equations */
1369 Equations : ParamConsistency(
1370 fullratsimp( ev( Equations, LinearSolutions ) ),
1374 /* append the linear solutions to the list of solutions */
1375 Solutions : append( Solutions, LinearSolutions ),
1377 /* append all variables to the working list which appear on the rhs's of */
1378 /* the linear solutions. delete all variables which have been solved for. */
1380 /* Linear variables for which a solution has been obtained. */
1381 LinSolVars : map( 'lhs, LinearSolutions ),
1383 /* Linear variables which are free parameters of the null space. */
1384 LinearVars : SetDifference( LinearVars, LinSolVars ),
1386 /* append all those variables which are parameters of the null space of */
1387 /* the linear equations and which do not appear in the remaining */
1388 /* equations to the list of parameters. */
1390 for Var in LinearVars do
1391 if freeof( Var, Equations ) then (
1392 PrintMsg( 'SHORT, SolverMsg["FreeVar2Par"], Var ),
1393 Parameters : endcons( Var, Parameters ),
1394 Variables : delete( Var, Variables )
1397 NewVars : SetDifference(
1398 listofvars( map( 'rhs, LinearSolutions ) ),
1403 SetDifference( Variables, LinSolVars ),
1407 return( [ true, Solutions, Equations, Variables ] )
1412 /******************************************************************************/
1413 /* The following strategies decide whether the linear solver should delete a */
1414 /* nonlinear equation or a nonlinear variable from the system while searching */
1415 /* for linear subblocks of equations. */
1416 /******************************************************************************/
1418 define_variable( EqWasLast, false, boolean )$
1420 MakeSquareLinearBlocks( ValEq, ValVar ) := (
1423 [ ValEq, ValVar ], fixnum
1426 if ValEq = ValVar then
1427 EqWasLast : not EqWasLast
1429 if ValEq > ValVar then
1436 DelEqBeforeVar( ValEq, ValVar ) := (
1439 [ ValEq, ValVar ], fixnum
1442 if ValEq >= ValVar then
1449 /******************************************************************************/
1450 /* ComplCoeffMatrix returns a matrix whose row size is equal to the number of */
1451 /* equations and whose column size is equal to the number of variables. The */
1452 /* entry at Position [i,j] is RatCoeff( equation[i], variable[j] ) if */
1453 /* equation[i] is linear w.r.t. variable[j] and 'false if equation[i] is */
1454 /* nonlinear w.r.t. variable[j]. */
1455 /******************************************************************************/
1457 ComplCoeffMatrix( Eqs, ActiveVars ) := (
1460 [ Eqs, ActiveVars ], list
1470 /* for each equation do */
1476 /* for each variable do */
1482 rc : LinCoeff( Eq, Var ),
1484 /* Return the RatCoeff only if it contains none of the active */
1485 /* variables or if the equation doesn't contain var at all. */
1488 ( ( rc # 0 ) and DisjointP( listofvars( rc ), ActiveVars ) )
1499 ) /* END lambda( [ Var ] ) */
1501 ), /* END lambda( [ Eq ] ) */
1504 /* map target: Transform all equations into homogeneous form. */
1509 fullratsimp( expand( lhs( Eq ) - rhs( Eq ) ) )
1515 ) /* END map( lambda( [ Eq ] ) ) */
1522 /******************************************************************************/
1523 /* LinCoeff returns the linear coefficient of Var within Eq if Var appears */
1524 /* raised to the first power only. */
1525 /******************************************************************************/
1527 LinCoeff( Eq, Var ) := (
1541 if ListOfPowers( [ Eq ], Var ) = [ 1 ] then (
1542 BCoeff : bothcoeff( Eq, Var ),
1543 if freeof( Var, second( BCoeff ) ) then
1544 return( first( BCoeff ) )
1552 /******************************************************************************/
1553 /* ValuationSolver */
1554 /******************************************************************************/
1556 ValuationSolver( Solutions, Equations, Variables, Parameters ) := (
1559 [ Solutions, Equations, Variables, Parameters ], list
1565 VarPaths, ValMatrix, Eq, Var, Trans, TempEq, TransEq,
1566 SolveOrder, SolveInfo, Transform, Solution, SolCheck,
1567 Status, Solved, Failed, UniqueSol, TryToSolve, CheckSol,
1572 [ VarPaths, ValMatrix, Eq, Var, Trans, TempEq, TransEq ], any,
1573 [ SolveOrder, SolveInfo, Transform, Solution, SolCheck ], list,
1574 [ Status, Solved, Failed, UniqueSol, TryToSolve, CheckSol ], boolean,
1583 PrintMsg( 'SHORT, SolverMsg["Chk4RemEq"] ),
1585 if Empty( Equations ) then (
1587 if Empty( Variables ) then
1588 PrintMsg( 'SHORT, SolverMsg["AllSolved"] )
1590 PrintMsg( 'SHORT, SolverMsg["NoEqLeft"], Variables ),
1595 else if Empty( Variables ) then (
1596 PrintMsg( 'SHORT, SolverMsg["EqLeft"] ),
1603 length( Equations ), SolverMsg["Eqs"],
1604 length( Variables ), SolverMsg["Vars"]
1606 PrintMsg( 'DETAIL, SolverMsg["VarsAre"], Variables ),
1607 PrintMsg( 'DEBUG, SolverMsg["EqsAre"], Equations ),
1609 /* Dump solutions and remaining equations to file if requested. */
1610 if Solver_Dump_To_File then
1611 DumpToFile( Solutions, Equations, Variables ),
1613 if ( length( Variables ) = 1 ) and ( length( Equations ) = 1 ) then
1614 SolveOrder : [ [ 1, 1, "(irrelevant)" ] ]
1617 PrintMsg( 'SHORT, SolverMsg["ValStrat"] ),
1619 /* Set up the valuation matrices. */
1620 VarPaths : OccurrenceMatrix( Equations, Variables ),
1621 ValMatrix : ValuationMatrix( Equations, Variables ),
1623 /* Determine an order by which the equations should be solved. */
1624 SolveOrder : apply( Solver_Valuation_Strategy, [ VarPaths, ValMatrix ] )
1629 unless Solved or Empty( SolveOrder ) do (
1631 SolveInfo : Pop( SolveOrder ),
1632 if listp(SolveInfo[1]) then SolveInfo : first(SolveInfo),
1633 k : mode_identity( fixnum, first( SolveInfo ) ),
1634 Eq : part( Equations, k ),
1635 Var : Variables[ second( SolveInfo ) ],
1639 SolverMsg["TrySolveEq"], k, SolverMsg["ForVar"], Var
1641 PrintMsg( 'SHORT, SolverMsg["Valuation"], third( SolveInfo ) ),
1642 PrintMsg( 'DETAIL, SolverMsg["EqIs"], Eq = 0 ),
1646 Transform : copylist( Solver_Transforms ),
1648 /* Do the solver break test to check whether it is worth */
1649 /* attempting to solve the equation at all. */
1650 Failed : apply( Solver_Break_Test, [Eq, Var, third( SolveInfo )] ),
1652 unless Solved or Failed do (
1654 /* Try to solve the selected equation */
1656 Solution : solve( Eq, Var ),
1658 /* Check if the equation was solved correctly */
1660 PrintMsg( 'SHORT, SolverMsg["CheckSol"] ),
1661 SolCheck : SolutionOK( Solution, Var ),
1663 PrintMsg( 'DETAIL, SolverMsg["Solutions"], Solution )
1666 SolCheck : [ false ],
1668 /* All solutions OK? */
1669 if member( true, SolCheck ) then (
1670 PrintMsg( 'SHORT, SolverMsg["SolOK"] ),
1674 /* If not, apply transformations */
1677 PrintMsg( 'SHORT, SolverMsg["SolNotOK"] ),
1679 /* Give up if no transformations are left */
1680 if Empty( Transform ) then (
1681 PrintMsg( 'SHORT, SolverMsg["GiveUp"] ),
1686 /* Retrieve one transformation function */
1687 Trans : Pop( Transform ),
1688 PrintMsg( 'SHORT, SolverMsg["AppTrans"], Trans ),
1690 /* and apply it to the equation, the variable, and the solution */
1691 TransEq : apply( Trans, [ Eq, Var, Solution ] ),
1693 /* The transformation should return an equation as its function */
1694 /* value. However, if no reasonable transformation of the */
1695 /* equation was possible then the SOLVE function should not be */
1696 /* tried again. Hence, to signal a failure, the transformation */
1697 /* must return an empty list, which will instruct the Solver to */
1698 /* try the next transformation instead. In addition, the */
1699 /* transformation may itself take care of solving the equation. */
1700 /* It must then return a list of solutions: */
1701 /* [ var = solution_1, var = solution_2, ... ] */
1703 /* Did the transformation fail? */
1704 if TransEq = [] then (
1705 PrintMsg( 'SHORT, SolverMsg["TransFail"] ),
1707 /* Instruct the Solver to try the next transformation */
1712 /* Did it solve the equation by itself? */
1713 else if listp( TransEq ) then (
1714 PrintMsg( 'SHORT, SolverMsg["TransSolv"] ),
1717 /* Instruct the Solver not to call SOLVE again */
1722 /* Transformation thinks it has succeeded, so try again */
1725 PrintMsg( 'DETAIL, SolverMsg["ResTrans"], Eq = 0 ),
1726 PrintMsg( 'SHORT, SolverMsg["RetryTrans"] ),
1732 ) /* END if Empty( Transform ) else */
1734 ) /* if member( true, SolCheck ) else */
1736 ) /* END unless Solved or Failed */
1738 ), /* END unless Solved or Empty( SolveOrder ) */
1741 if length( Solution ) > 1 then
1742 PrintMsg( 'SHORT, SolverMsg["NotUnique"] ),
1744 if member( false, SolCheck ) then
1745 PrintMsg( 'SHORT, SolverMsg["SolsLost"] ),
1747 /* Remove solved equation from list of equations. Store it in TempEq */
1748 /* so it can be appended to Equations again if the consistency check */
1750 TempEq : part( Equations, k ),
1751 Equations : delete( [], Set_Element( Equations, k, [] ) ),
1753 /* Check solutions for consistency with remaining equations. */
1754 for i thru length( Solution ) do (
1756 if part( SolCheck, i ) then (
1759 'DETAIL, SolverMsg["Solution"], i, SolverMsg["ForVar"], Var
1762 if not ParamConsistency(
1763 fullratsimp( ev( Equations, part( Solution, i ) ) ),
1767 PrintMsg( 'SHORT, SolverMsg["Contradict"], part( Solution, i ) ),
1768 Set_Element( Solution, i, 'INCONSISTENT_PATH )
1773 PrintMsg( 'DETAIL, SolverMsg["Dropped"], part( Solution, i ) ),
1774 Set_Element( Solution, i, [] )
1779 /* delete all implicit or empty solutions */
1780 Solution : delete( [], Solution ),
1782 if Empty( Solution ) then (
1783 PrintMsg( 'SHORT, SolverMsg["NoValidSol"], Var ),
1785 Equations : endcons( TempEq, Equations ),
1789 /* Check if there are any consistent solutions */
1795 if x = 'INCONSISTENT_PATH then
1804 PrintMsg( 'SHORT, SolverMsg["NoConsSol"], Var ),
1806 Solutions : endcons( 'INCONSISTENT_PATH, Solutions ),
1810 /* append consistent solutions to the solution list */
1812 PrintMsg( 'DETAIL, SolverMsg["ConsSol"], Var, ":", Solution ),
1814 Variables : delete( Var, Variables ),
1816 /* If the solution is unique or if there's only one consistent */
1817 /* solution then ... */
1818 if length( Solution ) = 1 then (
1820 /* ... store it, insert it into the remaining equations, and */
1821 /* add its rhs variables to the list of unknowns. */
1822 Solutions : append( Solutions, Solution ),
1823 Equations : copylist(
1824 fullratsimp( ev( Equations, Solution ) )
1829 listofvars( rhs( first( Solution ) ) ),
1835 else /* length( Solution ) > 1, call ValuationSolver recursively */
1838 [ MultipleSolutions, RSolutions, RVars, REqs, Sol, Stat ],
1841 [ MultipleSolutions, RSolutions, RVars, REqs ], list,
1846 MultipleSolutions : [],
1848 for Sol in Solution do (
1851 lambda([x,y], x::y),
1852 [ 'Stat, 'RSolutions, 'REqs, 'RVars ],
1855 copylist( fullratsimp( ev( Equations, Sol ) ) ),
1858 SetDifference( listofvars( rhs( Sol ) ), Parameters )
1864 MultipleSolutions : endcons( RSolutions, MultipleSolutions )
1866 ), /* END for Sol */
1868 Solutions : endcons( MultipleSolutions, Solutions ),
1873 ) /* END if Empty( Solution ) */
1879 /* append remaining equations to the solutions if no further */
1880 /* solutions could be determined. */
1882 for e in Equations do (
1883 if is( equal( e, 0 ) ) # 'unknown then (
1884 PrintMsg( SHORT, "Inconsistent equation", e = 0),
1889 Solutions : endcons( [ Equations ], Solutions )
1890 ), /* END if Solved */
1895 if Status and UniqueSol then
1898 return( [ Status, Solutions, Equations, Variables ] )
1903 /******************************************************************************/
1904 /* SolutionOK checks whether the result of a call to the solve function is */
1905 /* indeed a solution of the form var = expression_free_of_var. */
1906 /******************************************************************************/
1908 SolutionOK( Solution, Var ) := (
1911 [ Solution, Var ], any
1914 if listp( Solution ) then
1916 /* List of solutions must not be empty. */
1917 if Empty( Solution ) then
1922 /* Check if the lhs of each solution is equal to var and make sure */
1923 /* that var does not appear on the rhs's. */
1927 ( lhs( Sol ) = Var ) and freeof( Var, rhs( Sol ) )
1938 /******************************************************************************/
1939 /* ValuationMatrix generates a matrix of valuations with respect to each */
1940 /* equation and each variable. */
1941 /******************************************************************************/
1943 ValuationMatrix( Equations, Variables ) := (
1946 [ Equations, Variables ], list
1950 lambda( [ i, j ], Valuation( Equations[i], Variables[j] ) ),
1951 length( Equations ), length( Variables )
1956 /******************************************************************************/
1957 /* Operator valuation factors for expression valuation. */
1958 /******************************************************************************/
1960 SetProp( 'sin, 'Valuation, 10 )$
1961 SetProp( 'cos, 'Valuation, 10 )$
1962 SetProp( 'tan, 'Valuation, 10 )$
1963 SetProp( 'asin, 'Valuation, 12 )$
1964 SetProp( 'acos, 'Valuation, 12 )$
1965 SetProp( 'atan, 'Valuation, 12 )$
1966 SetProp( 'sinh, 'Valuation, 12 )$
1967 SetProp( 'cosh, 'Valuation, 12 )$
1968 SetProp( 'tanh, 'Valuation, 12 )$
1969 SetProp( 'asinh, 'Valuation, 12 )$
1970 SetProp( 'acosh, 'Valuation, 12 )$
1971 SetProp( 'atanh, 'Valuation, 12 )$
1972 SetProp( "+", 'Valuation, 1 )$
1973 SetProp( "-", 'Valuation, 1 )$
1974 SetProp( "*", 'Valuation, 4 )$
1975 SetProp( "/", 'Valuation, 4 )$
1976 SetProp( "^", 'Valuation, 10 )$
1977 SetProp( 'sqrt, 'Valuation, 10 )$
1978 SetProp( 'exp, 'Valuation, 10 )$
1979 SetProp( 'log, 'Valuation, 10 )$
1982 /******************************************************************************/
1983 /* With SetValuation, the operator valuation factors can be redefined. */
1984 /******************************************************************************/
1986 SetValuation( Operator, Valuation ) :=
1987 SetProp( Operator, 'Valuation, Valuation )$
1990 /******************************************************************************/
1991 /* Valuation measures the complexity of an expression with respect to Var by */
1992 /* weighting the operator tree representation of Expr. */
1993 /******************************************************************************/
1995 Valuation( Expr, Var ) := (
2010 /* Return zero if Expr does not contain Var. */
2011 if freeof( Var, Expr ) then
2015 /* Return 1 if Expr is an atom, i.e. Expr = Var. */
2016 if atom( Expr ) then
2019 /* If Expr is an algebraic expression then retrieve the valuation */
2020 /* factor associated with the operator of Expr and recursively apply */
2021 /* the valuation function to each subexpression of Expr. */
2025 OpFactor : mode_identity( fixnum, get( op( Expr ), 'Valuation ) )
2028 OpFactor : Solver_Default_Valuation,
2034 lambda( [ SubExpr ], Valuation( SubExpr, Var ) ),
2035 substpart( "[", Expr, 0 )
2046 /******************************************************************************/
2047 /* OccurrenceMatrix sets up a matrix in which the number of occurrences of */
2048 /* each variable in each equation is counted. */
2049 /******************************************************************************/
2051 OccurrenceMatrix( Equations, Variables ) := (
2054 [ Equations, Variables ], list
2058 lambda( [ i, j ], Occurences( Equations[i], Variables[j] ) ),
2059 length( Equations ), length( Variables )
2064 /******************************************************************************/
2065 /* Occurences counts the number of occurences of Var in Expr, i.e. the number */
2066 /* of paths to distinct occurrences of the atom Var in the internal tree */
2067 /* representation of Expr. */
2068 /******************************************************************************/
2070 Occurences( Expr, Var ) := (
2076 if atom( Expr ) then
2087 lambda( [ SubExpr], Occurences( SubExpr, Var ) ),
2088 substpart( "[", Expr, 0)
2094 /******************************************************************************/
2095 /* MinVarPathsFirst tries to find variables which can be easily isolated. */
2096 /* These are variables which appear only once in an entire expression tree */
2097 /* (= 1 in OccurrenceMatrix). */
2098 /******************************************************************************/
2100 MinVarPathsFirst( OccMat, ValMat ) := (
2103 [ OccMat, ValMat ], any
2109 SolveOrder, SolveOrder1, SumVarPaths,
2114 [ SolverOrder, SolveOrder1, SumVarPaths ], list,
2115 [ i, j, v, ne, nv, Function( RowSize, ColSize, Position ) ], fixnum
2120 ne : RowSize( OccMat ),
2121 nv : ColSize( OccMat ),
2124 lambda( [ Row ], apply( "+", Row ) ),
2125 ListMatrix( OccMat )
2128 /* Search for equations which contain only one variable in one path. */
2129 for i thru length( SumVarPaths ) do
2130 if SumVarPaths[i] = 1 then (
2131 SolveOrder : endcons(
2132 [ i, j : Position( 1, OccMat[i] ), ValMat[i, j] ],
2135 /* Mark eq/var Position as used. */
2140 /* Sort SolveOrder by least valuation. */
2141 if not Empty( SolveOrder ) then
2142 SolveOrder : SortSolveOrder( SolveOrder ),
2144 /* Find all variables with only one path in the expression tree. */
2148 if OccMat[i, j] = 1 then (
2149 SolveOrder1 : endcons( [ i, j, ValMat[i, j] ], SolveOrder1 ),
2154 /* Sort variables by least valuation. */
2155 if not Empty( SolveOrder1 ) then
2156 SolveOrder : append(
2158 SortSolveOrder( SolveOrder1 )
2161 /* append additional candidates if necessary. */
2162 if length( SolveOrder ) < Solver_Max_Len_Val_Order then (
2167 if ( v : mode_identity( fixnum, ValMat[i, j] ) ) # 0 then
2168 SolveOrder1 : endcons( [ i, j, v ], SolveOrder1 ),
2170 SolveOrder : append(
2172 SortSolveOrder( SolveOrder1 )
2175 /* Return only as many candidates as given by Solver_Max_Len_Val_Order */
2176 if ( i : length( SolveOrder ) ) > Solver_Max_Len_Val_Order then
2178 SolveOrder, Solver_Max_Len_Val_Order - i
2182 return( SolveOrder )
2187 /******************************************************************************/
2188 /* PostProcess does all the postprocessing needed to display the results. */
2189 /* This includes expansion of the solution list hierarchies, backsubsitution, */
2190 /* and extraction of the variables which the user explicitly asked for. */
2191 /******************************************************************************/
2193 PostProcess( Solutions, UserVars, Expressions, PowerSubst ) := (
2196 [ Solutions, UserVars, Expressions, PowerSubst ], list
2201 SolSet, UsrSolSet, UserSolutions, UnsolvedEqs, InternalSols,
2202 Var, TempVar, EvalVar,
2207 [ SolSet, UsrSolSet, UserSolutions, UnsolvedEqs, InternalSols ], list,
2208 [ Var, TempVar, EvalVar ], any,
2213 PrintMsg( 'SHORT, SolverMsg["PostPr"] ),
2215 /* first of all, Flatten the solution list hierarchy and drop all */
2216 /* inconsistent solution paths. */
2217 Solutions : sublist(
2218 ExpandSolutionHierarchy( Solutions ),
2219 lambda( [Set], last( Set ) # 'INCONSISTENT_PATH )
2222 /* Return an empty list if no consistent solution paths are left. */
2223 if Empty( Solutions ) then
2226 /* Do the backsubstitutions. */
2227 if not Empty( Solutions ) then (
2232 for SolSet in Solutions do (
2235 PrintMsg( 'SHORT, SolverMsg["SolSet"], i ),
2239 /* Extract the unsolved equations */
2240 UnsolvedEqs : sublist( SolSet, lambda( [x], not EquationP( x ) ) ),
2242 /* and the solutions. */
2243 SolSet : sublist( SolSet, 'EquationP ),
2245 /* If no complete backsubstitution is requested then variables on the */
2246 /* right-hand sides of the solutions will only be substituted if they */
2247 /* do not belong to the variables specified in the command line. */
2248 if not Solver_Backsubst then
2249 InternalSols : sublist(
2251 lambda( [x], not member( lhs( x ), UserVars ) )
2254 /* Evaluate all variables and expressions with the solutions. */
2255 for Var in append( UserVars, Expressions ) do (
2257 EvalVar : if member( Var, map(lhs, SolSet) ) or not atom( Var ) then
2259 /* There may be errors when indeterminate expressions are */
2263 if Solver_Backsubst then
2264 ev( Var, SolSet, infeval )
2266 TempVar : ev( Var, SolSet ),
2267 ev( TempVar, InternalSols, infeval )
2274 if EvalVar # [] then
2275 UsrSolSet : endcons( Var = EvalVar[1], UsrSolSet )
2277 PrintMsg( 'SHORT, SolverMsg["NoSol"], Var )
2281 /* append the unsolved equations. */
2282 if not Empty( UnsolvedEqs ) then
2283 UsrSolSet : endcons( UnsolvedEqs, UsrSolSet ),
2285 if Solver_RatSimp_Sols then
2286 UsrSolSet : errcatch( fullratsimp( UsrSolSet ) )
2288 UsrSolSet : [UsrSolSet],
2290 if UsrSolSet = [] then
2291 PrintMsg( 'SHORT, SolverMsg["SolSetDrp"] )
2293 UserSolutions : endcons( UsrSolSet[1], UserSolutions )
2295 ), /* END for SolSet */
2297 if Solver_Dump_To_File then
2298 DumpToFile( UserSolutions, [], [] )
2300 ), /* END if not Empty( Solutions ) */
2303 return( UserSolutions )
2309 /******************************************************************************/
2310 /* ExpandSolutionHierarchy transforms the hierarchically structured list of */
2311 /* solutions into a list of flat lists of solutions. */
2312 /******************************************************************************/
2314 ExpandSolutionHierarchy( Solutions ) := (
2327 if length( Solutions ) = 0 then return ( [] )
2328 /* listp = true indicates an additional recursion level */
2329 else if listp( last( Solutions ) ) then (
2331 FlatSolutions : rest( Solutions, -1 ),
2336 lambda( [ x ], append( FlatSolutions, x ) ),
2340 map( 'ExpandSolutionHierarchy, last( Solutions ) )
2349 return( [ Solutions ] )
2355 /******************************************************************************/
2356 /* DumpToFile dumps the current set of solutions, equations and variables to */
2357 /* the file <Solver_Dump_File>. */
2358 /******************************************************************************/
2360 DumpToFile( Sols, Eqs, Vars ) := (
2363 [ Sols, Eqs, Vars ], list
2367 [ Solutions, Equations, Variables ],
2369 PrintMsg( 'SHORT, SolverMsg["Dump"], Solver_Dump_File ),
2389 ImmediateAssignments,
2391 MakeSquareLinearBlocks,
2404 ExpandSolutionHierarchy,