Subject: Half-angle-simplification of trig functions
From: Dieter Kaiser
Date: Thu, 01 Jan 2009 20:34:21 +0100
We have the open bug SF[505443] Halfangle limitation.
I had a look at this topic. We have to adjust the factors for the cases
sin(z/2), cos(z/2), sinh(z/2) and cosh(z/2). The most general formula
for half-angle-simplification would be e. g. for the Sin function (see
e.g. functions.wolfram.com):
sin(z/2) = c(z) * sqrt((1-cos(z))/2) with a factor
c(z) = (-1)^floor(realpart(z)/(2*%%pi))
* (1- (1+ (-1)^(floor(realpart(z)/(2*%pi))
+ floor(-realpart(z)/(2*%pi))))
* unit_step(-imagpart(z)))
For a real argument this simplifies to:
c(x) = (-1)^floor(realpart(z)/(2*%%pi))
For a pure imaginary argument we get:
c(y) = (1-2*unit_step(-y))
This simplifies further to -1 for y negative and 1 for y positive. For
general complex expressions the term (-1)^(floor(expr)+floor(-expr)) is
1 for realpart(z) a multiple of 2*%pi and otherwise -1.
For the functions cos, sinh and cosh we get similiar expressions. I have
done an implementation of these general factors.
The examples at the end of this text show that the expressions simplify
well when we specialize the argument of the trig function accordingly.
Mathematically it is clear that c(z)^2 = 1. One problem is that only for
real arguments Maxima can simplify the general expression to c(z)^2 = 1.
For complex arguments this simplification only works when the sign of
the imaginary part is known to Maxima. For this case the unit_step
function will simplify.
One problem of the testsuite (Problem 33 in rtest_trig.mac) will fail.
But after restriction the argument to a positive value in the interval
(0,%pi) the example works again and as expected.
I think we can implement the most general formulas for the factors of
the half-angle-simplification. For complex arguments the result can look
very complicated. But it will simplify to correct expressions.
This is of course true for arguments with rational and integer numbers
like (1+%i)/2. For such expressions we allows get simple and correct
results. See e.g. the following example:
(%i24) sin((2*%pi-%i)/2);
(%o24) sqrt(1-cos(%i))/sqrt(2)
(%i25) sin((2*%pi+%i)/2);
(%o25) -sqrt(1-cos(%i))/sqrt(2)
What do you think. Should we do the generalization of the
half-angle-simplification to complex arguments and close the bug report?
Dieter Kaiser
Examples for the half-angle-simplification:
/* Tests for half-angles simplification */
kill(all);
done;
(old_halfangles:halfangles,halfangles:true,old_%iargs:%iargs,%
iargs:false,done);
done;
/* Sin function: The general result for real argument */
sin(x/2);
(-1)^floor(x/(2*%pi))*sqrt(1-cos(x))/sqrt(2);
/* The square simplifies correctly */
sin(x/2)^2;
(1-cos(x))/2;
/* Correct sign for negative and positive real argument */
(assume(x1>0,x1<2*%pi),done);
done;
sin(x1/2);
sqrt(1-cos(x1))/sqrt(2);
(assume(x2>-2*%pi,x2<0),done);
done;
sin(x2/2);
-sqrt(1-cos(x2))/sqrt(2);
/* Correct sign for pure imaginary argument
and complex argument with realpart a multiple of 2*%pi */
(assume(y1>0,y2<0),done);
done;
sin(%i*y1/2);
sqrt(1-cos(%i*y1))/sqrt(2);
sin(%i*y2/2);
-sqrt(1-cos(%i*y2))/sqrt(2);
sin((2*%pi+%i*y2)/2);
sqrt(1-cos(2*%pi+%i*y2))/sqrt(2);
sin((2*%pi+%i*y1)/2);
-sqrt(1-cos(2*%pi+%i*y1))/sqrt(2);
/* Simplification for negative or positive imaginary part */
(assume(yneg<0,ypos>0),done);
done;
sin((x1+%i*yneg)/2);
sqrt(1-cos(x1+%i*yneg))/sqrt(2);
sin((2*%pi+%i*yneg)/2);
sqrt(1-cos(2*%pi+%i*yneg))/sqrt(2);
sin((x1+%i*ypos)/2);
sqrt(1-cos(x1+%i*ypos))/sqrt(2);
sin((2*%pi+%i*ypos)/2);
-sqrt(1-cos(2*%pi+%i*ypos))/sqrt(2); /* for this case -1 */
/* Cos function: The general result for real argument */
cos(x/2);
(-1)^floor((x+%pi)/(2*%pi))*sqrt(1+cos(x))/sqrt(2);
/* The square simplifies correctly */
cos(x/2)^2;
(1+cos(x))/2;
/* Correct sign for real argument in (-%pi,%pi) */
(forget(x1<2*%pi,x1>0),assume(x1>-%pi,x1<%pi),done);
done;
cos(x1/2);
sqrt(1+cos(x1))/sqrt(2);
/* Correct sign for pure imaginary argument
and complex argument with realpart a multiple of %pi */
cos(%i*y1/2);
sqrt(1+cos(%i*y1))/sqrt(2);
cos(%i*y2/2);
sqrt(1+cos(%i*y2))/sqrt(2);
cos((%pi+%i*y2)/2);
sqrt(1+cos(%pi+%i*y2))/sqrt(2);
cos((%pi+%i*y1)/2);
-sqrt(1+cos(%pi+%i*y1))/sqrt(2);
/* Simplification for negative or positive imaginary part */
(assume(yneg<0,ypos>0),done);
done;
cos((x1+%i*yneg)/2);
sqrt(1+cos(x1+%i*yneg))/sqrt(2);
cos((%pi+%i*yneg)/2);
sqrt(1+cos(%pi+%i*yneg))/sqrt(2);
cos((x1+%i*ypos)/2);
sqrt(1+cos(x1+%i*ypos))/sqrt(2);
cos((%pi+%i*ypos)/2);
-sqrt(1+cos(%pi+%i*ypos))/sqrt(2); /* for this case an -1 */
/* Sinh function with Real arguments */
(assume(xpos>0,xneg<0),done);
done;
sinh(x/2);
abs(x)/x*sqrt((cosh(x)-1)/2);
sinh(x/2)^2;
(cosh(x)-1)/2;
sinh(xpos/2);
sqrt((cosh(xpos)-1)/2);
sinh(xneg/2);
-sqrt((cosh(xneg)-1)/2);
/* Sinh function with Complex arguments */
/* x1 is in (-%pi,%pi) */
sinh(%i*x1/2);
abs(x1)/x1*sqrt((cosh(%i*x1)-1)/2);
sinh((xpos+%i*x1)/2);
sqrt((xpos+%i*x1)^2)/(xpos+%i*x1)*sqrt((cosh(xpos+%i*x1)-1)/2);
sinh((xneg+%i*x1)/2);
sqrt((xneg+%i*x1)^2)/(xneg+%i*x1)*sqrt((cosh(xneg+%i*x1)-1)/2);
/* Cosh function with Real arguments */
cosh(x/2);
sqrt((cosh(x)+1)/2);
cosh(x/2)^2;
(cosh(x)+1)/2;
cosh(xpos/2);
sqrt((cosh(xpos)+1)/2);
cosh(xneg/2);
sqrt((cosh(xneg)+1)/2);
/* Cosh function with Complex arguments */
/* x1 is in (-%pi,%pi) */
cosh(%i*x1/2);
sqrt((cosh(%i*x1)+1)/2);
cosh((xpos+%i*x1)/2);
sqrt((cosh(xpos+%i*x1)+1)/2);
cosh((xneg+%i*x1)/2);
sqrt((cosh(xneg+%i*x1)+1)/2);
/* Problem 33 in rtest_trig will fail. The argument has to be restricted
to a positive interval, then the example works again */
(trig : [cos(x), sin(x), tan(x), csc(x), sec(x), cot(x)],0);
0$
(htrig : [cosh(x), sinh(x), tanh(x), csch(x), sech(x), coth(x)],0);
0$
/* We restrict to be positive value in the interval (0,3). In this
interval sin(x/2) and cos(x/2) and the corresponding hyperbolic
functions simplifies to the known simple expressions.
Remark: Better would be %pi as upper limit, but this does not work!
Is this related to known problems with assume? */
(assume(x>0,x<3),halfangles : true, 0);
0$
(xtrig : subst(x/2,x, append(trig,htrig)),0);
0$
(halfangles : false, 0);
0$
xtrig : taylor(xtrig - subst(x/2,x, append(trig,htrig)),x,0,5);
''(makelist(taylor(0,x,0,5),i,1,length(append(trig,htrig))))$
(forget(x>0,x<3),0);
0;
/* Restore global flags */
(halfangles:old_halfangles,%iargs:old_%iargs,done);
done;