8
$\begingroup$

I want to write a function that takes another function, say $f(x)$ and a variable $x$ as input and return True it this function can be written in the form a*Sin[b*x + c] + d, False otherwise. For example:

GetSinForm[1 + x^2,x]
False

GetSinForm[Cos[x]^2,x]
True

My idea is to convert the function with the TrigToExp and to check if the form is the typical sine-like form, namely $f(x) =\frac{1}{2i}(e^{-i\pi x} + e^{+i \pi x})$ and to check the matcing with MatchQ but this last function is too strict with pattern and I get almost always False.

$\endgroup$
8
$\begingroup$

Here is a differential equations/operator approach, leading to mathematics-based solutions instead of expression-matching ones. As the OP observes, matching algebraic expressions with expression patterns can be tricky, despite Mathematica's Optional patterns and Default values. For trigonometric functions it is usually trickier since, as @alx observes, trig functions have automatic simplification rules that change, for instance, Sin[t + Pi/2] to Cos[t]. Sometimes a mathematical approach is simpler or more robust.

Mathematical background

The expression $y = a * \sin (b * x + c)$ is the general solution to $$y'' + b^2 y = 0 \,,$$ and thus $y = a * \sin (b * x + c) + d$ is the general solution to $$y''' + b^2 y' = 0\,.\tag{1}$$ Even further, it is the general solution to $${d \over dx} {y''' \over y'} = {d \over dx} \left(-b^2\right) \,,$$ or equivalently $$y'y^{(4)}-y''y''' = 0 \,.\tag{2}$$ Equation (2) is equivalent to the vanishing of the second-order Wronskian of $y'$ and $y'''$, $$W \equiv \left|\matrix{ y' & y''' \\ y'' & y^{(4)} \\}\right| = 0 \,. \tag{3}$$ The condition $W=0$ is equivalent to $y'$ and $y'''$ being linearly dependent. A fourth-order ODE is as far as we can go, since the form $y = a * \sin (b * x + c) + d$ has four parameters.

Solution methods

A solution based on (1), using the internal, undocumented function Periodic`PeriodicFunctionPeriod is the following:

ClearAll[sinQ];
sinQ[f_, x_] := 
  With[{b = 2 Pi/Periodic`PeriodicFunctionPeriod[f, x]},
   Simplify[D[f, {x, 3}] + b^2 D[f, x]] === 0 /; FreeQ[b, $Failed]];
sinQ[__] = False;

A solution based on (2) is the following:

ClearAll[sinQ];
sinQ[f_, x_] := 
  Simplify[D[f, {x, 1}] D[f, {x, 4}] - D[f, {x, 2}] D[f, {x, 3}]] === 0;

And a solution based on (3), using the alternative PossibleZeroQ to simplifying the Wronskian, is the following:

ClearAll[sinQ];
sinQ[f_, x_] := 
  PossibleZeroQ[Det@Partition[Rest@NestList[D[#, x] &, f, 4], 2]];

Examples

sinQ[a*Sin[b*x + c] + d, x]
(*  True  *)

sinQ[5 Sin[4 (t - 1)] + Cos[2 t]^2 - 1, t]
(*  True  *)

sinQ[5 Sin[4 (t - 1)] + Cos[4 t]^2, t]
(*  False  *)

Note: Only the method that uses PossibleZeroQ works on the following example, which has approximate real parameter values:

sinQ[5 Sin[4. (t - 1)] + Cos[2. t + 0.1]^2, t]
(*  True  *)

One can use PossibleZeroQ in the other methods. It will work with the second example but not with the first, because Periodic`PeriodicFunctionPeriod fails to recognize the function with approximate coefficients as periodic. One could Rationalize the argument f in that case, if one wanted to extend that method to such as examples as this last one.

Addendum

It wasn't asked for but here is a way to compute the parameters. One can compute the offset d and amplitude b from integrals, which for a sine curve can be computed exactly and quickly with the trapezoid rule with a sampling of four points per period (see trapsample below). This can be used to verify sinQ, about whose accuracy a question arose.

ClearAll[sinParameters];
sinParameters[e_, t_] := Module[
   {df, aa, bb, cc, dd, trapsample},
   df = NestList[D[#, t] &, e, 4]; (* y''' + b^2 y' == 0 *)
   ( bb = Simplify@ Sqrt[-df[[4]]/df[[2]]];
     trapsample = Map[e /. t -> # &, Pi {1/4, 3/4, 0, 1/2, 1, 3/2}/bb];
     dd = Simplify@ Mean@ trapsample[[3 ;;]];
     aa = Simplify@ Sqrt[2 Mean[(trapsample[[;; 4]] - dd)^2]];
     cc = Simplify@ ArcSin[(e - dd)/aa /. t -> 0];
     If[df[[2]] != aa bb /. t -> -cc/bb, (* adjust quadrant of ArcSin *)
      aa = -aa;     (* alternatively, leave aa as is and set cc=Pi-cc *)
      cc = -cc
      ];
     Thread[{a, b, c, d} -> {aa, bb, cc, dd}]
     ) /; PossibleZeroQ[Det@Partition[Rest@df, 2]] (* same test as sinQ *)
   ];

On the second example above, @alx noted that @alx's method returns False. One can see that it can be written in the desired form from the following:

fn = 5 Sin[4 (t - 1)] + Cos[2 t]^2 - 1;
a * Sin[b*t + c] + d /. sinParameters[fn, t]
(*
  -(1/2)  -  1/2 Sqrt[101 - 20 Sin[4]] *
      Sin[ 4 t  -  ArcSin[(1 - 10 Sin[4])/Sqrt[101 - 20 Sin[4]]] ]
*)

fn - % // TrigToExp // FullSimplify
(*  0  *)

I wrote a symbolic analysis solver for the parameters, but the code is about twice as long and runs more than a 100 times more slowly. I didn't think it worth sharing.

sinQ[fn, t] // RepeatedTiming
sinParameters[fn, t] // RepeatedTiming
(*
  {0.000087, True}
  {0.00054, {
    a -> -(1/2) Sqrt[101 - 20 Sin[4]],
    b -> 4, 
    c -> -ArcSin[(1 - 10 Sin[4])/Sqrt[101 - 20 Sin[4]]],
    d -> -(1/2)}}
*)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Why the downvote? Is something wrong? $\endgroup$ – Michael E2 Oct 9 '19 at 18:34
  • $\begingroup$ (Not the downvoter). A slightly related observation. I was a little surprised to see this call to PossibleZeroQ return False: {a, b, c, d} = CloudGet["https://wolfr.am/HclVT0Om"]; func = 5 Sin[4 (t - 1)] + Cos[2 t]^2 - 1; PossibleZeroQ[a Sin[b t + c] + d - func] $\endgroup$ – Chip Hurst Oct 9 '19 at 19:37
  • $\begingroup$ @ChipHurst Yes, it happens for (-(1/2) - 1/2 Sqrt[101 - 20 Sin[4]] Sin[4 t - ArcTan[1/5 Sec[4] (-(1/2) + 5 Sin[4])]]) - func, too; but PossibleZeroQ is wrong, isn't it? (I'm not sure if you have a point beyond that there are limitations on symbolic computation; however, sinQ does correctly identify func as being able to be written in the OP's sine form.) $\endgroup$ – Michael E2 Oct 10 '19 at 0:38
  • $\begingroup$ Yes, PossibleZeroQ is wrong in this case. When plugging in explicit values for t, PossibleZeroQ returns True and throws a message. $\endgroup$ – Chip Hurst Oct 10 '19 at 0:38
  • $\begingroup$ @MichaelE2, +1. But I probably misunderstood OP requirements: I thought his function f(x) should be represented with only one Sin term, but your 2nd example can be written with 2 Sin terms, this gives False in my approach. $\endgroup$ – Alx Oct 10 '19 at 11:02
4
$\begingroup$

You can use TrigReduce to come to form with multiple angles instead of powers and then use MatchQ to compare the result with Sin[...] pattern. I also use here Inactive[Sin] to keep results of conversion Cos terms to corresponding Sin terms:

MatchQ[(TrigReduce[expr]//Expand)/.{Sin[x_] -> Inactive[Sin][x],
Cos[x_] -> Inactive[Sin][x + \[Pi]/2]},
a_. Inactive[Sin][b_. x + c_.] + d_. /; FreeQ[d, x]

So this will match only to a Sin[b x+c]+d, where all a, b, c, d are constants.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks, it works but there is a small bug, for example with Cos[x]^2 + x. In this case, even if the function cannot be expressed as Sin/Cos, the snippet returns True . $\endgroup$ – Marcus Garvey Oct 9 '19 at 8:32
  • $\begingroup$ @MarcusGarvey, I edited FreeQ[] term, now it works with your example in comment :) $\endgroup$ – Alx Oct 9 '19 at 8:49
0
$\begingroup$

If you only intend to check the identity a simple workaround might be

NMinimize[NIntegrate[(a Sin[b x + c] + d - Cos[x]^2)^2, {x, 0, 2 Pi}], {a, b,c, d}]
(*{9.22063*10^-15, {a -> -0.5, b -> -2., c -> -1.5708, d -> 0.5}}*) 
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.