Bill,
Thank you for following up on this.
This is a nice illustration of how to extend pan-Axiom to deal with a
specific case. One of the advantages of algorithmic differentiation (AD)
is that we can rapidly differentiate across a range of operators and
expressions. One could re-write the previous example using an iterative
function:
)set message time on
g(x) ==
expr := 1+x
for i in 1..10000 repeat
expr := expr*(1+x)
expr
eval(D(g(x),x),x=0.1) -- very slow
g(jet(0.1)) -- fast
)set message time off
The symbolic solution takes about 30 seconds on my laptop, while the AD
solution was 500-1000 times faster.
I have extended the "Jet" package: to include some more operators; allow
for coersion from T to Jet(T); and defined the export category as
Join(PartialOrder,IntegralDomain), which now supports eval(). One can
now write:
reduce(+, vector [1,2,3*jet(3)])
-- and
eval(x^exp(x)^exp(x), x=jet(0.1))
-- Mark
On 03/20/2017 06:46 PM, Bill Page wrote:
> Oops, I forgot to add one line of output
>
> (3) -> distribute(%)::Float
>
> (3) 0.7681727501_0912254214 E 418
> Type: Float
> Time: 0 sec
>
> 'distribute' is the operator that "undoes" 'paren'.
>
> On 20 March 2017 at 13:32, Bill Page <bill.p...@newsynthesis.org> wrote:
>> The problem is just the expansion of (1+x)^10000.
>>
>> (1) -> )set message time on
>>
>> (1) -> g(x) == (1+x)^10000
>> Type: Void
>> Time: 0 sec
>> (2) -> eval(D(g(x),x), x=0.1)
>> Compiling function g with type Variable x -> Polynomial Integer
>>
>> Type: Polynomial Float
>> Time: 3.09 (IN) + 6.74 (EV) + 0.06 (OT) = 9.89 sec
>>
>> In FriCAS and OpenAxiom (probably Axiom too) the correct way to avoid
>> this is to treat (1+x) as a kernel via the identity operator 'paren'.
>>
>> (3) -> g(x) == paren(1+x)^10000
>> Compiled code for g has been cleared.
>> 1 old definition(s) deleted for function or rule g
>> Type: Void
>> Time: 0 sec
>> (4) -> eval(D(g(x),x), x=0.1)
>> Compiling function g with type Variable x -> Expression Integer
>> 9999 ,
>> (4) 10000.0 (1.1) %paren (1.1)
>>
>> Type: Expression Float
>> Time: 0.09 (IN) + 0.05 (OT) = 0.14 sec
>>
>> The only trouble here is that neither FriCAS nor OpenAxiom know how to
>> differentiate this operator. This can be fixed with the following
>> patch:
>>
>> https://github.com/billpage/fricas/commit/04c1f9b2d5eb4ece95c6f5dded46a72181cecb06
>> https://github.com/billpage/fricas/commit/04c1f9b2d5eb4ece95c6f5dded46a72181cecb06.patch
>>
>> (1) -> g(x) == paren(1+x)^10000
>> Type: Void
>> Time: 0 sec
>> (2) -> eval(D(g(x),x), x=0.1)
>> Compiling function g with type Variable(x) -> Expression(Integer)
>>
>> 9999
>> (2) 10000.0 (1.1)
>> Type: Expression(Float)
>> Time: 0.04 (IN) + 0.00 (EV) + 0.12 (OT) = 0.16 sec
>>
)abbrev domain JET Jet
Jet(T: Join(PartialOrder,Ring)): Public == Private where
Public ==> Join(PartialOrder,IntegralDomain) with
jet: (T, T) -> %
++ construct a jet value from a pair
jet: (T) -> %
++ construct a jet value with delta=1
elt: (%, "value") -> T
++ retrieve the value field of a jet
elt: (%, "delta") -> T
++ retrieve the delta field of a jet
setelt: (%, "value", T) -> T
++ set the value field of a jet
setelt: (%, "delta", T) -> T
++ set the delta field of a jet
coerce: T -> %
++ coerce from T to %
Zero: () -> %
++ zero element
One: () -> %
++ one element
if T has PartialOrder then PartialOrder
if T has Comparable then Comparable
if T has OrderedRing then OrderedRing
if T has RetractableTo Integer then RetractableTo Integer
if T has RetractableTo Fraction Integer then RetractableTo Fraction Integer
if T has Field then
"exquo": (%,%) -> Union(%,"failed")
"/": (%, %) -> %
"/": (T, %) -> %
"/": (%, T) -> %
"^": (%, Integer) -> %
"^": (%, Fraction Integer) -> %
if T has TranscendentalFunctionCategory and T has Field and T has
RadicalCategory then
-- RadicalCategory and Field are *not* required for differentiating all
of TranscendentalFunctionCategory;
-- this is done for conciseness.
TranscendentalFunctionCategory
"^": (T, %) -> %
"^": (%, T) -> %
if T has RadicalCategory and T has Field then
sqrt: % -> %
if FloatLiouvilianFunctions has (erf:T->T) and T has FloatingPointSystem
and T has TranscendentalFunctionCategory then
erf: % -> %
++ Error function
pnorm: (%) -> %
++ pnorm(x) is the CDF at x for a standard normal distribution with a
mean 0 and variance 1
pnorm: (%, %, %) -> %
++ pnorm(x,mu,sigma) is the CDF at x for a normal distribution with a
mean mu and variance sigma^2
Private ==> add
x, y : %
z : T
n : Integer
Rep := Record(val: T, der: T)
jet(v,d) == [v, d]
jet(v) == jet(v, 1$T) -- is this the right default?
coerce(z) == jet(z, 0$T)
coerce(n) == jet(n::T, 0$T)
elt(jet: %, key: "value") == jet.val
elt(jet: %, key: "delta") == jet.der
setelt(jet: %, key: "value", jetValue: T) == jet.val := jetValue
setelt(jet: %, key: "delta", jetDelta: T) == jet.der := jetDelta
-- if T has Ring then
Zero() == jet(0$T, 0$T)
One() == jet(1$T, 0$T)
x + y == jet(x.value + y.value, x.delta + y.delta)
x - y == jet(x.value - y.value, x.delta - y.delta)
x * y == jet(x.value * y.value, x.delta*y.value + x.value*y.delta)
n * y == jet(n * y.value, n*y.delta)
-x == jet(-x.value, -x.delta)
if T has Comparable then
x = y == x.value = y.value and x.delta=y.delta
hash x == hash(x.value)+hash(x.delta)
coerce(x):OutputForm ==
hconcat(('jet)::OutputForm, paren [ (x.value)::OutputForm,
(x.delta)::OutputForm ])
latex x ==
concat ["jet(", latex(x.value), "," latex(x.delta), ")"]
if T has PartialOrder then
smaller?(x,y) == x.value<y.value or (x.value=y.value and
x.delta<y.delta)
else
smaller?(x,y) == false
if T has PartialOrder then
x <= y == x.value <= y.value or (x.value = y.value and x.delta <= y.delta)
if T has OrderedRing then
abs x == jet(abs(x.value), sign(x.value)*x.delta)
if T has RetractableTo Integer then
coerce(n:Integer):% == jet(n::T,0$T)
retract(x):Integer == retract(x.value)@Integer
retractIfCan(x):Union(Integer,"failed") ==
retractIfCan(x.value)@Union(Integer,"failed")
if T has RetractableTo Fraction Integer then
coerce(f:Fraction Integer):% == jet(f::T,0$T)
retract(x):Fraction Integer == retract(x.value)@Fraction(Integer)
retractIfCan(x):Union(Fraction Integer,"failed") ==
retractIfCan(x.value)@Union(Fraction Integer,"failed")
if T has Field and T has RetractableTo Integer and T has RetractableTo
Fraction Integer then
x exquo y ==
y.value=0 => "failed"
x/y
x / y ==
r : T := x.value / y.value
jet(r, (x.delta - r * y.delta) / y.value)
x / z ==
jet(x.value / z, x.delta / z)
z / x ==
jet(z / x.value, -z*x.delta / x.value / x.value)
x ^ (n:Integer) ==
jet(x.value ^ n, n*x.value^(n-1)*x.delta)
if T has TranscendentalFunctionCategory and T has Field and T has
RadicalCategory and T has RetractableTo Fraction Integer then
x ^ (frac:Fraction Integer) == x^(frac::T)
if T has TranscendentalFunctionCategory and T has Field and T has
RadicalCategory then
-- extras
z ^ x == jet(z ^ x.value, log(z)*z^x.value*x.delta)
x ^ z == jet(x.value ^ z, x.delta*z*x.value^(z-1))
-- TranscendentalFunctionCategory
x ^ y == jet(x.value ^ y.value, log(x.value)*x.value^y.value*y.delta +
x.delta*y.value*x.value^(y.value-1))
acos x == jet(acos(x.value), -x.delta/sqrt(1-x.value*x.value))
acosh x == jet(acosh(x.value), x.delta/sqrt(x.value*x.value-1))
acot x == jet(acot(x.value), -x.delta/(1+x.value*x.value))
acoth x == jet(acoth(x.value), -x.delta/(x.value*x.value-1))
acsc x == jet(acsc(x.value), -x.delta/(x.value*sqrt(x.value*x.value-1)))
acsch x == jet(acsch(x.value), -x.delta/(x.value*sqrt(x.value*x.value+1)))
asec x == jet(asec(x.value), x.delta/(x.value*sqrt(x.value*x.value-1)))
asech x == jet(asech(x.value), -x.delta/(x.value*sqrt(1-x.value*x.value)))
asin x == jet(asin(x.value), x.delta/sqrt(1-x.value*x.value))
asinh x == jet(asinh(x.value), x.delta/sqrt(1+x.value*x.value))
atan x == jet(atan(x.value), x.delta/(1+x.value*x.value))
atanh x == jet(atanh(x.value), -x.delta/(x.value*x.value-1))
cos x == jet(cos(x.value), -x.delta*sin(x.value))
cosh x == jet(cosh(x.value), x.delta*sinh(x.value))
cot x == jet(cot(x.value), -x.delta*(1+cot(x.value)*cot(x.value)))
coth x == jet(coth(x.value), x.delta*(1-coth(x.value)*coth(x.value)))
csc x == jet(csc(x.value), -x.delta*csc(x.value)*cot(x.value))
csch x == jet(csch(x.value), -x.delta*csch(x.value)*coth(x.value))
exp x == jet(exp(x.value), x.delta*exp(x.value))
log x == jet(log(x.value), x.delta/x.value)
pi() == jet(pi()$T,0$T)
sec x == jet(sec(x.value), x.delta*sec(x.value)*tan(x.value))
sech x == jet(sech(x.value), -x.delta*sech(x.value)*tanh(x.value))
sin x == jet(sin(x.value), x.delta*cos(x.value))
sinh x == jet(sinh(x.value), x.delta*cosh(x.value))
tan x == jet(tan(x.value), x.delta*(1+tan(x.value)*tan(x.value)))
tanh x == jet(tanh(x.value), x.delta*(1-tanh(x.value)*tanh(x.value)))
if T has RadicalCategory and T has Field then
sqrt x == jet(sqrt(x.value), x.delta/sqrt(x.value)/(1+1))
if FloatLiouvilianFunctions has (erf:T->T) and T has FloatingPointSystem
and T has TranscendentalFunctionCategory then
import FloatLiouvilianFunctions
import Pi
erf x == jet(erf(x.value)$FloatLiouvilianFunctions,
(1+1)*exp(-x.value*x.value)*x.delta/sqrt(pi()))
pnorm x == (1 + erf(x/sqrt(1+1)))/(1+1)
pnorm(x,mu,sigma) == pnorm((x-mu)/sigma)
)if false
)cd /home/marcle/Home/axiom
)co Jet2.spad
JF ==> Jet Float
-- constructors
jet(1.0,2.0)
jet(1.0)
0$Jet Float
latex 1$Jet DoubleFloat
0$Jet Float < 1$Jet Float
jet(1.0) exquo 0
jet(2.0,1.0)<=jet(2.0,1.0)
jet(1,2) <= jet(1,3)
-- some functions
cos(jet(1.0)*pi())
erf(jet(2.0))
2*jet(2)
log(jet(2.0))
pnorm(jet(1.96))
pnorm(1+2*jet(1.96),1,2)
-- coerce and use vectors
reduce(+, vector [1,2,3*jet(3)]) -- cool!
reduce(+, vector [1,2,3*jet(3.0)]) -- cool!
vector [1,2,3] :: Vector Jet Float -- ok
-- testing
testDeriv(f,val) ==
fjet : Jet Float := f(jet(val))
(f(val)-fjet.value, eval(D(f(x),x),x=val) - fjet.delta)
testDeriv(log,2.0)
testDeriv(exp,2.0)
testDeriv(-,2.0)
testDeriv(x +-> exp(exp(x)),2.0)
testDeriv(sin,2.0)
testDeriv(cos,2.0)
testDeriv(tan,2.0)
testDeriv(sinh,2.0)
testDeriv(cosh,2.0)
testDeriv(tanh,2.0)
testDeriv(asin,0.1)
testDeriv(acos,0.1)
testDeriv(atan,0.1)
testDeriv(x +-> x^(2*x),2.0)
testDeriv(x +-> x^(x*2),2.0)
testDeriv(x +-> x^3,3.0)
testDeriv(sqrt,2.0)
testDeriv(x +-> exp(x)/sqrt(x),2.0)
testDeriv(x +-> exp(x)*sqrt(x),2.0)
testDeriv(x +-> x*x,2.0)
testDeriv(x +-> x+x,2.0)
testDeriv(x +-> 10*x,2.0)
-- some more functions
)set message time on
g(x) == (1+x)^10000
gx := g(x); -- slow expansion
eval(D(gx,x), x=0.1) -- slow symbolic differentiation
eval(gx, x=jet(0.1)) -- also slow - but faster than D(gx,x)
g(jet(0.1)) -- fast algorithmic differentiation!
)set message time off
-- g(jet(0.1::DoubleFloat)) -- FLOATING-POINT-OVERFLOW error
-- g(jet(x)$Jet(Expression Float)) -- very slow
)set message time on
g(x) ==
expr := 1+x
for i in 1..10000 repeat
expr := expr*(1+x)
expr
g(jet(0.1))
eval(D(g(x),x),x=0.1)
)set message time off
)set message time on
g(x) ==
expr := 1
for i in 1..1000 repeat
expr := expr+x^i/factorial(i)
expr
g(1.0)
g(jet(1.0)) - jet(1.0)*exp(1.0)
eval(D(g(x),x),x=1.0) - exp(1.0)
)set message time off
-- and more functions
g(x) == x^exp(x)^exp(x)
gx := g(x);
eval(D(gx,x),x=0.1)
g(jet(0.1))
g(jet(0.1::DoubleFloat))
eval(x^exp(x)^exp(x), x=jet(0.1))
--eval(x^erf(x)^erf(x), x=jet(0.1)) -- Expression Jet Float
-- calling an interpreter function
fx(x, n) ==
if n=0 then return 0*x
else
a := 0*x
b := x
(a,b) := (0, x)
for k in 1..(n-1) repeat
h := b
b := log(a+b)
a := h
b
eval(D(fx(x,7),x), x=2.0)
fx(jet(2.0,1.0), 7)
-- eval now works; eval() requires PartialOrder and IntegralDomain for
Expression(Jet(T))?
beta = jet(1.0) -- ok
eval((beta+1)^2,beta=jet(1.0)) -- ok
subst((beta+1)^2,beta=jet(1.0)) -- ok
eval(exp(2*beta),beta=jet(1.0)) -- ok
-- Extended example: partial log-likelihood
-- Assume distinct ordered times
l(beta) ==
event := [true,false,false,true,true,false]
times := [i*1.0 for i in 1..#event]
x := [1.0,0,1,1,0,1]
eta := [beta*xi for xi in x]
index := [i for i in 1..#event for e in event | e]
-- L := reduce(*,[exp(eta(i))/reduce(+,[exp(eta(j)) for j in i..#event]) for
i in index])
reduce(+,[eta(i) - log(reduce(+,[exp(eta(j)) for j in i..#event])) for i in
index])
l(jet(1.0))
eval(l(beta), beta=jet(1.0)) -- ok
-- time-varying effect
f(beta,gamma) == reduce(+,[x.i*beta+x.i*times.i*gamma -
log(reduce(+,[exp(x.j*beta+x.j*times.j*gamma) for j in i..#event])) for i in
index])
f(jet(1.0),0)
f(jet(1.0),3.0)
--
-- check some operators
g := operator 'g
h := operator 'h
Dgx(f) == (f,D(f(g(x)),x))
Dgx2(f) == (f,D(f(g(x),h(x)),x))
[Dgx(acos), Dgx(acosh), Dgx(acot), Dgx(acoth), Dgx(acsc), Dgx(acsch),
Dgx(asec), Dgx(asech), Dgx(asin), Dgx(asinh), Dgx(atan), Dgx(atanh), Dgx(cos),
Dgx(cosh), Dgx(cot), Dgx(coth), Dgx(csc), Dgx(csch), Dgx(exp), Dgx(log),
Dgx(sec), Dgx(sech), Dgx(sin), Dgx(sinh), Dgx(tan), Dgx(tanh), Dgx(sqrt),
Dgx(-), Dgx(h)]
)sh TranscendentalFunctionCategory
[Dgx(Chi), Dgx(Ci), Dgx(Ei), Dgx(Shi), Dgx(Si), Dgx(erf), Dgx(erfi),
Dgx(fresnelC), Dgx(fresnelS), Dgx(li)]
)sh FloatLiouvilianFunctions
[Dgx2(Beta), Dgx(Gamma), Dgx(airyAi)]
)sh SPFCAT
Dgx(erf)
-- finite differences
fd(f,x) ==
eps:=1.0e-10
(f(x+eps) - f(x-eps))/2.0/eps
fd(log,1.1)
)endif
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
open-axiom-devel mailing list
open-axiom-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/open-axiom-devel