grzegorzmazur / yacas

Computer calculations made easy
http://www.yacas.org
GNU Lesser General Public License v2.1
120 stars 23 forks source link

Function application seem to evaluate before application. #315

Open Isomorph70 opened 4 years ago

Isomorph70 commented 4 years ago

Following test show the problem. D({x,y} will be applied to node instead of 9*x^8.

Verify(MapSingle({{node},D({x,y}) node},{9x^8,0}),{D({x,y}) 9x^8,D({x,y}) 0});

Verify(MapSingle(Lambda({{node},D({x,y}) node}),{9x^8,0}),{D({x,y}) 9x^8,D({x,y}) 0});

Isomorph70 commented 4 years ago

Ops, an error in coping the examples. The correct ones is.

Verify(MapSingle({{node},D({x,y}) node},{9*x^8,0}),{D({x,y}) 9*x^8,D({x,y}) 0});

Verify(MapSingle(Lambda({{node},D({x,y}) node}),{9*x^8,0}),{D({x,y}) 9*x^8,D({x,y}) 0});

Isomorph70 commented 4 years ago

Seem like githubs text rendering remove multiplication signs.

grzegorzmazur commented 4 years ago

Hi,

I agree that this is a bit disappointing result. But it is actually for a reason, even though not a very important one. The D() operator is a macro, so it's application is a bit more convoluted. Fortunately, it's built around much simpler Deriv() operator, which in your case can be used like

In> MapSingle(Lambda({node},{Deriv(x) node, Deriv(y) node}),{9*x^8,0})
Out> {{72*x^7,0},{0,0}}

It this what you're actually after? Does the workaround lokk acceptable?

Cheers, Grzesiek

Isomorph70 commented 4 years ago

I am basically trying to make a rule that take in a list of variables vl with n elements and a list of expressions el with m elements and then derive on each expression. Like {{Deriv(vl[1]) el[1],,,Derive(vl[n]) el[1]},.........,{Deriv(vl[1]) el[m],...,Deriv(vl[n]) el[m]}}.

grzegorzmazur commented 4 years ago

OK, then how about

derivs(vars_IsList, exprs_IsList) <--
    MapSingle(Lambda({e}, MapSingle(Lambda({v}, Deriv(v) e), vars)), exprs);

Would this work for you?

grzegorzmazur commented 4 years ago

Alternatively, risking the wrath of functional programming Gods

derivs(vars_IsList, exprs_IsList) <-- [
    Local(e, v, row, result);
    result := {};
    ForEach(e, exprs) [
        row := {};
        ForEach(v, vars)
        DestructiveAppend(row, Deriv(v) e);
    DestructiveAppend(result, row);
    ];
    result;
];
Isomorph70 commented 4 years ago

Something more like this will be optimal. 10 # derivs(vars_IsList, expr)\(Not IsList(expr)) <-- MapSingle(Lambda({v}, Deriv(v) expr), vars); 20 # derivs(vars_IsList, exprs_IsList) <-- MapSingle(Lambda({e}, derivs(vars,e)), exprs);

Isomorph70 commented 4 years ago

Or in full functional mode. 10 # derivs(_vars, exprs_IsList) <-- If(Length(exprs)=0,{},derivs(vars,Head(exprs)):derivs(vars,Tail(exprs))); 20 # derivs(vars_IsList, _expr) <-- If(Length(vars)=0,{},(derivs(Head(vars),expr)):derivs(Tail(vars),expr)); 30 # derivs(_var,_expr) <-- Deriv(var) expr;

Isomorph70 commented 4 years ago

Also, I have been working on a multidimensional Taylor expansion, that I can contribute to the project.

grzegorzmazur commented 4 years ago

That's awesome, thanks! :)

And obviously the code you suggest is better then mine. The only possible improvement I can see is providing additional rules instead of using If(), like

5 # derivs(_vars, {}) <-- {};
10 # derivs(_vars, exprs_IsList) <-- derivs(vars,Head(exprs)):derivs(vars,Tail(exprs));
15 # derivs({}, _expr) <-- {};
20 # derivs(vars_IsList, _expr) <-- derivs(Head(vars),expr):derivs(Tail(vars),expr);
30 # derivs(_var,_expr) <-- Deriv(var) expr;

but I admit that it's a minor change and based more on personal preference than anything else.

Cheers, Grzesiek

Isomorph70 commented 4 years ago

Perfect... Here are some tests, that our new derivs can do, but D can't.

Verify([Local(vl);vl :={x,y};derivs(vl, x+c);],derivs({x,y}, x+c)); Verify(MapSingle(Lambda({node},derivs({x,y},node)),{9*x^8,0}),{derivs({x,y}, 9*x^8),derivs({x,y},0)});

Verify([Local(vl);vl :={x,y};D(vl) x+c;],D({x,y}) x+c); Verify(MapSingle(Lambda({node},D({x,y}) node),{9*x^8,0}),{D({x,y}) 9*x^8,D({x,y}) 0});

grzegorzmazur commented 4 years ago

The basic issue with D() is that it has shady semantics, definitely to complex for its own good. It's easy to use for some basic operations, but for anything more involved it fails miserably. If ever, it should be used only by an end user to do something simple ad hoc. Definitely it has no place in any implementation of other mechanisms.

On the other hand the semantics you suggest for what is now called derivs() looks good. I think it'd be reasonable to incorporate it into Derivs(), which till now has only basic capabilities, but does them reasonably well.

What do you think about it?

Isomorph70 commented 4 years ago

Yes, We better integrate it into Deriv(), so everyone can use it.