REDUCE

7.16 SOLVE Operator

SOLVE is an operator for solving one or more simultaneous algebraic equations. It is used with the syntax:

SOLVE(EXPRN:algebraic[,VAR:kernel,VARLIST:list of kernels]) : list.

EXPRN is of the form expression or {expression1,expression2, …}. Each expression is an algebraic equation, or is the difference of the two sides of the equation. The second argument is either a kernel or a list of kernels representing the unknowns in the system. This argument may be omitted if the number of distinct, non-constant, top-level kernels equals the number of unknowns, in which case these kernels are presumed to be the unknowns.

For one equation, SOLVE recursively uses factorization and decomposition, together with the known inverses of LOG, SIN, COS, ^, ACOS, ASIN, and linear, quadratic, cubic, quartic, or binomial factors. Solutions of equations built with exponentials or logarithms are often expressed in terms of Lambert’s W function. This function is (partially) implemented in the special functions package.

Linear equations are solved by the multi-step elimination method due to Bareiss, unless the switch CRAMER is on, in which case Cramer’s method is used. The Bareiss method is usually more efficient unless the system is large and dense.

Non-linear equations are solved using the Groebner basis package (chapter 16.28). Users should note that this can be quite a time consuming process.

Examples:

            solve(log(sin(x+3))^5 = 8,x);  
            solve(a*log(sin(x+3))^5 - b, sin(x+3));  
            solve({a*x+y=3,y=-2},{x,y});

SOLVE returns a list of solutions. If there is one unknown, each solution is an equation for the unknown. If a complete solution was found, the unknown will appear by itself on the left-hand side of the equation. On the other hand, if the solve package could not find a solution, the “solution” will be an equation for the unknown in terms of the operator ROOT_OF. If there are several unknowns, each solution will be a list of equations for the unknowns. For example,

     solve(x^2=1,x);             -> {X=-1,X=1}  
 
     solve(x^7-x^6+x^2=1,x)  
                            6  
            -> {X=ROOT_OF(X_  + X_ + 1,X_,TAG_1),X=1}  
 
     solve({x+3y=7,y-x=1},{x,y}) -> {{X=1,Y=2}}.

The TAG argument is used to uniquely identify those particular solutions. Solution multiplicities are stored in the global variable ROOT_MULTIPLICITIES rather than the solution list. The value of this variable is a list of the multiplicities of the solutions for the last call of SOLVE. For example,

       solve(x^2=2x-1,x); root_multiplicities;

gives the results

        {X=1}  
 
        {2}

If you want the multiplicities explicitly displayed, the switch MULTIPLICITIES can be turned on. For example

        on multiplicities; solve(x^2=2x-1,x);

yields the result

        {X=1,X=1}

7.16.1 Handling of Undetermined Solutions

When SOLVE cannot find a solution to an equation, it normally returns an equation for the relevant indeterminates in terms of the operator ROOT_OF. For example, the expression

        solve(cos(x) + log(x),x);

returns the result

       {X=ROOT_OF(COS(X_) + LOG(X_),X_,TAG_1)} .

An expression with a top-level ROOT_OF operator is implicitly a list with an unknown number of elements (since we don’t always know how many solutions an equation has). If a substitution is made into such an expression, closed form solutions can emerge. If this occurs, the ROOT_OF construct is replaced by an operator ONE_OF. At this point it is of course possible to transform the result of the original SOLVE operator expression into a standard SOLVE solution. To effect this, the operator EXPAND_CASES can be used.

The following example shows the use of these facilities:

solve(-a*x^3+a*x^2+x^4-x^3-4*x^2+4,x);  
              2     3  
{X=ROOT_OF(A*X_  - X_  + 4*X_ + 4,X_,TAG_2),X=1}  
 
sub(a=-1,ws);  
 
{X=ONE_OF({2,-1,-2},TAG_2),X=1}  
 
expand_cases ws;  
 
{X=2,X=-1,X=-2,X=1}

7.16.2 Solutions of Equations Involving Cubics and Quartics

Since roots of cubics and quartics can often be very messy, a switch FULLROOTS is available, that, when off (the default), will prevent the production of a result in closed form. The ROOT_OF construct will be used in this case instead.

In constructing the solutions of cubics and quartics, trigonometrical forms are used where appropriate. This option is under the control of a switch TRIGFORM, which is normally on.

The following example illustrates the use of these facilities:

let xx = solve(x^3+x+1,x);  
 
xx;  
             3  
{X=ROOT_OF(X_  + X_ + 1,X_)}  
 
on fullroots;  
 
xx;  
 
                           - SQRT(31)*I  
                    ATAN(---------------)  
                            3*SQRT(3)  
{X=(I*(SQRT(3)*SIN(-----------------------)  
                              3

                      - SQRT(31)*I  
               ATAN(---------------)  
                       3*SQRT(3)  
        - COS(-----------------------)))/SQRT(3),  
                         3  
 
                              - SQRT(31)*I  
                       ATAN(---------------)  
                               3*SQRT(3)  
 X=( - I*(SQRT(3)*SIN(-----------------------)  
                                 3  
 
                         - SQRT(31)*I  
                  ATAN(---------------)  
                          3*SQRT(3)  
           + COS(-----------------------)))/SQRT(  
                            3  
 
      3),  
 
                  - SQRT(31)*I  
           ATAN(---------------)  
                   3*SQRT(3)  
    2*COS(-----------------------)*I  
                     3  
 X=----------------------------------}  
                SQRT(3)  
 
off trigform;  
 
xx;  
                             2/3  
{X=( - (SQRT(31) - 3*SQRT(3))   *SQRT(3)*I  
 
                             2/3    2/3  
     - (SQRT(31) - 3*SQRT(3))    - 2   *SQRT(3)*I  
 
        2/3                           1/3  1/3  
     + 2   )/(2*(SQRT(31) - 3*SQRT(3))   *6  
 
                1/6  
              *3   ),  
 
                          2/3  
 X=((SQRT(31) - 3*SQRT(3))   *SQRT(3)*I  
 
                             2/3    2/3  
     - (SQRT(31) - 3*SQRT(3))    + 2   *SQRT(3)*I  
 
        2/3                           1/3  1/3  
     + 2   )/(2*(SQRT(31) - 3*SQRT(3))   *6  
 
                1/6  
              *3   ),  
 
                           2/3    2/3  
     (SQRT(31) - 3*SQRT(3))    - 2  
 X=-------------------------------------}  
                          1/3  1/3  1/6  
    (SQRT(31) - 3*SQRT(3))   *6   *3

7.16.3 Other Options

If SOLVESINGULAR is on (the default setting), degenerate systems such as x+y=0, 2x+2y=0 will be solved by introducing appropriate arbitrary constants. The consistent singular equation 0=0 or equations involving functions with multiple inverses may introduce unique new indeterminant kernels ARBCOMPLEX(j), or ARBINT(j), (j=1,2,...), representing arbitrary complex or integer numbers respectively. To automatically select the principal branches, do off allbranch; . ALLBRANCH To avoid the introduction of new indeterminant kernels do OFF ARBVARS – then no equations are generated for the free variables and their original names are used to express the solution forms. To suppress solutions of consistent singular equations do OFF SOLVESINGULAR.

To incorporate additional inverse functions do, for example:

     put(’sinh,’inverse,’asinh);  
     put(’asinh,’inverse,’sinh);

together with any desired simplification rules such as

     for all x let sinh(asinh(x))=x, asinh(sinh(x))=x;

For completeness, functions with non-unique inverses should be treated as ^, SIN, and COS are in the SOLVE module source.

Arguments of ASIN and ACOS are not checked to ensure that the absolute value of the real part does not exceed 1; and arguments of LOG are not checked to ensure that the absolute value of the imaginary part does not exceed π; but checks (perhaps involving user response for non-numerical arguments) could be introduced using LET statements for these operators.

7.16.4 Parameters and Variable Dependency

The proper design of a variable sequence supplied as a second argument to SOLVE is important for the structure of the solution of an equation system. Any unknown in the system not in this list is considered totally free. E.g. the call

    solve({x=2*z,z=2*y},{z});

produces an empty list as a result because there is no function z = z(x,y) which fulfills both equations for arbitrary x and y values. In such a case the share variable REQUIREMENTS displays a set of restrictions for the parameters of the system:

    requirements;  
 
    {x - 4*y}

The non-existence of a formal solution is caused by a contradiction which disappears only if the parameters of the initial system are set such that all members of the requirements list take the value zero. For a linear system the set is complete: a solution of the requirements list makes the initial system solvable. E.g. in the above case a substitution x = 4y makes the equation set consistent. For a non-linear system only one inconsistency is detected. If such a system has more than one inconsistency, you must reduce them one after the other. 1 The set shows you also the dependency among the parameters: here one of x and y is free and a formal solution of the system can be computed by adding it to the variable list of solve. The requirement set is not unique – there may be other such sets.

A system with parameters may have a formal solution, e.g. 

     solve({x=a*z+1,0=b*z-y},{z,x});  
 
        y     a*y + b  
   {{z=---,x=---------}}  
        b        b

which is not valid for all possible values of the parameters. The variable ASSUMPTIONS contains then a list of restrictions: the solutions are valid only as long as none of these expressions vanishes. Any zero of one of them represents a special case that is not covered by the formal solution. In the above case the value is

    assumptions;  
 
    {b}

which excludes formally the case b = 0; obviously this special parameter value makes the system singular. The set of assumptions is complete for both, linear and non–linear systems.

SOLVE rearranges the variable sequence to reduce the (expected) computing time. This behavior is controlled by the switch VAROPT, which is on by default. If it is turned off, the supplied variable sequence is used or the system kernel ordering is taken if the variable list is omitted. The effect is demonstrated by an example:

   s:= {y^3+3x=0,x^2+y^2=1};  
 
   solve(s,{y,x});  
 
                  6       2  
   {{y=root_of(y_  + 9*y_  - 9,y_),  
 
            3  
         - y  
     x=-------}}  
          3  
 
   off varopt; solve(s,{y,x});  
 
                  6       4        2  
   {{x=root_of(x_  - 3*x_  + 12*x_  - 1,x_),  
 
               4      2  
        x*( - x  + 2*x  - 10)  
     y=-----------------------}}  
                  3  

In the first case, solve forms the solution as a set of pairs (yi,x(yi)) because the degree of x is higher – such a rearrangement makes the internal computation of the Gröbner basis generally faster. For the second case the explicitly given variable sequence is used such that the solution has now the form (xi,y(xi)). Controlling the variable sequence is especially important if the system has one or more free variables. As an alternative to turning off varopt, a partial dependency among the variables can be declared using the depend statement: solve then rearranges the variable sequence but keeps any variable ahead of those on which it depends.

   on varopt;  
   s:={a^3+b,b^2+c}$  
   solve(s,{a,b,c});  
 
                           3       6  
   {{a=arbcomplex(1),b= - a ,c= - a }}  
 
   depend a,c; depend b,c; solve(s,{a,b,c});  
 
   {{c=arbcomplex(2),  
 
                 6  
     a=root_of(a_  + c,a_),  
 
           3  
     b= - a }}

Here solve is forced to put c after a and after b, but there is no obstacle to interchanging a and b.