Hi everyone! It's been a remarkably long time since I posted on MaplePrimes -- I should probably briefly reintroduce myself to the community here. My name is Erik Postma. I manage the mathematical software group at Maplesoft: the team that writes most of the Maple-language code in the Maple product, also known as the math library. You can find a longer introduction at this link.

One of my tasks at Maplesoft is the following. When a request for tech support comes in, our tech support team can usually answer the request by themselves. But no single person can know everything, and when specialized knowledge of Maple's mathematical library is needed, they ask my team for help. I screen such requests, answer what I can by myself, and send the even more specialized requests to the experts responsible for the appropriate part of the library.

Yesterday I received a request from a user asking how to unwrap angles occurring in an expression. This is the general idea of taking the fact that sin(phi) = 'sin'(phi + 2*Pi), and similarly for the other trig functions; and using it to modify an expression of the form sin(phi) to make it look "nicer" by adding or subtracting a multiple of 2*Pi to the angle. For a constant, real value of phi you would simply make the result be as close to 0 as possible; this is discussed in e.g. this MaplePrimes question, but the expressions that this user was interested in had arguments for the trig functions that involved variables, too.

In such cases, the easiest solution is usually to write a small piece of custom code that the user can use. You might think that we should just add all these bits and pieces to the Maple product, so that everyone can use them -- but there are several reasons why that's not usually a good idea:

  • Such code is often too specialized for general use.
  • Sometimes it is reliable enough to use if we can communicate a particular caveat to the user -- "this will not work if condition XYZ occurs" -- but if it's part of the Maple library, an unsuspecting user might try it under condition XYZ and maybe get a wrong answer.
  • This type of code code generally doesn't undergo the careful interface design, the testing process, and the documentation effort that we apply to the code that we ship as part of the product; to bring it up to the standards required for shipping it as part of Maple might increase the time spent from, say, 15 minutes, to several days.

That said, I thought this case was interesting enough to post on MaplePrimes, so that the community can take a look - maybe there is something here that can help you with your own code.

So here is the concrete question from the user. They have expressions coming from an inverse Laplace transform, such as:

with(inttrans):
F := -0.3000*(-1 + exp(-s))*s/(0.0500*s^2 + 0.1*s + 125);
f := invlaplace(F, s, t)*u(t);
# result: (.1680672269e-1*exp(1.-1.*t)*Heaviside(t-1.)*(7.141428429*sin(49.98999900*t-
#         49.98999900)-357.*cos(49.98999900*t-49.98999900))+.1680672269e-1*(-7.141428429*sin
#         (49.98999900*t)+357.*cos(49.98999900*t))*exp(-1.*t))*u(t)

I interpreted their request for unwrapping these angles as replacing the expressions of the form sin(c1 * t + c0) with versions where the constant term was unwrapped. Thinking a bit about how to be safe if unexpected expressions show up, I came up with the following solution:

unwrap_trig_functions := module()
local ModuleApply := proc(expr :: algebraic, $)
  return evalindets(expr, ':-trig', process_trig);
end proc;

local process_trig := proc(expr :: trig, $)
  local terms := convert(op(expr), ':-list', ':-`+`');
  local const, nonconst;
  const, nonconst := selectremove(type, terms, ':-complexcons');
  const := add(const);
  local result := add(nonconst) + (
    if is(const = 0) then
      0;
    else
      const := evalf(const);
      if type(const, ':-float') then
        frem(const, 2.*Pi);
      else
        frem(Re(const), 2.*Pi) + I*Im(const);
      end if;
    end if);
  return op(0, expr)(result);
end proc;
end module;

# To use this, with f defined as above:
f2 := unwrap_trig_functions(f);
# result: (.1680672269e-1*exp(1.-1.*t)*Heaviside(t-1.)*(7.141428429*sin(49.98999900*t+
#         .27548346)-357.*cos(49.98999900*t+.27548346))+.1680672269e-1*(-7.141428429*sin(
#         49.98999900*t)+357.*cos(49.98999900*t))*exp(-1.*t))*u(t)

Exercise for the reader, in case you expect to encounter very large constant terms: replace the calls to frem above with the code that Alec Mihailovs wrote in the question linked above!


Please Wait...