Performance Improvements in Maple 2024
Operations on Matrices Containing Quantities with Units
Floating-Point Matrix and Vector Initialization
Faster Univariate Complex Solver
RootFinding
Quantifier Elimination
Various Small Improvements
A new Units indexing function has been created which allows a matrix, vector, or array to keep units separate from the data. This allows the data to be a single block of hardware type values, including datatype=float[8] and datatype=complex[8]. LinearAlgebra and other package routines can then dispatch to fast hardware algorithms providing optimal performance.
Here is an example that creates a 100x100 matrix of random data, each element has the unit of meters per second. Computing the inverse takes milliseconds, which is over a thousand times faster than the way done in previous versions which required datatype=anything.
M := LinearAlgebra:-RandomMatrix(100,datatype=float[8],shape=Unit(m/s)):
CodeTools:-Usage( LinearAlgebra:-MatrixInverse(M) ):
memory used=412.60KiB, alloc change=0 bytes, cpu time=18.00ms, real time=84.00ms, gc time=0ns
A matrix with the Units indexing function can be used seamlessly in place of a matrix that has no indexing function, but keeps quantities with units. Such a matrix allows a single unit for all data, different units on different entries, and even entries with no unit assigned. The matrix will adapt to the situation.
M := Matrix( 2, (i,j)->(10*i+j)*Unit(kg), shape=Unit(kg) );
M≔11⁢kg12⁢kg21⁢kg22⁢kg
M[1,1] := 1.23 * Unit(ohm);
M1,1≔1.23⁢Ω
M[1,2] := 17;
M1,2≔17
M;
1.23⁢Ω1721⁢kg22⁢kg
Quantities assigned into the matrix will be converted into the default unit specified for that matrix whenever possible.
M[2,2] := 35.2 * Unit(lb);
M2,2≔35.2⁢lb
1.23⁢Ω1721⁢kg15.96645142⁢kg
The unit and data can be easily separated, so algorithms can inspect and take advantage of the new structure as needed. In this example we perform a matrix power by operating on just the data, and only apply a unit transformation at the end. This kind of manipulation is used behind the scenes in many built-in operations in the LinearAlgebra package.
A := Matrix(2,(i,j)->(10*i+j)*Unit(m), shape=Unit(m), datatype=float[8] );
A≔11.⁢m12.⁢m21.⁢m22.⁢m
default_unit, data := op(rtable_split_unit(A));
default_unit,data≔m,11.12.21.22.
newA := rtable_set_indfn(data^4,Units:-Simple:-simplify(default_unit^4));
newA≔413557.⁢m4439164.⁢m4768537.⁢m4816124.⁢m4
The Units:-Simple package makes use of this new data structure by default when possible.
with(Units:-Simple);
`*`,`+`,`-`,`/`,`<`,<=,<>,`=`,`^`,arccos,arccosh,arccot,arccoth,arccsc,arccsch,arcsec,arcsech,arcsin,arcsinh,arctan,arctanh,argument,collect,combine,cos,cosh,cot,coth,csc,csch,diff,eval,evalc,evalr,exp,factor,frem,ln,log,log10,log2,max,min,piecewise,polar,root,sec,sech,shake,sin,sinh,sqrt,surd,tan,tanh,type,verify
A := <1,2;3,4>*Unit(m);
A≔m2⁢m3⁢m4⁢m
B := <5,6;7,8>*Unit(s);
B≔5⁢s6⁢s7⁢s8⁢s
C := A . B^(-1);
C≔3⁢ms−2⁢ms2⁢ms−ms
rtable_indfns(A);
m
rtable_indfns(B);
s
rtable_indfns(C);
ms
For additional improvements involving units in Maple 2024, see Improvements in Handling of Units in Maple 2024.
Maple uses hardware double-precision floating-point representations when constructing matrices and vectors specified with datatype=float[8]. On initialization Maple 2024 calls directly to the evalhf implementation of known functions and avoids conversion from hardware to software floats. Performance of examples such as the following has dramatically sped up.
tt:=time[real]():
signal:=Vector(10^5,i->sin(i*1.0),datatype=float[8]):
time[real]()-tt;
0.311
Maple 2024 contains a new solver for isolating and approximating all complex roots of a univariate polynomial with numeric or complex(numeric) coefficients. This solver is available through the method=PW option of the RootFinding:-Isolate command. Except when the domain option is given, the new solver is considerably faster than the one used when method=HR is specified, in particular for high accuracy, and it is now the default when option complex is used. Here are a few examples and timing comparisons for the two solvers.
Random polynomial, increasing accuracy.
f := randpoly(x, dense, coeffs=rand(-2^32+1..2^32-1), degree=512):
CodeTools:-Usage(RootFinding:-Isolate(f, digits=10, method=HR)): CodeTools:-Usage(RootFinding:-Isolate(f, digits=10, method=PW)):
memory used=6.55MiB, alloc change=0 bytes, cpu time=161.00ms, real time=161.00ms, gc time=0ns
memory used=5.18MiB, alloc change=0 bytes, cpu time=118.00ms, real time=117.00ms, gc time=0ns
CodeTools:-Usage(RootFinding:-Isolate(f, digits=15, method=HR)): CodeTools:-Usage(RootFinding:-Isolate(f, digits=15, method=PW)):
memory used=6.58MiB, alloc change=0 bytes, cpu time=1.66s, real time=1.66s, gc time=0ns
memory used=4.90MiB, alloc change=0 bytes, cpu time=271.00ms, real time=271.00ms, gc time=0ns
CodeTools:-Usage(RootFinding:-Isolate(f, digits=30, method=HR)): CodeTools:-Usage(RootFinding:-Isolate(f, digits=30, method=PW)):
memory used=6.76MiB, alloc change=0 bytes, cpu time=1.73s, real time=1.73s, gc time=0ns
memory used=5.58MiB, alloc change=0 bytes, cpu time=388.00ms, real time=388.00ms, gc time=0ns
Bernoulli polynomials, increasing degree.
g1 := expand(bernoulli(128,x)):
CodeTools:-Usage(RootFinding:-Isolate(g1, digits=10, method=HR)): CodeTools:-Usage(RootFinding:-Isolate(g1, digits=10, method=PW)):
memory used=1.57MiB, alloc change=0 bytes, cpu time=7.24s, real time=7.24s, gc time=0ns
memory used=1.35MiB, alloc change=0 bytes, cpu time=1.17s, real time=1.17s, gc time=0ns
g2 := expand(bernoulli(255,x)):
CodeTools:-Usage(RootFinding:-Isolate(g2, digits=10, method=HR)): CodeTools:-Usage(RootFinding:-Isolate(g2, digits=10, method=PW)):
memory used=3.61MiB, alloc change=0 bytes, cpu time=37.04s, real time=37.04s, gc time=0ns
memory used=2.94MiB, alloc change=0 bytes, cpu time=3.36s, real time=3.36s, gc time=0ns
The EvaluateAtRoot command in the RootFinding package has a new option `avoidsymbolic`. Its main purpose is to improve the command's efficiency. When specified, symbolic processing to ascertain whether the given root is an exact zero of a constraint or not will be skipped. In general this will lead to weaker answers, e.g., FAIL being returned instead of true or false.
In the following example, the unique root of sys contained in sysroot is actually an exact root of the non-strict inequality in cons.
vars := [x, y];
vars≔x,y
sys := [-7*x^5+22*x^4-55*x^3-94*x^2+87*x-56, -10*y^5+62*y^4-82*y^3+80*y^2-44*y+71];
sys≔−7⁢x5+22⁢x4−55⁢x3−94⁢x2+87⁢x−56,−10⁢y5+62⁢y4−82⁢y3+80⁢y2−44⁢y+71
sysroot := [[-228940483906911495/144115188075855872, -57235120976727867/36028797018963968], [338902091289333/70368744177664, 677804182578693/140737488355328]];
sysroot≔−228940483906911495144115188075855872,−5723512097672786736028797018963968,33890209128933370368744177664,677804182578693140737488355328
cons := [434*x^9-2043*x^8+6055*x^7-1085*x^6-10004*x^5+17167*x^4-6842*x^3+11542*x^2-6997*x <= -4648, -50*y^5+23*y^4+75*y^3-92*y^2+6*y+74 > 0];
cons≔434⁢x9−2043⁢x8+6055⁢x7−1085⁢x6−10004⁢x5+17167⁢x4−6842⁢x3+11542⁢x2−6997⁢x≤−4648,0<−50⁢y5+23⁢y4+75⁢y3−92⁢y2+6⁢y+74
consSet := convert(cons, 'set');
consSet≔434⁢x9−2043⁢x8+6055⁢x7−1085⁢x6−10004⁢x5+17167⁢x4−6842⁢x3+11542⁢x2−6997⁢x≤−4648,0<−50⁢y5+23⁢y4+75⁢y3−92⁢y2+6⁢y+74
By default, EvaluateAtRoot uses symbolic computation to prove that the first constraint is satisfied:
CodeTools:-Usage(RootFinding:-EvaluateAtRoot(cons, sysroot, sys, vars));
memory used=3.14MiB, alloc change=0 bytes, cpu time=33.00ms, real time=34.00ms, gc time=0ns
true,false
CodeTools:-Usage(RootFinding:-EvaluateAtRoot(consSet, sysroot, sys, vars));
memory used=446.16KiB, alloc change=0 bytes, cpu time=10.00ms, real time=10.00ms, gc time=0ns
false
When specifying the avoidsymbolic option and giving a maximum value of 100 for the floating-point precision, the underlying numerical computation will not be able to prove that the first constraint is satisfied, and therefore FAIL is returned:
CodeTools:-Usage(RootFinding:-EvaluateAtRoot(cons, sysroot, sys, vars, 'avoidsymbolic' = true, 'threshold' = 100));
memory used=143.38KiB, alloc change=0 bytes, cpu time=3.00ms, real time=3.00ms, gc time=0ns
FAIL,false
Nevertheless, in the following calling sequence, where we are only interested in the truth value of the logical conjunction of the two constraints, this weaker result is still sufficient, since the 2nd constraint, and therefore the whole conjunction, is false:
CodeTools:-Usage(RootFinding:-EvaluateAtRoot(consSet, sysroot, sys, vars, 'avoidsymbolic' = true, 'threshold' = 100));
memory used=133.67KiB, alloc change=0 bytes, cpu time=3.00ms, real time=3.00ms, gc time=0ns
Significant performance improvements have been made to the QuantifierElimination package, including faster evaluation at sample points by using RootFinding:-EvaluateAtRoot and caching of intermediate results.
The following examples are 6-7 times faster than Maple 2023.
formula1 := forall([x,y,t], Implies(And(x^3+y^2-x=t, t^2=4/27, t<0), x^2+y^2>=rho));
formula1≔∀⁡x,y,t,x3+y2−x=t∧t2=427∧t<0⇒ρ≤x2+y2
CodeTools:-Usage(QuantifierElimination:-QuantifierEliminate(formula1));
memory used=2.88GiB, alloc change=158.48MiB, cpu time=49.36s, real time=43.16s, gc time=8.52s
ρ−RootOf⁡729⁢_Z2+270⁢_Z−83,−6730380045934410413031180591620717411303424..−26921520183737641651854722366482869645213696<0∨ρ−RootOf⁡729⁢_Z2+270⁢_Z−83,−6730380045934410413031180591620717411303424..−26921520183737641651854722366482869645213696=0∨RootOf⁡729⁢_Z2+270⁢_Z−83,−6730380045934410413031180591620717411303424..−26921520183737641651854722366482869645213696−ρ<0∧ρ−RootOf⁡27⁢_Z2−18⁢_Z−13,−64411164140810191969147573952589676412928..−10305786262529630714772361183241434822606848<0∨ρ−RootOf⁡27⁢_Z2−18⁢_Z−13,−64411164140810191969147573952589676412928..−10305786262529630714772361183241434822606848=0∨RootOf⁡27⁢_Z2−18⁢_Z−13,−64411164140810191969147573952589676412928..−10305786262529630714772361183241434822606848−ρ<0∧ρ−RootOf⁡27⁢_Z2−4,−18176397067312372737894722366482869645213696..−9088198533656186368812361183241434822606848<0∨ρ−RootOf⁡27⁢_Z2−4,−18176397067312372737894722366482869645213696..−9088198533656186368812361183241434822606848=0∨RootOf⁡27⁢_Z2−4,−18176397067312372737894722366482869645213696..−9088198533656186368812361183241434822606848−ρ<0∧27⁢ρ<−5∨27⁢ρ+5=0∨−27⁢ρ<5∧ρ−RootOf⁡27⁢_Z2−18⁢_Z−1,−30439693221836108653590295810358705651712..−1217587728873444345852361183241434822606848<0∨ρ−RootOf⁡27⁢_Z2−18⁢_Z−1,−30439693221836108653590295810358705651712..−1217587728873444345852361183241434822606848=0∨RootOf⁡27⁢_Z2−18⁢_Z−1,−30439693221836108653590295810358705651712..−1217587728873444345852361183241434822606848−ρ<0∧ρ<0∨ρ=0∨−ρ<0∧ρ−RootOf⁡729⁢_Z2+270⁢_Z−83,18862547901774207647059444732965739290427392..4715636975443551911832361183241434822606848<0∨ρ−RootOf⁡729⁢_Z2+270⁢_Z−83,18862547901774207647059444732965739290427392..4715636975443551911832361183241434822606848=0∨RootOf⁡729⁢_Z2+270⁢_Z−83,18862547901774207647059444732965739290427392..4715636975443551911832361183241434822606848−ρ<0∧3⁢ρ<1∨−1+3⁢ρ=0
formula2 := exists([t__10,t__11,t__12,t__6,b__12,t__5], And( 0<=t__10, 0<=t__11, 0<=t__12, 0<=t__6, 0<=b__12, t__10<=1, t__11<=1, t__12<=1, t__6<=1, b__12<=1, 0<t__5, t__5<1, t__5^3-2*t__5^2+t__5<0, 0<t__5^3-2*t__5^2+t__5+1, t__6+1-t__5=0, -t__5^2+t__10+t__5=0, t__5^2-t__11+t__12-2*t__5+1=0, -t__5^2+t__11+2*t__5+b__12-2 = 0));
formula2≔∃⁡t__10,t__11,t__12,t__6,b__12,t__5,0≤t__10∧0≤t__11∧0≤t__12∧0≤t__6∧0≤b__12∧t__10≤1∧t__11≤1∧t__12≤1∧t__6≤1∧b__12≤1∧0<t__5∧t__5<1∧t__53−2⁢t__52+t__5<0∧0<t__53−2⁢t__52+t__5+1∧t__6+1−t__5=0∧−t__52+t__10+t__5=0∧t__52−t__11+t__12−2⁢t__5+1=0∧−t__52+b__12+t__11+2⁢t__5−2=0
CodeTools:-Usage(QuantifierElimination:-QuantifierEliminate(formula2));
memory used=2.56GiB, alloc change=0 bytes, cpu time=39.67s, real time=34.16s, gc time=7.40s
In addition, the display of various data structures in the QuantifierElimination package has been improved.
formula := exists(x, x^2+b*x+c=0);
formula≔∃⁡x,b⁢x+x2+c=0
C := QuantifierElimination:-PartialCylindricalAlgebraicDecompose(formula, 'output'=['data']);
C≔Variables=⁢c,b,xInput Formula=⁢∃⁡x,b⁢x+x2+c=0# Cells=⁢17Projection polynomials for level 1=⁢cProjection polynomials for level 2=⁢b2−4⁢cProjection polynomials for level 3=⁢b⁢x+x2+c
GetLeafCells(C)[1..5];
Description=⁢c<0∧x<RootOf⁡_Z2+b⁢_Z+c,index=real1Sample Point=⁢c=−1,b=0,x=−2Index=⁢1,1,1,Description=⁢c<0∧x=RootOf⁡_Z2+b⁢_Z+c,index=real1Sample Point=⁢c=−1,b=0,x=−1Index=⁢1,1,2,Description=⁢c=0∧b<0∧x<RootOf⁡_Z⁢_Z+b,index=real1Sample Point=⁢c=0,b=−1,x=−1Index=⁢2,1,1,Description=⁢c=0∧b<0∧x=RootOf⁡_Z⁢_Z+b,index=real1Sample Point=⁢c=0,b=−1,x=0Index=⁢2,1,2,Description=⁢c=0∧b=0∧x<0Sample Point=⁢c=0,b=0,x=−1Index=⁢2,2,1
The new GetCells method, which supersedes GetLeafCells, also has additional options to control the output.
r := GetCells(C, output=[samplepoints,descriptions]):
r[1][1..5];
c=−1,b=0,x=−2,c=−1,b=0,x=−1,c=0,b=−1,x=−1,c=0,b=−1,x=0,c=0,b=0,x=−1
r[2][1..5];
c<0∧x<RootOf⁡_Z2+b⁢_Z+c,index=real1,c<0∧x=RootOf⁡_Z2+b⁢_Z+c,index=real1,c=0∧b<0∧x<RootOf⁡_Z⁢_Z+b,index=real1,c=0∧b<0∧x=RootOf⁡_Z⁢_Z+b,index=real1,c=0∧b=0∧x<0
The ceil, floor, and round functions are 1.5-2x faster for some numeric arguments in Maple 2024.
CodeTools:-Usage([seq(round(n), n=1..10^5)]):
memory used=85.45MiB, alloc change=0 bytes, cpu time=217.00ms, real time=218.00ms, gc time=0ns
CodeTools:-Usage([seq(floor(n/13), n=1..10^5)]):
memory used=191.96MiB, alloc change=0 bytes, cpu time=2.00s, real time=1.25s, gc time=1.05s
The evalhf command uses a more accurate formula for computing the tangent, cotangent, hyperbolic tangent, and hyperbolic cotangent function in certain areas of the complex plane. As a consequence, evalhf is now used for computing these functions numerically in those parts of the complex plane whenever the requested precision is suitable for hardware float computation. This speeds the following computation up by 4-5x.
CodeTools:-Usage([seq(tan(n * 1e-4 + 20.*I), n=1..10^4)]):
memory used=75.71MiB, alloc change=0 bytes, cpu time=811.00ms, real time=811.00ms, gc time=0ns
Download Help Document