Advanced Math - Maple Help
For the best experience, we recommend viewing online help using Google Chrome or Microsoft Edge.

Online Help

All Products    Maple    MapleSim


Advanced Math Improvements in Maple 2025

Maple 2025 includes many improvements to the math engine.

 

PartiallyOrderedSets

simplify

SumTools

Numerical Inverse Laplace Transform

Solver for complex functions

PolyhedralSets

VerifyTools

Improvements in Generating Random Expressions

Improved Units Support

List and Vector Form for fsolve

Ceil, Floor, Round, Trunc

Identifying an Integer Sequence from Its First Terms

Improvements to evala

ExpressionTools

ValuesUnderConstraints

PartiallyOrderedSets

• 

Maple 2025 contains a new package for computing with partially ordered sets, also known as posets. After considering a random poset, we illustrate the capabilities of this package with four examples taken from different fields of mathematics.

with(PartiallyOrderedSets):

• 

A partially ordered set is a set equipped with a relation that is reflexive, antisymmetric, and transitive. For this package, only finite sets are considered. The relation is usually denoted by the  relator.

A random poset

• 

Posets can be constructed in various ways. One way is to supply an adjacency matrix. Below, we generate a random matrix that is valid as an adjacency matrix for a poset: we first generate a fully random 0-1 matrix with appropriate density, set its lower triangle to 0 and its diagonal to 1, and then find the transitive closure.

n := 25:

L := LinearAlgebra:-RandomMatrix(n, generator=1, density=1/3);

(1)

L := ArrayTools:-UpperTriangle(L):

L := max~(L, LinearAlgebra:-IdentityMatrix(n)):

L := min~(1, L^n);

(2)
• 

Now we generate the poset. We specify the base set (in this case, the integers 1 through n) and the relation (defined by L).

random_poset := PartiallyOrderedSet([seq(1 .. n)], L);

random_poset< a poset with 25 elements >

(3)
• 

We can visualize this poset in multiple ways. The naive way would be to draw an arrow between every pair of related elements, as follows.

DrawGraph(random_poset, reduction = false);

• 

This does not give much insight. A better approach is showing the so-called Hasse diagram, where we only show an arrow from a to c if a<c and there is no b such that a<b<c.

DrawGraph(random_poset);

The poset of the divisors of a positive integer

• 

We define the relation of divisibility as a procedure, and create the poset on the divisors of 2025 using this procedure.

divisibility := (x, y) -> irem(y, x) = 0:

divisibility_poset := PartiallyOrderedSet(NumberTheory:-Divisors(2025), divisibility);

divisibility_poset< a poset with 15 elements >

(4)
• 

We display this poset.

DrawGraph(divisibility_poset);

• 

There are a number of properties that a poset may or may not have that we can test using this package. A poset is graded if its elements can be partitioned into numbered subsets such that the immediate successors of the entries of subset n are numbered n+1.

IsGraded(divisibility_poset);

true

(5)
• 

A poset is ranked if its maximal chains (that is, subsets where every element is related to every other element) all have the same cardinality. Every ranked poset is graded, but this is not true the other way around.

IsRanked(divisibility_poset);

true

(6)
• 

A poset is a lattice if every pair of elements has a smallest upper bound and a greatest lower bound.

IsLattice(divisibility_poset);

true

(7)
• 

A poset is a face lattice if it is isomorphic to the face lattice of a polyhedral set (see below).

IsFaceLattice(divisibility_poset);

true

(8)

The face lattice of a polyhedral set

• 

A polyhedral set is the intersection of a finite number of half spaces. An example is a tetrahedron.

t := PolyhedralSets:-ExampleSets:-Tetrahedron();

t&lcub;Coordinates&colon;x1&comma;x2&comma;x3Relations&colon;x1x2x31&comma;x1+x2+x31&comma;x1x2+x31&comma;x1+x2x31

(9)

PolyhedralSets:-Plot(t);

d := PolyhedralSets:-Dimension(t);

d3

(10)
• 

The faces of a polyhedral sets are the vertices, edges, and higher-dimensional boundaries of the polyhedral set. We sometimes also include the empty subset.

t_faces := {seq(op(PolyhedralSets:-Faces(t, dimension = i)), i = 0 .. d), PolyhedralSets:-ExampleSets:-EmptySet(d)}:

face_list := convert(t_faces, list):

• 

We now construct the face lattice, ordered by inclusion.

inclusion := (x, y) -> PolyhedralSets:-`subset`(face_list[x], face_list[y]);

inclusionx&comma;yPolyhedralSets:−subsetface_listx&comma;face_listy

(11)

polyhedral_poset := PartiallyOrderedSet({seq(1 .. numelems(face_list))}, inclusion);

polyhedral_poset< a poset with 16 elements >

(12)

DrawGraph(polyhedral_poset);

• 

We verify some properties of this poset.

IsGraded(polyhedral_poset);

true

(13)

IsRanked(polyhedral_poset);

true

(14)

IsLattice(polyhedral_poset);

true

(15)

IsFaceLattice(polyhedral_poset);

true

(16)
• 

The width of a poset is the size of the largest antichain (a set where every pair of elements is not related).

Width(polyhedral_poset);

6

(17)
• 

The height of a poset is the size of the largest chain.

Height(polyhedral_poset);

5

(18)

The lattice of the flats of a matroid

• 

Consider the Fano matroid. Its flats form a partially ordered set, ordered by inclusion. We ensure compact display of the flats with some custom procedures.

M := Matroids:-ExampleMatroids:-Fano();

Ma matroⅈ&DifferentialD; on 7 &ExponentialE;l&ExponentialE;m&ExponentialE;nts wⅈth 14 cⅈrcuⅈts

(19)

F := Matroids:-Flats(M);

F&comma;1&comma;2&comma;3&comma;4&comma;5&comma;6&comma;7&comma;1&comma;2&comma;4&comma;1&comma;3&comma;5&comma;2&comma;3&comma;6&comma;4&comma;5&comma;6&comma;3&comma;4&comma;7&comma;2&comma;5&comma;7&comma;1&comma;6&comma;7&comma;1&comma;2&comma;3&comma;4&comma;5&comma;6&comma;7

(20)

compact_display := proc(s :: set, $)
if s = {} then
  return "&empty;";
else
  return String(seq(s));
end if;
end proc:

Compact_F := map(compact_display, F);

Compact_F&comma;1&comma;2&comma;3&comma;4&comma;5&comma;6&comma;7&comma;124&comma;135&comma;236&comma;456&comma;347&comma;257&comma;167&comma;1234567

(21)

Expand_F := table(zip(`=`, Compact_F, F));

Expand_Ftable2=2&comma;167=1&comma;6&comma;7&comma;347=3&comma;4&comma;7&comma;236=2&comma;3&comma;6&comma;124=1&comma;2&comma;4&comma;5=5&comma;7=7&comma;456=4&comma;5&comma;6&comma;3=3&comma;=&comma;1=1&comma;257=2&comma;5&comma;7&comma;4=4&comma;135=1&comma;3&comma;5&comma;1234567=1&comma;2&comma;3&comma;4&comma;5&comma;6&comma;7&comma;6=6

(22)

inclusion := (x, y) -> Expand_F[x] subset Expand_F[y];

inclusionx&comma;yExpand_FxExpand_Fy

(23)

matroid_poset := PartiallyOrderedSet({op(Compact_F)}, inclusion);

matroid_poset< a poset with 16 elements >

(24)

DrawGraph(matroid_poset);

• 

Observe that this is the lattice of the faces of a polyhedral set, but not of the tetrahedron.

IsLattice(matroid_poset);

true

(25)

IsFaceLattice(matroid_poset);

true

(26)

AreIsomorphic(polyhedral_poset, matroid_poset);

false

(27)

The poset of the partitions of a set

• 

The partitions of a set are partially ordered by the "finer-than" relation. One partition X is finer than another partition Y if each set in X is a subset of a set in Y.

P := combinat:-setpartition({1,2,3,4});

P1&comma;2&comma;3&comma;4&comma;1&comma;2&comma;3&comma;4&comma;2&comma;1&comma;3&comma;4&comma;3&comma;1&comma;2&comma;4&comma;4&comma;1&comma;2&comma;3&comma;1&comma;2&comma;3&comma;4&comma;1&comma;3&comma;2&comma;4&comma;1&comma;4&comma;2&comma;3&comma;1&comma;2&comma;3&comma;4&comma;1&comma;3&comma;2&comma;4&comma;1&comma;4&comma;2&comma;3&comma;2&comma;3&comma;1&comma;4&comma;2&comma;4&comma;1&comma;3&comma;3&comma;4&comma;1&comma;2&comma;1&comma;2&comma;3&comma;4

(28)

IsFiner := proc(X :: set(set), Y :: set(set), $)
for local x in X do
  if not ormap(y -> x subset y, Y) then
    return false;
  end if;
end do;
return true;
end proc:

• 

Again we ensure compact display with some custom procedures.

compact_display_2 := (s :: set(set)) -> StringTools:-Join(map(compact_display, [op(s)]), "|");

compact_display_2s::setsetStringTools:−Joinmapcompact_display&comma;ops&comma;|

(29)

compact_P := map(compact_display_2, [op(P)]);

compact_P1234&comma;1|234&comma;2|134&comma;3|124&comma;4|123&comma;12|34&comma;13|24&comma;14|23&comma;1|2|34&comma;1|3|24&comma;1|4|23&comma;2|3|14&comma;2|4|13&comma;3|4|12&comma;1|2|3|4

(30)

expand_P := table(zip(`=`, compact_P, [op(P)])):

compactIsFiner := (x, y) -> IsFiner(expand_P[x], expand_P[y]);

compactIsFinerx&comma;yIsFinerexpand_Px&comma;expand_Py

(31)

partition_poset := PartiallyOrderedSet(convert(compact_P, set), compactIsFiner);

partition_poset< a poset with 15 elements >

(32)

DrawGraph(partition_poset);

• 

This is another face lattice.

IsFaceLattice(partition_poset);

true

(33)

simplify

• 

Maple 2025 introduces several important improvements to simplify regarding expressions containing exponential, trigonometric, and/or inverse trigonometric functions. These updates also bring beneficial ripple effects across the Maple library.

New and improved conversion of exp to trig

• 

simplify now recognizes when exponentials can be profitably converted to hyperbolic trig functions:

restart;

simplify(exp(x)-exp(-x));

2sinhx

(34)

simplify((exp(x)-1)/(exp(x)+1));

tanhx2

(35)

simplify(1/4/(1/2*exp(1)-1/2*exp(-1))*(exp(3)-exp(-3)-exp(1)+exp(-1)));

cosh2

(36)
• 

Moreover, conversion to non-hyperbolic trig functions is now recognized in more cases than previously:

simplify(I*(exp(2*I*x*y)+exp(-2*I*x*(-2+y)))/(exp(4*I*x)-1));

cos2x1+ycsc2x

(37)

Simplification of expressions with exp or trig is more careful

• 

In general, simplification w.r.t. exp is now more careful to avoid unnecessary expansions and normalizations. This result is now significantly more compact:

simplify(1/2*(-sin(t-x)*exp(I*1/6*abs(t-x))-cos(3*t-3*x))*exp(-I/6*abs(t-x)));

&ExponentialE;I6txcos3t3x2sintx2

(38)
• 

In certain cases involving trigonometric functions, simplify now tries harder to get a fully simplified answer before returning:

simplify(-sin(2)^2/2 - cos(2)^2/2 - sin(2)*cos(x - 1)*sin(x - 1) + cos(2)*cos(2*x - 2)/2);

cos2x212

(39)

Improved simplification of expressions with inverse trigonometric functions

• 

Trigonometric functions with logarithms or inverse trig functions in their arguments are now converted to a simpler form without trig and arctrig/ln when it can be determined that it leads to a simpler result:

simplify(cos(ln(2)+arctanh(x))-2^(-1+I)*(1-x)^(-1/2*I)*(x+1)^(1/2*I));

2−1I1xI21+xI2

(40)
• 

simplify also now recognizes when converting arctan or arctanh to ln reduces the size of the expression:

simplify(2*arctanh(x) + ln(1 - x));

ln1+x

(41)

Conversion of exp(ln(t)) and exp(Pi*I*t)

• 

A trivial simplification was previously missed (it should be noted, this input is rare due to the uneval quotes; without them exp itself would make this simplification):

simplify('exp'(ln(t)));

t

(42)
• 

While simplify does not by default convert exp(Pi*I*t) to a power for symbolic t, it will now do so if it detects that the resulting power already exists in the expression:

simplify((1+(-1)^t)/exp(Pi*I*t));

−1t+1

(43)

Resulting improvements to other library commands

• 

The aforementioned improvements to simplify have had knock-on effects in other areas of the Maple library. For example, this limit is now computed correctly:

q := exp(s*t)/s/cosh(s):

p := (2*n+1)/2*Pi*I:

limit((s-p)*q, s=p) assuming n::integer;

2&ExponentialE;I22n+1πt−1n2n+1π

(44)

and these results are more compact:

Student:-Calculus1:-SurfaceOfRevolution(cosh(x), x=-Pi..Pi, axis=vertical);

4sinhππ24coshππ+4π

(45)

dsolve({diff(y(x),x,x)-2*y(x)=0, y(0)=1.2, y(1)=0.9});

yx=33sinh2x4sinh2x1csch210

(46)

pdsolve([diff(u(x, y), x, x)+diff(u(x, y), y, y)-k*u(x, y) = 0, u(0, y) = 1, u(Pi, y) = 0, u(x, 0) = 0, u(x, Pi) = 0]) assuming k > 0;

ux&comma;y=n=12−1n1sinnycschn2+kπsinhn2+kπxπn

(47)

SumTools

• 

Two new commands were added to the SumTools[DefiniteSum] subpackage to test for convergence of an infinite series and find its convergence radius. See Radius of Convergence for details.

Numerical Inverse Laplace Transform

• 

Users on MaplePrimes have long requested an efficient method of numerically inverting a transfer function for which no symbolic inverse exists. In response, in Maple 2025 there is a new quadrature method for computing numerical inverse Laplace transforms, which is available by using the numeric option to the inttrans:-invlaplace command. The new calling sequence takes an expression or procedure and produces an Array of evaluation values of the inverse transform. See inttrans/invlaplace/numeric for details.

expr := exp(0.3*s)*sinh(0.5*sqrt(s))/(s*(sqrt(s)*cosh(sqrt(s)) + s));

expr&ExponentialE;0.3ssinh0.5ssscoshs+s

(48)
• 

No symbolic inverse exists.

inttrans:-invlaplace(expr, s, t);

1&ExponentialE;310ssinh12ss3&sol;2coshs&plus;s2&comma;s&comma;t

(49)
• 

Numerical inversion

numerical_inversion := inttrans:-invlaplace(expr, 100, 0.01, 'numeric');

(50)
• 

Plot the simulated result.

Times := Vector(100, i -> (i-1)*0.01):

plot(Times, numerical_inversion);

Solver for complex functions

• 

The new command SolveTools:-ComplexSolve uses some transformations to produce better complex-valued solutions to equations involving abs z and conjugate z&conjugate0; than solve.

SolveTools:-ComplexSolve( { abs(z)^2 + z*abs(z) + conjugate(z) }, {z} );

z=0&comma;z=12I32&comma;z=12+I32

(51)

PolyhedralSets

• 

In Maple 2025, the PolyhedralSets package has a number of new features, which we illustrate with the examples below.

restart;

with(PolyhedralSets):

with(ExampleSets):

Computing the number of integer points for non-parametric polyhedral sets

• 

The generating function of a polyhedral set in a space with coordinate variables x1&comma;&comma;xn is the formal sum of the monomials x1a1xnan, where a1&comma;&comma;an iterates over all integer points in the polyhedral set. Here is an example.

ps := Tetrahedron();

ps&lcub;Coordinates&colon;x1&comma;x2&comma;x3Relations&colon;x1x2x31&comma;x1+x2+x31&comma;x1x2+x31&comma;x1+x2x31

(52)

Plot(ps);

• 

From the plot, we can see that this polyhedral set has all its coordinates between −1 and 1. So we can enumerate all points in that box, and test if the relations defining the polyhedral set hold. This gives us the number of integer points.

relations := Relations(ps);

relationsx1x2x31&comma;x1+x2+x31&comma;x1x2+x31&comma;x1+x2x31

(53)

candidates := [seq(seq(seq([i, j, k], i=-1..1), j=-1..1), k=-1..1)];

candidates−1&comma;−1&comma;−1&comma;0&comma;−1&comma;−1&comma;1&comma;−1&comma;−1&comma;−1&comma;0&comma;−1&comma;0&comma;0&comma;−1&comma;1&comma;0&comma;−1&comma;−1&comma;1&comma;−1&comma;0&comma;1&comma;−1&comma;1&comma;1&comma;−1&comma;−1&comma;−1&comma;0&comma;0&comma;−1&comma;0&comma;1&comma;−1&comma;0&comma;−1&comma;0&comma;0&comma;0&comma;0&comma;0&comma;1&comma;0&comma;0&comma;−1&comma;1&comma;0&comma;0&comma;1&comma;0&comma;1&comma;1&comma;0&comma;−1&comma;−1&comma;1&comma;0&comma;−1&comma;1&comma;1&comma;−1&comma;1&comma;−1&comma;0&comma;1&comma;0&comma;0&comma;1&comma;1&comma;0&comma;1&comma;−1&comma;1&comma;1&comma;0&comma;1&comma;1&comma;1&comma;1&comma;1

(54)

points_inside := select(c -> andmap(r -> eval(r, [x[1], x[2], x[3]] =~ c), relations), candidates);

points_inside1&comma;−1&comma;−1&comma;0&comma;0&comma;−1&comma;−1&comma;1&comma;−1&comma;0&comma;−1&comma;0&comma;−1&comma;0&comma;0&comma;0&comma;0&comma;0&comma;1&comma;0&comma;0&comma;0&comma;1&comma;0&comma;−1&comma;−1&comma;1&comma;0&comma;0&comma;1&comma;1&comma;1&comma;1

(55)

numelems(points_inside);

11

(56)
• 

We can automate this process partially by computing the generating function, using the new GeneratingFunction command. The generating function has a term x1a__1x2a__2x3a__3 for every integer point a1&comma;a2&comma;a3 in the polyhedral set; for example, x2x1x3 for the point −1&comma;1&comma;−1.

gps := GeneratingFunction(ps);

gpsx12x22x32+x12x2x3+x3x1x22+x2x1x32+x1x2x3+x12+x1x2+x1x3+x22+x2x3+x32x1x2x3

(57)
• 

If we substitute 1 for each of the coordinate variables, this gives us the number of integer points.

eval(gps, [x[1], x[2], x[3]] =~ 1);

11

(58)
• 

We can automate this even further by using the new NumberOfIntegerPoints command.

NumberOfIntegerPoints(ps);

11

(59)
• 

Consider another rational polyhedral set.

ps := TruncatedOctahedron();

ps&lcub;Coordinates&colon;x1&comma;x2&comma;x3Relations&colon;x31&comma;x31&comma;x21&comma;x21&comma;x1x2x332&comma;x2+x3x132&comma;x11&comma;x3+x2x132&comma;x1+x2+x332&comma;x1x3x232&comma; and 4 more constraints

(60)

Plot(ps);

NumberOfIntegerPoints(ps);

7

(61)

Computing the number of integer points for parametric polyhedral sets

• 

The NumberOfIntegerPoints command can also compute the number of integer points for parametric polyhedral sets, that is, a polyhedral set where the defining relations include one or more parameters. This sort of analysis often occurs when analyzing nested loops in compiler optimization. For example, here is a right triangle with n points on each leg:

parametric_triangle := [1 <= i, i <= n, 1 <= j, j <= i];

parametric_triangle1i&comma;in&comma;1j&comma;ji

(62)
• 

We can plot a parametric triangle only if we specialize the parameters to a concrete value. Here we specialize to n=10.

ps := PolyhedralSet(eval(parametric_triangle, n=10));

ps&lcub;Coordinates&colon;i&comma;jRelations&colon;j−1&comma;i+j0&comma;i10

(63)

Plot(ps);

• 

We can of course compute the number of integer points of this specialized triangle.

NumberOfIntegerPoints(ps);

55

(64)
• 

To compute the number of integer points of the parametric triangle, we need to tell Maple which of the variables are meant to be coordinate variables and which are meant to be parameters. When we do so, we get back a formula for the number of integer points in terms of the parameter, n.

np := NumberOfIntegerPoints(parametric_triangle, [i, j], [n], output = piecewise);

np12n2+12n02+n1n1=0

(65)

eval(np, n = 10);

55

(66)
• 

The following parametric triangle is more complicated, because the formula is different depending on whether n is even or odd.

parametric_triangle_2 := [1 <= i, j <= n, 2*i <= 3*j];

parametric_triangle_21i&comma;jn&comma;2i3j

(67)

ps_8 := PolyhedralSet(eval(parametric_triangle_2, n=8));

ps_8&lcub;Coordinates&colon;i&comma;jRelations&colon;j8&comma;i−1&comma;i3j20

(68)

ps_9 := PolyhedralSet(eval(parametric_triangle_2, n=9));

ps_9&lcub;Coordinates&colon;i&comma;jRelations&colon;j9&comma;i−1&comma;i3j20

(69)

ps_10 := PolyhedralSet(eval(parametric_triangle_2, n=10));

ps_10&lcub;Coordinates&colon;i&comma;jRelations&colon;j10&comma;i−1&comma;i3j20

(70)

Plot([ps_8, ps_9, ps_10]);

NumberOfIntegerPoints(ps_8);

52

(71)

NumberOfIntegerPoints(ps_9);

65

(72)

NumberOfIntegerPoints(ps_10);

80

(73)

NumberOfIntegerPoints(parametric_triangle_2, [i, j], [n], output = piecewise);

one of the polynomials 34n2+12n14+34n2+12n depending on the value of n modulo 202+3n0otherwise

(74)
• 

This computation gives rise to a so-called quasi-polynomial. A quasi-polynomial is an expression given by an integer m (the modulo), a list of m polynomials f1&comma;&comma;fm (the constituents), and a so-called switch s, which is a linear polynomial. The expression takes on the value f__i whenever s is equal to i modulo m. In other words, it is a polynomial with coefficients that are periodic functions, all with the same integral period. In this case, m=2 and s=n: the result is given by one of two polynomials, depending on the parity of n.

• 

The easiest way to deal with such quasi-polynomials is when they occur inside ValuesUnderConstraints objects, described in a section below, which the NumberOfIntegerPoints command generates by default for parametric polyhedral sets.

np := NumberOfIntegerPoints(parametric_triangle_2, [i, j], [n]);

npvalue one of the polynomials 34n2+12n14+34n2+12n depending on the value of n modulo 2 when 02+3n

(75)

with(ValuesUnderConstraints):

Eval(np[1], n=8);

52

(76)

Eval(np[1], n=9);

65

(77)

Eval(np[1], n=10);

80

(78)
• 

The polyhedral set below depends on two parameters, m and n. The shape can be a triangle or a quadrangle, depending on the parameter values. In the display below, all shapes extend to the lower left; the blue region contains the red, and the green contains both.

parametric_polytope := [1 <= i, j <= n,  i <= m, 3*i <= 5*j];

parametric_polytope1i&comma;jn&comma;im&comma;3i5j

(79)

ps23 := PolyhedralSet(eval(parametric_polytope, {m=2, n=3}));

ps23&lcub;Coordinates&colon;i&comma;jRelations&colon;j3&comma;i−1&comma;i5j30&comma;i2

(80)

ps74 := PolyhedralSet(eval(parametric_polytope, {m=7, n=4}));

ps74&lcub;Coordinates&colon;i&comma;jRelations&colon;j4&comma;i−1&comma;i5j30

(81)

ps86 := PolyhedralSet(eval(parametric_polytope, {m=8, n=6}));

ps86&lcub;Coordinates&colon;i&comma;jRelations&colon;j6&comma;i−1&comma;i5j30&comma;i8

(82)

Plot([ps23, ps74, ps86]);

map(NumberOfIntegerPoints, [ps23, ps74, ps86]);

5&comma;15&comma;31

(83)
• 

We first view the number of integer points, which is easiest as follows. Then we evaluate the results for the configurations we displayed above.

NumberOfIntegerPoints(parametric_polytope, [i, j], [m, n], output = piecewise);

nm+one of the polynomials 00251525 depending on the value of m modulo 53m210+3m100m205n703m+5n1nm+1=005n4one of the polynomials 01313 depending on the value of n modulo 3+5n26+n23m+5n=005n4one of the polynomials 01313 depending on the value of n modulo 3+5n26+n205n403m5n1

(84)

np := NumberOfIntegerPoints(parametric_polytope, [i, j], [m, n]);

npvalue nm+one of the polynomials 00251525 depending on the value of m modulo 53m210+3m10 when 0m2&comma;05n7&comma;03m+5n1&comma;value n when 1+m=0&comma;05n4&comma;value one of the polynomials 01313 depending on the value of n modulo 3+5n26+n2 when 3m+5n=0&comma;05n4&comma;value one of the polynomials 01313 depending on the value of n modulo 3+5n26+n2 when 05n4&comma;03m5n1

(85)

map(Eval, np, {m=2, n=3});

5

(86)

map(Eval, np, {m=7, n=4});

15

(87)

map(Eval, np, {m=8, n=6});

31

(88)
• 

The following 3-dimensional polyhedral set depends on two parameters, m and n. For this example, the geometry varies with the values of the parameters.

parametric_polytope_2 := [1 <= i, i <= n, j <= m, 1 <= j, j <= i, 1 <= k, k <= j];

parametric_polytope_21i&comma;in&comma;jm&comma;1j&comma;ji&comma;1k&comma;kj

(89)

ps46 := PolyhedralSet(eval(parametric_polytope_2, {m=4, n=6}));

ps46&lcub;Coordinates&colon;i&comma;j&comma;kRelations&colon;k−1&comma;j+k0&comma;j4&comma;i+j0&comma;i6

(90)

ps97 := PolyhedralSet(eval(parametric_polytope_2, {m=9, n=7}));

ps97&lcub;Coordinates&colon;i&comma;j&comma;kRelations&colon;k−1&comma;j+k0&comma;i+j0&comma;i7

(91)

Plot([ps46, ps97], transparency=[0, 0.5], orientation=[18, 54, -1]);

NumberOfIntegerPoints(ps46);

40

(92)

NumberOfIntegerPoints(ps97);

84

(93)

np := NumberOfIntegerPoints(parametric_polytope_2, [i, j, k], [m, n], output = piecewise);

np13n+12n2+16n3m+n=002+n1n1=0m+1=013n+12n2+16n302+n0mn11n1=00m213m13m3+12nm2+12nm0m203+n0m+n14+nm+1=002+n

(94)
• 

In this case, the result does not contain any quasi-polynomials.

eval(np, {m=4, n=6});

40

(95)

eval(np, {m=9, n=7});

84

(96)
• 

The examples in this section come from the following reference:

  

Rui-Juan Jing, Yuzhuo Lei, Christopher F. S. Maligec, Marc Moreno Maza: Counting the Integer Points of Parametric Polytopes: A Maple Implementation. In the proceedings of Computer Algebra in Scientific Computing: 26th International Workshop, pp. 140-160.

VerifyTools

• 

In earlier versions of Maple, testing and verifying properties could be done using the verify command which came with a number of useful pre-built verification methods. For Maple 2025, we introduce the package VerifyTools which allows users to extend this system to include their own personally defined verifications. While any given verification could be simulated by writing an equivalent procedure, the great advantage of using VerifyTools is the ability to combine various user-defined and/or system verifications together in a structured verification.

• 

VerifyTools is similar to the TypeTools package. A type is essentially a predicate that a single expression can either satisfy or not. Analogously, a verification is a predicate that applies to a pair of expressions, comparing them. Just as types can be combined to produce compound types, verifications can also be combined to produce compound, or structured, verifications. New types can be created, retrieved, queried, or,deleted using the commands AddType, GetType (or GetTypes), Exists, and RemoveType, respectively. Similarly in the VerifyTools package we can create, retrieve, query, or delete verifications using AddVerification, GetVerification (or GetVerifications), Exists, and RemoveVerification.

• 

The package command VerifyTools:-Verify is also available as the top-level Maple command verify which should already be familiar to expert Maple users. Similarly, the command VerifyTools:-IsVerification is also available as a type, that is,

VerifyTools:-IsVerification(ver);

false

(97)

will return the same as

type(ver, 'verification');

false

(98)
• 

In the following examples we show what can be done with these commands.

with(VerifyTools):

• 

Suppose we want to create a verification which checks that the length of a result has not increased compared to the expected result. We can do this using the AddVerification command:

AddVerification(length_not_increased, (a, b) -> evalb(length(a) <= length(b)));

• 

First, we can check the existence of our new verification and get its value:

Exists(length_not_increased);

true

(99)

GetVerification(length_not_increased);

a&comma;bevalblengthalengthb

(100)
• 

For named verifications, IsVerification is equivalent to Exists (since names are only recognized as verifications if an entry exists for them in the verification database):

IsVerification(length_not_increased);

true

(101)
• 

On the other hand, a nontrivial structured verification can be checked with IsVerification,

IsVerification(boolean = length_not_increased);

true

(102)

whereas Exists only accepts names:

Exists(boolean = length_not_increased);

Error, invalid input: VerifyTools:-Exists expects its 1st argument, x, to be of type symbol, but received boolean = length_not_increased

• 

The preceding command using Exists is also equivalent to the following type call:

type(boolean = length_not_increased, verification);

true

(103)
• 

Now, let's use the new verification:

Verify(x + 1/x, (x^2 + 1)/x, length_not_increased);

true

(104)

Verify((x^2 + 1)/x, x + 1/x, length_not_increased);

false

(105)
• 

For a more complicated example, suppose we have two lists of results, one expected and the other newly computed:

expected_results := [(x^2+1)/x, 2*sin(t)*cos(t)];

expected_resultsx2+1x&comma;2sintcost

(106)

computed_results := [sin(2*t), x + 1/x];

computed_resultssin2t&comma;x+1x

(107)

We want to verify two things: that the computed results are mathematically equal to the expected results, though they may have a different form (we can use the simplify verification for this), and that the length of the computed results are not larger than those expected (we use our custom verification, length_not_increased, for this). Moreover, the entries in the lists can be in a different order, so we can use the as_set verification for this. Putting all of this together, we would use the following structured verification:

verify(computed_results, expected_results, as_set(And(simplify, length_not_increased)));

true

(108)

In previous versions of Maple, we could have written a procedure to do this work, but we would have had to re-implement at least the as_set verifications: it works only when the relation between the elements of the "sets" can be expressed as a verification.

• 

Finally, let's remove the verification:

RemoveVerification(length_not_increased);

Exists(length_not_increased);

false

(109)

GetVerification(length_not_increased);

Error, (in VerifyTools:-GetVerification) length_not_increased is not a recognized verification

• 

GetVerifications returns the list of all verifications known to the system:

GetVerifications();

&under&comma;Array&comma;FAIL&comma;FrobeniusGroupId&comma;Global&comma;Matrix&comma;MultiSet&comma;PermGroup&comma;RootOf&comma;SmallGroupId&comma;Vector&comma;address&comma;after&comma;approx&comma;array&comma;as_list&comma;as_multiset&comma;as_set&comma;attributes&comma;boolean&comma;box&comma;cbox&comma;curve&comma;curves&comma;dataframe&comma;dataseries&comma;default&comma;default&comma;dummyvariable&comma;equal&comma;evala&comma;evalc&comma;expand&comma;false&comma;float&comma;function&comma;function_bounds&comma;function_curve&comma;function_shells&comma;greater_equal&comma;greater_than&comma;in_convex_polygon&comma;indef_int&comma;interval&comma;less_equal&comma;less_than&comma;list&comma;listlist&comma;matrix&comma;member&comma;multiset&comma;neighborhood&comma;neighbourhood&comma;normal&comma;permute_elements&comma;plot&comma;plot3d&comma;plot_distance&comma;plotthing_compile_result&comma;polynom&comma;procedure&comma;ptbox&comma;range&comma;rational&comma;record&comma;relation&comma;reverse&comma;rifset&comma;rifsimp&comma;rtable&comma;set&comma;sign&comma;simplify&comma;sublist&comma;subset&comma;subtype&comma;superlist&comma;superset&comma;supertype&comma;symbol&comma;table&comma;table_indices&comma;testeq&comma;text&comma;true&comma;truefalse&comma;type&comma;undefined&comma;units&comma;vector&comma;verifyfunc&comma;wildcard&comma;xmltree&comma;xvm

(110)
  

Note: VerifyTools has been available in Maple for roughly 24 years, but until now it has never been documented, as it was originally intended for internal use only. Some of the VerifyTools commands have been revised for this release.

Improvements in Generating Random Expressions

• 

The RandomTools:-GenerateSimilar command can produce a new expression that shares characteristics with a given expression.  One use of this is in generating similar problems for student practice. With Maple 2025, we’ve improved this capability to include making it even easier for students to practice differentiation problems by producing a new differentiation problem that requires the same core steps required to find the solution as the given problem.

• 

In addition, for convenience an option has been added to facilitate the creation of a list of random expressions instead of just one. For details, see Improved Ability to Generate Similar Problems.

Improved Units Support

• 

max and min now offer built-in Units support for simple inputs.  Prior to Maple 2025 it was required that one of the Units packages, for example, Units:-Simple, be loaded, otherwise max and min would return unevaluated

max(3*Unit(m),2*Unit(m));

3m

(111)
• 

The sort command now supports inputs containing Units.

sort([3*Unit(m),4*Unit(ft),10*Unit(ft)]);

4ft&comma;3m&comma;10ft

(112)
• 

The fdiff command now supports units as well.

fdiff( 2.3*Unit(m/s)*t + 1/2 * 9.81 * Unit(m/s^2)*t^2, t=2.1*Unit(s) );

22.90100000ms

(113)

List and Vector Form for fsolve

• 

The fsolve command now accepts its equations, variables, and ranges options in list or Vector form. The form in which the variables are specified determines the form for the returned solutions. Using list or Vector form to specify the variables will preserve their order in the solutions. If the variables are specified in Vector form then the solution contains the ordered numeric values rather than equations for each variable. For the following example the result is a list in which the y-value solution appears first, matching the order of the specified list of variable names.

fsolve(<x^2-y-3=0, x+y=4>, [y,x], {y=4..8});

y=7.192582404&comma;x=−3.192582404

(114)

Ceil, Floor, Round, Trunc

• 

Four new top-level commands have been added that round to integer multiples of a given second argument. Each of these has the same behavior as the corresponding lowercase version of the command. More details and examples on the Trunc help page. Some common use cases of these commands would be round to multiples of 10, multiples of π, or to do conversions to nearest integer multiples of units.

restart;

Floor(72*Pi, 10);

220

(115)

Floor(72*Pi, 0.01);

226.19

(116)

Ceil(27, Pi);

9π

(117)

Trunc( 14, sqrt(3) );

83

(118)

Round( 45*Unit(miles), Unit(km) );

72km

(119)

Identifying an Integer Sequence from Its First Terms

• 

The new command IdentifySequence attempts to find a formula for the nth term of the sequence of integers that you input.

IdentifySequence([1, 3, 5, 7, 9], 'n');

2n1

(120)
• 

For more information, see Find a Formula for the nth Term of a Given Integer Sequence.

Improvements to evala

• 

Two new primitives RealPart and ImaginaryPart have been added to the evala command. These compute a RootOf expression of smallest possible degree for the real or imaginary part(s), respectively, of a given RootOf.

f := RootOf(x^4+x-1, 'index'=4):

re, im := evala(RealPart(f)), evala(ImaginaryPart(f));

re,imRootOf64_Z6+16_Z21&comma;index=real2,RootOf4096_Z122048_Z81664_Z61792_Z4+864_Z2283&comma;index=real1

(121)

evalf(f), evalf(re), evalf(im);

0.24812606281.033982061I,0.2481260628,−1.033982061

(122)
  

In earlier versions of Maple, the evalc@Re and evalc@Im calling sequences provide similar functionality. However, in this example they return radicals instead of RootOf expressions.

# evala(Re(f)), evala(Re(g));

6108+128492348108+128491312,6108+128492348108+1284913108+1284923+126108+128491348108+128492348108+1284913108+1284913108+128492348108+128491312

(123)
• 

The new commands also work for a RootOf with interval selector.

f := RootOf(x^4+x-1, -2*I..1/2);

fRootOf_Z4+_Z1&comma;2I..12

(124)

re, im := evala(RealPart(f)), evala(ImaginaryPart(f));

re,imRootOf64_Z6+16_Z21&comma;0..12,RootOf4096_Z122048_Z81664_Z61792_Z4+864_Z2283&comma;−2..0

(125)

evalf(f), evalf(re), evalf(im);

0.24812606281.033982061I,0.2481260628,−1.033982061

(126)
• 

The new commands accept a RootOf without a selector. The defining polynomial of the resulting RootOf has all the real/imaginary parts of the input as roots, but in general has additional roots.

g := RootOf(x^4+x-3);

gRootOf_Z4+_Z3

(127)

re, im := evala(RealPart(g)), evala(ImaginaryPart(g));

re,imRootOf64_Z10+64_Z7144_Z6_Z4+48_Z3144_Z2_Z+3,RootOf4096_Z136144_Z91664_Z716128_Z5+2592_Z36939_Z

(128)

fsolve(op(g), 'complex');

−1.452626879,0.14429586931.324149775I,0.1442958693+1.324149775I,1.164035140

(129)
  

The second real root of the following polynomial closest to the origin does not correspond to a real part.

fsolve(op(re));

−1.452626879,−0.1442958693,0.1442958693,1.164035140

(130)

fsolve(op(im));

−1.324149775,0.,1.324149775

(131)

fsolve(op(im), 'complex');

−1.324149775,−0.66207488740.7984613741I,−0.66207488740.5098696355I,−0.6620748874+0.5098696355I,−0.6620748874+0.7984613741I,1.308331010I,0.,1.308331010I,0.66207488740.7984613741I,0.66207488740.5098696355I,0.6620748874+0.5098696355I,0.6620748874+0.7984613741I,1.324149775

(132)
  

In this example, evalc used to return RootOfs with higher degree.

# r := [evalc(Re(g)),evalc(Im(g))];

rRootOf4096_Z16+4096_Z136144_Z12128_Z10+6144_Z916128_Z8128_Z7+288_Z6+2304_Z56911_Z496_Z3+288_Z2+_Z3&comma;RootOf4096_Z166144_Z121664_Z1016128_Z8+2592_Z66939_Z4

(133)

map(factor@op, r);

_Z4+_Z364_Z6+48_Z212&comma;_Z44096_Z126144_Z81664_Z616128_Z4+2592_Z26939

(134)
• 

The Chebyshev polynomials of the first kind have the real parts of the roots of unity as roots.

evala(RealPart(RootOf(x^(2*5)+1)));

RootOf16_Z520_Z3+5_Z

(135)

expand(ChebyshevT(5,_Z));

16_Z520_Z3+5_Z

(136)
  

Here, the degree of the RootOf returned by evalc is much higher, and almost all of the factors are spurious.

evalc(Re(RootOf(x^(2*5)+1)));

RootOf1208925819614629174706176_Z100+23611832414348226068480_Z901064826772439837798563840_Z80+204717471137600488079360_Z70+89786788197682150113280_Z60+5495752133075089227776_Z5060625753209569280000_Z40+153936696153600000_Z30+2456009765625_Z20+9765625_Z10

(137)

factor(op((137)));

_Z10_Z2+1_Z8_Z6+_Z4_Z2+116_Z420_Z2+52256_Z8+160_Z4+100_Z2+252256_Z8+320_Z6+160_Z4+25216_Z4+12_Z2+12256_Z8256_Z6+96_Z4+4_Z2+12256_Z8+64_Z6+96_Z416_Z2+12

(138)

ExpressionTools

• 

A new package ExpressionTools was created which provides some tools for comparing expressions and highlighting their differences. For more information, see Tools for Comparing Expressions

ValuesUnderConstraints

• 

The ValuesUnderConstraints package is a new Maple package for Maple 2025, which is designed to support the implementation of algorithms with output, or values, depending on parameters. In other words, this new package facilitates the implementation of mechanisms for case discussion. The core concept in the package is that of a pair consisting of one or more values (arbitrary algebraic expressions) together with constraints under which these values are valid. In those pairs, variables can be real-valued or integer-valued, while constraints can be equalities or inequalities between polynomials in those variables. While polynomials of arbitrary degree are supported, the current version of this package is optimized for linear constraints.

with(ValuesUnderConstraints):

Comparing the domains of several functions

• 

Consider two functions fa&comma;b&comma;c&comma;d and ga&comma;b&comma;c&comma;d, together with their domains of definition. When are both defined, and when is only one of them defined? The command MakeCaseDiscussion answers these questions by computing a partition of the union of the domains so that on each part of this partition either fa&comma;b&comma;c&comma;d, or ga&comma;b&comma;c&comma;d, or both are defined.

vc1 := ValueUnderConstraints([f(a, b, c, d)], [a <> 0, b < 0, c > 0, d <> 0]);

vc1value RootOf_Z4+_Z1&comma;2I..12 when a0&comma;d0&comma;0<c&comma;0<b

(139)

vc2 := ValueUnderConstraints([g(a, b, c, d)], [c <> 0, d > 0, a < 0, b <> 0]);

vc2value RootOf_Z4+_Z3 when b0&comma;c0&comma;0<d&comma;0<a

(140)

MakeCaseDiscussion([vc1, vc2], output = piecewise);

RootOf_Z4+_Z1&comma;2I..12a00<c0<b0<dRootOf_Z4+_Z1&comma;2I..120<a0<c0<d0<bRootOf_Z4+_Z3&comma;RootOf_Z4+_Z1&comma;2I..120<c0<d0<a0<bRootOf_Z4+_Z3b00<d0<a0<cRootOf_Z4+_Z30<b0<c0<d0<a

(141)
• 

Consider four pairs of values and constraints.

vc1 := ValueUnderConstraints([1], [a = 0, b >= 0, c < 0, d <> 0]);

vc1value 1 when a=0&comma;d0&comma;0<c&comma;0b

(142)

vc2 := ValueUnderConstraints([2], [b = 0, a >= 0, d <> 0, c > -1]);

vc2value 2 when b=0&comma;d0&comma;0<c+1&comma;0a

(143)

vc3 := ValueUnderConstraints([3], [a <> 0, b >= 0, c < 0, d = 0]);

vc3value 3 when d=0&comma;a0&comma;0<c&comma;0b

(144)

vc4 := ValueUnderConstraints([4], [b <> 0, a >= 0, d <> 0, c = 0]);

vc4value 4 when c=0&comma;b0&comma;d0&comma;0a

(145)
• 

Use MakeCaseDiscussion to check which of these four pairs have intersecting domains.

MakeCaseDiscussion([vc1, vc2, vc3, vc4], output = piecewise);

1a=0d00<c0b0c11a=0d00<b0<c0<c+11&comma;2a=0b=0d00<c0<c+12b=0d00<c+10a0c2b=0d00<a0<c0<c+14c=0b0d00a3d=0a00<c0b

(146)

Distinguishing between real-valued and integer-valued variables

• 

Let's modify the four pairs above and specify that their variables are integer-valued.

vc1i := ValueUnderConstraints([1], [a = 0, b >= 0, c < 0, d <> 0], {a, b, c, d});

vc1ivalue 1 when a=0&comma;d0&comma;0b&comma;0c1

(147)

vc2i := ValueUnderConstraints([2], [b = 0, a >= 0, d <> 0, c > -1], {a, b, c, d});

vc2ivalue 2 when b=0&comma;d0&comma;0a&comma;0c

(148)

vc3i := ValueUnderConstraints([3], [a <> 0, b >= 0, c < 0, d = 0], {a, b, c, d});

vc3ivalue 3 when d=0&comma;a0&comma;0b&comma;0c1

(149)

vc4i := ValueUnderConstraints([4], [b <> 0, a >= 0, d <> 0, c = 0], {a, b, c, d});

vc4ivalue 4 when c=0&comma;b0&comma;d0&comma;0a

(150)
• 

Use MakeCaseDiscussion and observe the difference between the result here and the result above for the real-valued case.

MakeCaseDiscussion([vc1i, vc2i, vc3i, vc4i], output = piecewise);

4c=0b0d00a2b=0d00a0c1a=0d00b0c13d=0a00b0c1

(151)