Uniform design

• To: mathgroup at smc.vnet.net
• Subject: [mg48066] Uniform design
• From: ab_def at prontomail.com (Maxim)
• Date: Mon, 10 May 2004 06:51:22 -0400 (EDT)
• Sender: owner-wri-mathgroup at wolfram.com

```In some cases the interface of Mathematica functions seems to be
poorly thought out. For example, what formats are allowed in the
equation solving functions? This seems to vary a lot:

In[1]:=
Solve[{{x, y} == {1, 1}}]
NSolve[{{x, y} == {1, 1}}]
DSolve[{{y'[x], y[0]} == {y[x], 1}}, y, x]
FindRoot[{{x, y} == {1, 1}}, {x, 2}, {y, 2}]
FindRoot[{{x, y} == {1, 1}}, {x, 2}, {y, 2},
Jacobian -> {{1, 0}, {0, 1}}]

Out[1]=
{{x -> 1, y -> 1}}

Solve::elist: List encountered during logical expansion of
{-1.000000000000000000000000000000<<101>>000000000000000000 + x,
<<1>>}.

Out[2]=
NSolve[{{x, y} == {1, 1}}]

DSolve::litarg: To avoid possible ambiguity, the arguments of the
dependent variable in {{y'[x], y[0]} == {y[x], 1}} should
literally match the independent variables.

Out[3]=
DSolve[{{y'[x], y[0]} == {y[x], 1}}, y, x]

FindRoot::njnum: The Jacobian is not a matrix of
numbers at{x, y} = {2., 2.}.

Out[4]=
{x -> 2., y -> 2.}

Out[5]=
{x -> 1., y -> 1.}

Solve and FindRoot thread Equal over lists, other functions don't.
FindRoot seems to forget to set up the jacobian correctly in that
case, though. Actually, sometimes DSolve accepts lists too, which
makes things worse:

In[6]:=
DSolve[{y'[x], y[x]} == {y[x], 0}, y, x]
DSolve[{y'[x], y[x]} == {y[x], 0} // Thread, y, x]

Out[6]=
{{y -> Function[{x}, C[1]]}}

DSolve::overdet: The system has fewer dependent variables
than equations, so is overdetermined.

Out[7]=
DSolve[{y'[x] == y[x], y[x] == 0}, y, x]

The second DSolve just fails to solve the trivial system, but the

It should be remembered that FindRoot and NDSolve can work with
vector-valued functions, but I think it's possible to distinguish
between x=={1,1} (which means that x is a vector) and equality between
objects which have the same dimensions. Trying to give 'vector' input
to other functions leads to further problems:

In[8]:=
DSolve[y'[x] == {1, 1}, y, x]
DSolve[y'[x] == {1, 2}, y, x]

Out[8]=
{{y -> Function[{x}, x + C[1]]}

Out[9]=
{}

The results are different, but it's hard to tell which one makes less
sense. Then there's the question of how Solve and NSolve determine the
variables to solve for:

In[10]:=
Solve[Sin[x]/x == 1]
NSolve[Sin[x]/x == 1]

Solve::tdep: The equations appear to involve the variables
to be solved for in an essentially non-algebraic way.

Out[10]=
Solve[Sin[x]/x == 1]

NSolve::infsolns: Infinite solution set has dimension at
least 1. Returning intersection of solutions with <<1>>.

Out[11]=
{{x -> 5.17616, Sin[x] -> 5.17616}}

Solve decides that there's only one variable x, NSolve treats x and
Sin[x] as separate variables. I know that the question of what should
be considered a variable can be turned into a scholastic discussion, I
just wish that Solve/NSolve treated this issue in a more uniform way.

And that's still not all, there's a lot of similar issues: can the
list of equations include True? (Yes for Solve, no for other
functions.) Can Sin[x] be used as a variable? (Yes for Solve/NSolve,
no for FindRoot.) Such discrepancies will inevitably lead to problems
which are much harder to trace if they appear in the program code:

In[12]:=
Reduce[x > 0, {x, x}]

Out[12]=
False

Apparently, one of the reasons for this kind of problems is the
absence of standard routines that would handle the input -- analyze
the structure of the equations set, find independent variables, and so
on. If every Mathematica function has to implement the parsing
separately, then it's impossible to achieve uniform behaviour. Authors
of Mathematica add-on packages are also on their own here.

But even apart from having such 'SDK' routines, there are other ways
to alleviate this problem: first, to describe the expected input
format more clearly in the documentation; second, to provide the user
with better diagnostics -- messages like DSolve::litarg in the
examples above don't explain much. If you try FindRoot[{x==1,True},
{x,0}] in version 5.0, you'll get an especially helpful message: