That identity is correct for holomorphic functions, but we have to be
careful with some functions not being holomorphic everywhere. On branch
cuts, it is wrong. For a discussion, see:
<http://www.ginac.de/pipermail/ginac-list/2010-April/001601.html>.
This patch changes the default behavior of user-defined functions. From
now on, a user-defined conjugate_func must be registered, in order to
evaluate conjugate(f(x)) -> f(conjugate(x)). This patch also adds such
functions for most initially known functions.
-If you declare your own GiNaC functions, then they will conjugate themselves
-by conjugating their arguments. This is the default strategy. If you want to
-change this behavior, you have to supply a specialized conjugation method
-for your function (see @ref{Symbolic functions} and the GiNaC source-code
-for @code{abs} as an example). Also, specialized methods can be provided
-to take real and imaginary parts of user-defined functions.
+If you declare your own GiNaC functions and you want to conjugate them, you
+will have to supply a specialized conjugation method for them (see
+@ref{Symbolic functions} and the GiNaC source-code for @code{abs} as an
+example). GiNaC does not automatically conjugate user-supplied functions
+by conjugating their arguments because this would be incorrect on branch
+cuts. Also, specialized methods can be provided to take real and imaginary
+parts of user-defined functions.
@node Solving linear systems of equations, Input/output, Complex expressions, Methods and functions
@c node-name, next, previous, up
@node Solving linear systems of equations, Input/output, Complex expressions, Methods and functions
@c node-name, next, previous, up
const function_options & opt = registered_functions()[serial];
if (opt.conjugate_f==0) {
const function_options & opt = registered_functions()[serial];
if (opt.conjugate_f==0) {
- return exprseq::conjugate();
+ return conjugate_function(*this).hold();
}
if (opt.conjugate_use_exvector_args) {
}
if (opt.conjugate_use_exvector_args) {
evalf_func(step_evalf).
series_func(step_series).
conjugate_func(step_conjugate).
evalf_func(step_evalf).
series_func(step_series).
conjugate_func(step_conjugate).
- real_part_func(step_real_part).
- imag_part_func(step_imag_part));
+ real_part_func(step_real_part).
+ imag_part_func(step_imag_part));
//////////
// Complex sign
//////////
// Complex sign
throw do_taylor(); // caught by function::series()
}
throw do_taylor(); // caught by function::series()
}
+static ex Li2_conjugate(const ex & x)
+{
+ // conjugate(Li2(x))==Li2(conjugate(x)) unless on the branch cuts which
+ // run along the positive real axis beginning at 1.
+ if (x.info(info_flags::negative)) {
+ return Li2(x);
+ }
+ if (is_exactly_a<numeric>(x) &&
+ (!x.imag_part().is_zero() || x < *_num1_p)) {
+ return Li2(x.conjugate());
+ }
+ return conjugate_function(Li2(x)).hold();
+}
+
REGISTER_FUNCTION(Li2, eval_func(Li2_eval).
evalf_func(Li2_evalf).
derivative_func(Li2_deriv).
series_func(Li2_series).
REGISTER_FUNCTION(Li2, eval_func(Li2_eval).
evalf_func(Li2_evalf).
derivative_func(Li2_deriv).
series_func(Li2_series).
+ conjugate_func(Li2_conjugate).
latex_name("\\mathrm{Li}_2"));
//////////
latex_name("\\mathrm{Li}_2"));
//////////
+static ex lgamma_conjugate(const ex & x)
+{
+ // conjugate(lgamma(x))==lgamma(conjugate(x)) unless on the branch cut
+ // which runs along the negative real axis.
+ if (x.info(info_flags::positive)) {
+ return lgamma(x);
+ }
+ if (is_exactly_a<numeric>(x) &&
+ !x.imag_part().is_zero()) {
+ return lgamma(x.conjugate());
+ }
+ return conjugate_function(lgamma(x)).hold();
+}
+
+
REGISTER_FUNCTION(lgamma, eval_func(lgamma_eval).
evalf_func(lgamma_evalf).
derivative_func(lgamma_deriv).
series_func(lgamma_series).
REGISTER_FUNCTION(lgamma, eval_func(lgamma_eval).
evalf_func(lgamma_evalf).
derivative_func(lgamma_deriv).
series_func(lgamma_series).
+ conjugate_func(lgamma_conjugate).
latex_name("\\log \\Gamma"));
latex_name("\\log \\Gamma"));
+static ex tgamma_conjugate(const ex & x)
+{
+ // conjugate(tgamma(x))==tgamma(conjugate(x))
+ return tgamma(x.conjugate());
+}
+
+
REGISTER_FUNCTION(tgamma, eval_func(tgamma_eval).
evalf_func(tgamma_evalf).
derivative_func(tgamma_deriv).
series_func(tgamma_series).
REGISTER_FUNCTION(tgamma, eval_func(tgamma_eval).
evalf_func(tgamma_evalf).
derivative_func(tgamma_deriv).
series_func(tgamma_series).
+ conjugate_func(tgamma_conjugate).
derivative_func(beta_deriv).
series_func(beta_series).
latex_name("\\mathrm{B}").
derivative_func(beta_deriv).
series_func(beta_series).
latex_name("\\mathrm{B}").
- set_symmetry(sy_symm(0, 1)));
+ set_symmetry(sy_symm(0, 1)));
return exp(GiNaC::real_part(x))*sin(GiNaC::imag_part(x));
}
return exp(GiNaC::real_part(x))*sin(GiNaC::imag_part(x));
}
+static ex exp_conjugate(const ex & x)
+{
+ // conjugate(exp(x))==exp(conjugate(x))
+ return exp(x.conjugate());
+}
+
REGISTER_FUNCTION(exp, eval_func(exp_eval).
evalf_func(exp_evalf).
derivative_func(exp_deriv).
real_part_func(exp_real_part).
imag_part_func(exp_imag_part).
REGISTER_FUNCTION(exp, eval_func(exp_eval).
evalf_func(exp_evalf).
derivative_func(exp_deriv).
real_part_func(exp_real_part).
imag_part_func(exp_imag_part).
+ conjugate_func(exp_conjugate).
latex_name("\\exp"));
//////////
latex_name("\\exp"));
//////////
return atan2(GiNaC::imag_part(x), GiNaC::real_part(x));
}
return atan2(GiNaC::imag_part(x), GiNaC::real_part(x));
}
+static ex log_conjugate(const ex & x)
+{
+ // conjugate(log(x))==log(conjugate(x)) unless on the branch cut which
+ // runs along the negative real axis.
+ if (x.info(info_flags::positive)) {
+ return log(x);
+ }
+ if (is_exactly_a<numeric>(x) &&
+ !x.imag_part().is_zero()) {
+ return log(x.conjugate());
+ }
+ return conjugate_function(log(x)).hold();
+}
+
REGISTER_FUNCTION(log, eval_func(log_eval).
evalf_func(log_evalf).
derivative_func(log_deriv).
series_func(log_series).
real_part_func(log_real_part).
imag_part_func(log_imag_part).
REGISTER_FUNCTION(log, eval_func(log_eval).
evalf_func(log_evalf).
derivative_func(log_deriv).
series_func(log_series).
real_part_func(log_real_part).
imag_part_func(log_imag_part).
+ conjugate_func(log_conjugate).
latex_name("\\ln"));
//////////
latex_name("\\ln"));
//////////
return sinh(GiNaC::imag_part(x))*cos(GiNaC::real_part(x));
}
return sinh(GiNaC::imag_part(x))*cos(GiNaC::real_part(x));
}
+static ex sin_conjugate(const ex & x)
+{
+ // conjugate(sin(x))==sin(conjugate(x))
+ return sin(x.conjugate());
+}
+
REGISTER_FUNCTION(sin, eval_func(sin_eval).
evalf_func(sin_evalf).
derivative_func(sin_deriv).
real_part_func(sin_real_part).
imag_part_func(sin_imag_part).
REGISTER_FUNCTION(sin, eval_func(sin_eval).
evalf_func(sin_evalf).
derivative_func(sin_deriv).
real_part_func(sin_real_part).
imag_part_func(sin_imag_part).
+ conjugate_func(sin_conjugate).
latex_name("\\sin"));
//////////
latex_name("\\sin"));
//////////
return -sinh(GiNaC::imag_part(x))*sin(GiNaC::real_part(x));
}
return -sinh(GiNaC::imag_part(x))*sin(GiNaC::real_part(x));
}
+static ex cos_conjugate(const ex & x)
+{
+ // conjugate(cos(x))==cos(conjugate(x))
+ return cos(x.conjugate());
+}
+
REGISTER_FUNCTION(cos, eval_func(cos_eval).
evalf_func(cos_evalf).
derivative_func(cos_deriv).
real_part_func(cos_real_part).
imag_part_func(cos_imag_part).
REGISTER_FUNCTION(cos, eval_func(cos_eval).
evalf_func(cos_evalf).
derivative_func(cos_deriv).
real_part_func(cos_real_part).
imag_part_func(cos_imag_part).
+ conjugate_func(cos_conjugate).
latex_name("\\cos"));
//////////
latex_name("\\cos"));
//////////
return (sin(x)/cos(x)).series(rel, order, options);
}
return (sin(x)/cos(x)).series(rel, order, options);
}
+static ex tan_conjugate(const ex & x)
+{
+ // conjugate(tan(x))==tan(conjugate(x))
+ return tan(x.conjugate());
+}
+
REGISTER_FUNCTION(tan, eval_func(tan_eval).
evalf_func(tan_evalf).
derivative_func(tan_deriv).
series_func(tan_series).
real_part_func(tan_real_part).
imag_part_func(tan_imag_part).
REGISTER_FUNCTION(tan, eval_func(tan_eval).
evalf_func(tan_evalf).
derivative_func(tan_deriv).
series_func(tan_series).
real_part_func(tan_real_part).
imag_part_func(tan_imag_part).
+ conjugate_func(tan_conjugate).
latex_name("\\tan"));
//////////
latex_name("\\tan"));
//////////
return power(1-power(x,_ex2),_ex_1_2);
}
return power(1-power(x,_ex2),_ex_1_2);
}
+static ex asin_conjugate(const ex & x)
+{
+ // conjugate(asin(x))==asin(conjugate(x)) unless on the branch cuts which
+ // run along the real axis outside the interval [-1, +1].
+ if (is_exactly_a<numeric>(x) &&
+ (!x.imag_part().is_zero() || (x > *_num_1_p && x < *_num1_p))) {
+ return asin(x.conjugate());
+ }
+ return conjugate_function(asin(x)).hold();
+}
+
REGISTER_FUNCTION(asin, eval_func(asin_eval).
evalf_func(asin_evalf).
derivative_func(asin_deriv).
REGISTER_FUNCTION(asin, eval_func(asin_eval).
evalf_func(asin_evalf).
derivative_func(asin_deriv).
+ conjugate_func(asin_conjugate).
latex_name("\\arcsin"));
//////////
latex_name("\\arcsin"));
//////////
return -power(1-power(x,_ex2),_ex_1_2);
}
return -power(1-power(x,_ex2),_ex_1_2);
}
+static ex acos_conjugate(const ex & x)
+{
+ // conjugate(acos(x))==acos(conjugate(x)) unless on the branch cuts which
+ // run along the real axis outside the interval [-1, +1].
+ if (is_exactly_a<numeric>(x) &&
+ (!x.imag_part().is_zero() || (x > *_num_1_p && x < *_num1_p))) {
+ return acos(x.conjugate());
+ }
+ return conjugate_function(acos(x)).hold();
+}
+
REGISTER_FUNCTION(acos, eval_func(acos_eval).
evalf_func(acos_evalf).
derivative_func(acos_deriv).
REGISTER_FUNCTION(acos, eval_func(acos_eval).
evalf_func(acos_evalf).
derivative_func(acos_deriv).
+ conjugate_func(acos_conjugate).
latex_name("\\arccos"));
//////////
latex_name("\\arccos"));
//////////
+static ex atan_conjugate(const ex & x)
+{
+ // conjugate(atan(x))==atan(conjugate(x)) unless on the branch cuts which
+ // run along the imaginary axis outside the interval [-I, +I].
+ if (x.info(info_flags::real))
+ return atan(x);
+ if (is_exactly_a<numeric>(x)) {
+ const numeric x_re = ex_to<numeric>(x.real_part());
+ const numeric x_im = ex_to<numeric>(x.imag_part());
+ if (!x_re.is_zero() ||
+ (x_im > *_num_1_p && x_im < *_num1_p))
+ return atan(x.conjugate());
+ }
+ return conjugate_function(atan(x)).hold();
+}
+
REGISTER_FUNCTION(atan, eval_func(atan_eval).
evalf_func(atan_evalf).
derivative_func(atan_deriv).
series_func(atan_series).
REGISTER_FUNCTION(atan, eval_func(atan_eval).
evalf_func(atan_evalf).
derivative_func(atan_deriv).
series_func(atan_series).
+ conjugate_func(atan_conjugate).
latex_name("\\arctan"));
//////////
latex_name("\\arctan"));
//////////
return power(_ex1+power(x,_ex2),_ex_1_2);
}
return power(_ex1+power(x,_ex2),_ex_1_2);
}
+static ex asinh_conjugate(const ex & x)
+{
+ // conjugate(asinh(x))==asinh(conjugate(x)) unless on the branch cuts which
+ // run along the imaginary axis outside the interval [-I, +I].
+ if (x.info(info_flags::real))
+ return asinh(x);
+ if (is_exactly_a<numeric>(x)) {
+ const numeric x_re = ex_to<numeric>(x.real_part());
+ const numeric x_im = ex_to<numeric>(x.imag_part());
+ if (!x_re.is_zero() ||
+ (x_im > *_num_1_p && x_im < *_num1_p))
+ return asinh(x.conjugate());
+ }
+ return conjugate_function(asinh(x)).hold();
+}
+
REGISTER_FUNCTION(asinh, eval_func(asinh_eval).
evalf_func(asinh_evalf).
REGISTER_FUNCTION(asinh, eval_func(asinh_eval).
evalf_func(asinh_evalf).
- derivative_func(asinh_deriv));
+ derivative_func(asinh_deriv).
+ conjugate_func(asinh_conjugate));
//////////
// inverse hyperbolic cosine (trigonometric function)
//////////
// inverse hyperbolic cosine (trigonometric function)
return power(x+_ex_1,_ex_1_2)*power(x+_ex1,_ex_1_2);
}
return power(x+_ex_1,_ex_1_2)*power(x+_ex1,_ex_1_2);
}
+static ex acosh_conjugate(const ex & x)
+{
+ // conjugate(acosh(x))==acosh(conjugate(x)) unless on the branch cut
+ // which runs along the real axis from +1 to -inf.
+ if (is_exactly_a<numeric>(x) &&
+ (!x.imag_part().is_zero() || x > *_num1_p)) {
+ return acosh(x.conjugate());
+ }
+ return conjugate_function(acosh(x)).hold();
+}
+
REGISTER_FUNCTION(acosh, eval_func(acosh_eval).
evalf_func(acosh_evalf).
REGISTER_FUNCTION(acosh, eval_func(acosh_eval).
evalf_func(acosh_evalf).
- derivative_func(acosh_deriv));
+ derivative_func(acosh_deriv).
+ conjugate_func(acosh_conjugate));
//////////
// inverse hyperbolic tangent (trigonometric function)
//////////
// inverse hyperbolic tangent (trigonometric function)
+static ex atanh_conjugate(const ex & x)
+{
+ // conjugate(atanh(x))==atanh(conjugate(x)) unless on the branch cuts which
+ // run along the real axis outside the interval [-1, +1].
+ if (is_exactly_a<numeric>(x) &&
+ (!x.imag_part().is_zero() || (x > *_num_1_p && x < *_num1_p))) {
+ return atanh(x.conjugate());
+ }
+ return conjugate_function(atanh(x)).hold();
+}
+
REGISTER_FUNCTION(atanh, eval_func(atanh_eval).
evalf_func(atanh_evalf).
derivative_func(atanh_deriv).
REGISTER_FUNCTION(atanh, eval_func(atanh_eval).
evalf_func(atanh_evalf).
derivative_func(atanh_deriv).
- series_func(atanh_series));
+ series_func(atanh_series).
+ conjugate_func(atanh_conjugate));
ex power::conjugate() const
{
ex power::conjugate() const
{
- ex newbasis = basis.conjugate();
- ex newexponent = exponent.conjugate();
- if (are_ex_trivially_equal(basis, newbasis) && are_ex_trivially_equal(exponent, newexponent)) {
- return *this;
+ // conjugate(pow(x,y))==pow(conjugate(x),conjugate(y)) unless on the
+ // branch cut which runs along the negative real axis.
+ if (basis.info(info_flags::positive)) {
+ ex newexponent = exponent.conjugate();
+ if (are_ex_trivially_equal(exponent, newexponent)) {
+ return *this;
+ }
+ return (new power(basis, newexponent))->setflag(status_flags::dynallocated);
+ }
+ if (exponent.info(info_flags::integer)) {
+ ex newbasis = basis.conjugate();
+ if (are_ex_trivially_equal(basis, newbasis)) {
+ return *this;
+ }
+ return (new power(newbasis, exponent))->setflag(status_flags::dynallocated);
- return (new power(newbasis, newexponent))->setflag(status_flags::dynallocated);
+ return conjugate_function(*this).hold();
}
ex power::real_part() const
}
ex power::real_part() const