How to compute and evaluate composite function in GP? - pari-gp

I found a workaround to make composite function, but I believe there should be a better way to do this:
? f = x^2
%1 = x^2
? g = x^3
%2 = x^3
? x = g
%3 = x^3
? fog = eval(f)
%4 = x^6
? x = 2
%5 = 2
? result = eval(fog)
%6 = 64
In this method, I need to assign x many times and I don't want to use eval function. The code is not readable and maintainable.

You can simplify Piotr's nice answer to
comp(f, g) = x->f(g(x));
Indeed, you do not need to assign to the (global) variable h in the comp function itself. Also, the braces are not necessary for a single-line statement, and neither are type annotations (which are meant to optimize the byte compiler output or help gp2c; in this specific case they do not help).
Finally the parentheses around the argument list are optional in the closure definition when there is a single argument, as (x) here.
I would modify the examples as follows
f(x) = x^2;
g(x) = x^3;
h = comp(f, g);
? h('x) \\ note the backquote
%1 = x^6
? h(2)
%2 = 64
The backquote in 'x makes sure we use the formal variable x and not whatever value was assigned to the GP variable with that name. For the second example, there is no need to assign the value 2 to x, we can call h(2) directly
P.S. The confusion between formal variables and GP variables is unfortunate but very common for short names such as x or y. The quote operator was introduced to avoid having to kill variables. In more complicated functions, it can be cumbersome to systematically type 'x instead of x. The idiomatic construct to avoid this is my(x = 'x). This makes sure that the x GP variable really refers to the formal variable in the current scope.

PARI/GP supports the anonymous closures. So you can define the function composition on your own like this:
comp(f: t_FUNC, g: t_FUNC) = {
h = (x) -> f(g(x))
};
Then your code can be transformed to a more readable form:
f(x) = x^2;
g(x) = x^3;
h = comp(f, g);
h(x)
? x^6
x = 2; h(x)
? 64
Hope, this helps.

Related

.= operator in Julia

In the code taken from: https://tutorials.sciml.ai/html/models/01-classical_physics.html
as given below:
# Simple Harmonic Oscillator Problem
using OrdinaryDiffEq, Plots
# Parameters
ω = 1
# Initial Conditions
x₀ = [0.0]
dx₀ = [π/2]
tspan = (0.0, 2π)
ϕ = atan((dx₀[1]/ω)/x₀[1])
A = √(x₀[1]^2 + dx₀[1]^2)
# Define the problem
function harmonicoscillator(ddu,du,u,ω,t)
ddu .= -ω^2 * u
end
# Pass to solvers
prob = SecondOrderODEProblem(harmonicoscillator, dx₀, x₀, tspan, ω)
sol = solve(prob, DPRKN6())
# Plot
plot(sol, vars=[2,1], linewidth=2, title ="Simple Harmonic Oscillator", xaxis = "Time", yaxis = "Elongation", label = ["x" "dx"])
plot!(t->A*cos(ω*t-ϕ), lw=3, ls=:dash, label="Analytical Solution x")
plot!(t->-A*ω*sin(ω*t-ϕ), lw=3, ls=:dash, label="Analytical Solution dx")
I don't understand the usage of .= operator in the function harmonicoscillator. Using = gives me the wrong answer. So, I am wondering how is .= different from =? It is not vectorizing ddu because RHS is all scalar.
I don't understand the usage of .= operator in the function
harmonicoscillator. [...] It is not vectorizing ddu because RHS is all scalar.
It is; u, du, and ddu are not scalars, they are length-1 vectors.
You can ask Julia what the .= syntax means:
julia> Meta.#lower a .= b
:($(Expr(:thunk, CodeInfo(
# none within `top-level scope'
1 ─ %1 = Base.broadcasted(Base.identity, b)
│ %2 = Base.materialize!(a, %1)
└── return %2
))))
which looks a bit involved, but it is essentially a broadcasted assignment, similar to
for i in eachindex(a, b)
a[i] = b[i]
end
Using = gives me the wrong answer.
Yes, because the DiffEq library expects the function harmonicoscillator to modify the input. If you use just = you create a new variable local to that function rather than modifying the input vector, and that is not visible from the outside.

Unable to plot solution of ODE in Maxima

Good time of the day!
Here is the code:
eq:'diff(x,t)=(exp(cos(t))-1)*x;
ode2(eq,x,t);
sol:ic1(%,t=1,x=-1);
/*---------------------*/
plot2d(
rhs(sol),
[t,-4*%pi, 4*%pi],
[y,-5,5],
[xtics,-4*%pi, 1*%pi, 4*%pi],
[ytics, false],
/*[yx_ratio , 0.6], */
[legend,"Solution."],
[xlabel, "t"], [ylabel, "x(t)"],
[style, [lines,1]],
[color, blue]
);
and here is the errors:
integrate: variable must not be a number; found: -12.56637061435917
What went wrong?
Thanks.
Here's a way to plot the solution sol which was found by ode2 and ic2 as you showed. First replace the integrate nouns with calls to quad_qags, a numerical quadrature function. I'll introduce a made-up variable name (a so-called gensym) to avoid confusion with the variable t.
(%i59) subst (nounify (integrate) =
lambda ([e, xx],
block ([u: gensym(string(xx))],
quad_qags (subst (xx = u, e), u, -4*%pi, xx)[1])),
rhs(sol));
(%o59) -%e^((-t)-quad_qags(%e^cos(t88373),t88373,-4*%pi,t,
epsrel = 1.0E-8,epsabs = 0.0,
limit = 200)[
1]
+quad_qags(%e^cos(t88336),t88336,-4*%pi,t,
epsrel = 1.0E-8,epsabs = 0.0,
limit = 200)[
1]+1)
Now I'll define a function foo1 with that result. I'll make a list of numerical values to see if it works right.
(%i60) foo1(t) := ''%;
(%o60) foo1(t):=-%e
^((-t)-quad_qags(%e^cos(t88373),t88373,-4*%pi,t,
epsrel = 1.0E-8,epsabs = 0.0,
limit = 200)[
1]
+quad_qags(%e^cos(t88336),t88336,-4*%pi,t,
epsrel = 1.0E-8,epsabs = 0.0,
limit = 200)[
1]+1)
(%i61) foo1(0.5);
(%o61) -1.648721270700128
(%i62) makelist (foo1(t), t, makelist (k, k, -10, 10));
(%o62) [-59874.14171519782,-22026.46579480672,
-8103.083927575384,-2980.957987041728,
-1096.633158428459,-403.4287934927351,
-148.4131591025766,-54.59815003314424,
-20.08553692318767,-7.38905609893065,-2.71828182845904,
-1.0,-0.3678794411714423,-0.1353352832366127,
-0.04978706836786394,-0.01831563888873418,
-0.006737946999085467,-0.002478752176666358,
-9.118819655545163E-4,-3.354626279025119E-4,
-1.234098040866796E-4]
Does %o62 look right to you? I'll assume it is okay. Next I'll define a function foo which calls foo1 defined before when the argument is a number, otherwise it just returns 0. This is a workaround for a bug in plot2d, which incorrectly determines that foo1 is not a function of t alone. Usually that workaround isn't needed, but it is needed in this case.
(%i63) foo(t) := if numberp(t) then foo1(t) else 0;
(%o63) foo(t):=if numberp(t) then foo1(t) else 0
Okay, now the function foo can be plotted!
(%i64) plot2d (foo, [t, -4*%pi, 4*%pi], [y, -5, 5]);
plot2d: some values were clipped.
(%o64) false
That takes about 30 seconds to plot -- calling quad_qags is relatively expensive.
it looks like ode2 does not know how to completely solve the problem, so the result contains an integral:
(%i6) display2d: false $
(%i7) eq:'diff(x,t)=(exp(cos(t))-1)*x;
(%o7) 'diff(x,t,1) = (%e^cos(t)-1)*x
(%i8) ode2(eq,x,t);
(%o8) x = %c*%e^('integrate(%e^cos(t),t)-t)
(%i9) sol:ic1(%,t=1,x=-1);
(%o9) x = -%e^((-%at('integrate(%e^cos(t),t),t = 1))
+'integrate(%e^cos(t),t)-t+1)
I tried it with contrib_ode also:
(%i12) load (contrib_ode);
(%o12) "/Users/dodier/tmp/maxima-code/share/contrib/diffequations/contrib_ode.mac"
(%i13) contrib_ode (eq, x, t);
(%o13) [x = %c*%e^('integrate(%e^cos(t),t)-t)]
So contrib_ode did not solve it completely either.
However the solution returned by ode2 (same for contrib_ode) appears to be a valid solution. I'll post a separate answer describing how to evaluate it numerically for plotting.

How can I write higher-order function to add methods to a given function?

Let's suppose I want to write a function that accepts any associative operator ⊕ and adds methods to it such that I can replace any value with a function. The semantics of these additional methods are as follows:
If the operator is then applied to any two functions f and g, the result should be a function that first applies f and g (independently) to its arguments and then applies ⊕ to the results.
If one argument is a function f but the other is any non-function value x, the result is a function that first applies f to its arguments and then applies ⊕ to the result and x.
I can express this in code as:
associative!(⊕) = begin
⊕(f::F, y) where F<:Function = (xs...) -> f(xs...) ⊕ y
⊕(x, g::G) where G<:Function = (xs...) -> x ⊕ g(xs...)
⊕(f::F, g::G) where {F<:Function, G<:Function} = (args...) -> f(args...) ⊕ g(args...)
⊕(f::F, y, zs...) where F<:Function = f ⊕ ⊕(y, zs...)
⊕(x, g::G, zs...) where G<:Function = x ⊕ ⊕(g, zs...)
⊕(f::F, g::G, zs...) where {F<:Function, G<:Function} = f ⊕ ⊕(g, zs...)
end
However, when I try to compile this function I get the following error:
ERROR: syntax: cannot add method to function argument ⊕
I know I can write a higher-order function that returns a new function / operator that is based on the one given. For example:
associative(⊞) = begin
let ⊕(x) = x
⊕(x, y) = x ⊞ y
⊕(x, y, zs...) = ⊕(x⊞y, zs...)
associative!(⊕)
⊕
end
end
If I inline the definition of associative! here then associative works just fine, and I can write:
⊕ = associative(+)
⊗ = associative(*)
f(x) = 3⊗x ⊕ 1
f(1) # 4
f(cos)(0) # 4
But I thought it would be nice to have a mutating version as well. I assume re-writing associate! as a macro would work, but there really doesn't seem to be anything that would necessitate the use of a macro here. So, is it possible to do this as a higher-order function and, if so, how?
I don't know how to write your symbol but doesn't this get you someway towards what you want?
import Base.+
+(f::Function, g::Function) = x -> f(x) + g(x)
+(f::Function, x) = f(x) + x
# e.g.
new_plus = +(sum, sum)
new_plus([1,2,3]) # gives 12
There are a few things going on here. The first thing to note is that the first instance of ⊕(<args...>) = <body...> will create a new function and bind it to ⊕. However, it apparently does not create a new binding for ⊕. I'm guessing that's why the error message mentions ⊕ is a function argument, but this is a red herring anyway — it would simply create a new function, rather than mutate the existing one.
Toward the end of this thread, we see our first hint at a solution. In order to dynamically add a method to a function alias, we basically need to use the same syntax we'd use to define a callable object for some arbitrary datatype:
(::typeof(⊕))(<args...>) = <body...>
With this change, we get a different error:
Global method definition around ... needs to be placed at the top level, or use "eval"
A little further in the same thread, we see that this is due a change sometime after Julia 0.6, and that we now need to #eval these expressions. Now that we're involving #eval, we need to keep the following in mind:
We need to splice our references to ⊕ using $⊕
The $ in front of ⊕ seems to interfere with Julia recognizing it as an infix operator. So we need to use the functional (prefix) notation instead.
Putting this together, we end up with something like:
#eval (::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> $⊕(f(xs...), y)
If we really want to use infix notation instead, we can use a let-binding inside #eval:
#eval let ⊕ = $⊕
(::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> f(xs...) ⊕ y
.
.
.
end

How do I evaluate the function in only one of its variables in Scilab

How do I evaluate the function in only one of its variables, that is, I hope to obtain another function after evaluating the function. I have the following piece of code.
deff ('[F] = fun (x, y)', 'F = x ^ 2-3 * y ^ 2 + x * y ^ 3');
fun (4, y)
I hope to get 16-3y ^ 2 + 4y ^ 3
If what you want to do is to write x = f(4,y), and later just do x(2) to get -36, that is called partial application:
Intuitively, partial function application says "if you fix the first arguments of the function, you get a function of the remaining arguments".
This is a very useful feature, and very common Functional Programming Languages, such as Haskell, but even JS and Python now are able to do it. It is also possible to do this in MATLAB and GNU/Octave using anonymous functions (see this answer). In Scilab, however, this feature is not available.
Workround
Nonetheless, Scilab itself uses a workarounds to carry a function with its arguments without fully evaluating. You see this being used in ode(), fsolve(), optim(), and others:
Create a list containing the function and the arguments to partial evaluation: list(f,arg1,arg2,...,argn)
Use another function to evaluate such list and the last argument: evalPartList(list(...),last_arg)
The implementation of evalPartList() can be something like this:
function y = evalPartList(fList,last_arg)
//fList: list in which the first element is a function
//last_arg: last argument to be applied to the function
func = fList(1); //extract function from the list
y = func(fList(2:$),last_arg); //each element of the list, from second
//to last, becomes an argument
endfunction
You can test it on Scilab's console:
--> deff ('[F] = fun (x, y)', 'F = x ^ 2-3 * y ^ 2 + x * y ^ 3');
--> x = list(fun,4)
x =
x(1)
[F]= x(1)(x,y)
x(2)
4.
--> evalPartList(x,2)
ans =
36.
This is a very simple implementation for evalPartList(), and you have to be careful not to exceed or be short on the number of arguments.
In the way you're asking, you can't.
What you're looking is called symbolic (or formal) computational mathematics, because you don't pass actual numerical values to functions.
Scilab is numerical software so it can't do such thing. But there is a toolbox scimax (installation guide) that rely on a the free formal software wxmaxima.
BUT
An ugly, stupid but still sort of working solution is to takes advantages of strings :
function F = fun (x, y) // Here we define a function that may return a constant or string depending on the input
fmt = '%10.3E'
if (type(x)==type('')) & (type(y)==type(0)) // x is string is
ys = msprintf(fmt,y)
F = x+'^2 - 3*'+ys+'^2 + '+x+'*'+ys+'^3'
end
if (type(y)==type('')) & (type(x)==type(0)) // y is string so is F
xs = msprintf(fmt,x)
F = xs+'^2 - 3*'+y+'^2 + '+xs+'*'+y+'^3'
end
if (type(y)==type('')) & (type(x)==type('')) // x&y are strings so is F
F = x+'^2 - 3*'+y+'^2 + '+x+'*'+y+'^3'
end
if (type(y)==type(0)) & (type(x)==type(0)) // x&y are constant so is F
F = x^2 - 3*y^2 + x*y^3
end
endfunction
// Then we can use this 'symbolic' function
deff('F2 = fun2(y)',' F2 = '+fun(4,'y'))
F2=fun2(2) // does compute fun(4,2)
disp(F2)

How to do string interpolation within a given context?

Is there a macro f that allows to apply string interpolation within a given context?
#f("abc$x", x=3) == "abc3"
Or maybe a function g
g("abc\$x", x=3)
You can introduce a new context with a let block. Here is a macro that does that:
macro f(s, args...)
args = [:($(esc(a.args[1])) = $(esc(a.args[2]))) for a in args]
quote
let $(args...)
$(esc(s))
end
end
end
z = 5
x = 1
#f("abc$x, $(2y), $z", x=3, y = 2x)
# "abc3, 12, 5"
Note the difference to a function, where y = 2x would refer to x in the scope of the caller, i.e., to x=1. So I'm not sure if this is what you need.

Resources