Skip to content

Commit 2bc0561

Browse files
Write an exponential of a symbolic multiple of i arctan as powers of the linears (#1934)
* Write an exponential of a symbolic multiple of i arctan as powers of the linears e^(k i arctan(L)) is (1 + i L)^(k/2) (1 - i L)^(-k/2) and (K (1 + L^2))^q is K^q (1 + i L)^q (1 - i L)^q, both exactly for a real L and any k and q. (c + a^2 c x^2)^p/e^(2 i p arctan(a x)) is then c^p (1 - i a x)^(2p), a power of a linear; it was left unevaluated (Rubi's 5.3.6:502 and :503). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the exponentials of a symbolic multiple of i arctan in BREAKING-CHANGES Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent c1647fd commit 2bc0561

4 files changed

Lines changed: 159 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,19 @@ partial fractions divide. It is taken out in front of the integral first
607607
| `"(b*d + 2*c*d*x)^9/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 76,249 characters, the length the content-free one already had |
608608
| `"(b*d + 2*c*d*x)^8/(a + b*x + c*x^2)^3".ToEntity().Integrate("x")` | past a minute | 65,236 characters |
609609

610+
### An exponential of a symbolic multiple of i arctan is written as powers of the linears
611+
612+
**Answers where there were none.** `(c + a^2 c x^2)^p/e^(2 i p arctan(a x))` was left unevaluated. For a real `L`,
613+
`e^(k i arctan(L))` is `(1 + i L)^(k/2) (1 - i L)^(-k/2)` and `(K (1 + L^2))^q` is `K^q (1 + i L)^q (1 - i L)^q`, for any
614+
`k` and `q`, so the integrand is a product of powers of two linears, and with these exponents a single power
615+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
616+
617+
| Input | Was (2.5.0) | Now |
618+
|---|---|---|
619+
| `"(c+a^2*c*x^2)^p/e^(2*i*p*atan(a*x))".ToEntity().Integrate("x")` | `integral(...)` | 84 characters |
620+
| `"e^(2*i*p*atan(a*x))*(c+a^2*c*x^2)^p".ToEntity().Integrate("x")` | `integral(...)` | 86 characters |
621+
| `"e^(i*n*atan(a*x))/(c+a^2*c*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 123 characters |
622+
610623
### A whole power is taken out of a power of a product, in front of a constant
611624

612625
**Answers where there were none.** `(c sin(a + b x)^3)^(1/3)/x` was declined: nothing read a function under a root of

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9678,6 +9678,93 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
96789678
return ratio;
96799679
}
96809680

9681+
/// <summary>
9682+
/// An exponential of a symbolic multiple of <c>i arctan(L)</c>, <c>L</c> linear, beside
9683+
/// powers of multiples of <c>1 + L^2</c>, written as powers of the two linears:
9684+
/// <c>e^(k i arctan(L))</c> is <c>(1 + i L)^(k/2) (1 - i L)^(-k/2)</c> and
9685+
/// <c>(K (1 + L^2))^q</c> is <c>K^q (1 + i L)^q (1 - i L)^q</c>, each exactly for a real
9686+
/// <c>L</c> and any <c>k</c> and <c>q</c>: the two principal arguments are
9687+
/// <c>arctan(L)</c> and its negative, and <c>1 + L^2</c> is positive.
9688+
/// </summary>
9689+
/// <remarks>
9690+
/// Rubi's 5.3.6 <c>(c + a^2 c x^2)^p/e^(2 i p arctan(a x))</c> is then
9691+
/// <c>c^p (1 - i a x)^(2p)</c>, a power of a linear; it was left unevaluated.
9692+
/// <see cref="SolveByWritingAnExponentialOfAnInverseAlgebraically"/> takes a rational
9693+
/// multiple, and this the rest.
9694+
/// https://github.com/asc-community/AngouriMath/issues/718
9695+
/// </remarks>
9696+
internal static Entity? SolveAnExponentialOfASymbolicMultipleOfAnArctangentAsPowersOfTheLinears(Entity expr, Entity.Variable x, bool integrateByParts)
9697+
{
9698+
static IEnumerable<(Entity Factor, bool Below)> Factors(Entity e, bool below) => e switch
9699+
{
9700+
Mulf(var l, var r) => Factors(l, below).Concat(Factors(r, below)),
9701+
Divf(var l, var r) => Factors(l, below).Concat(Factors(r, !below)),
9702+
_ => new[] { (e, below) }
9703+
};
9704+
var factors = Factors(expr, false).ToList();
9705+
Entity? argument = null;
9706+
Entity? multiple = null;
9707+
foreach (var (factor, below) in factors)
9708+
{
9709+
if (factor is not Powf(var e, var exponent) || e != MathS.e || !exponent.ContainsNode(x))
9710+
continue;
9711+
if (argument is not null)
9712+
return null;
9713+
var arctangents = exponent.Nodes.OfType<Arctanf>().Distinct().ToList();
9714+
if (arctangents.Count != 1 || !TreeAnalyzer.TryGetPolyLinear(arctangents[0].Argument, x, out var slope, out _) || TreeAnalyzer.IsZero(slope))
9715+
return null;
9716+
var placeholder = Variable.CreateUnique(exponent, "u_atan");
9717+
if (!TreeAnalyzer.TryGetPolyLinear(exponent.Replace(node => node == arctangents[0] ? placeholder : node), placeholder, out var coefficient, out var free)
9718+
|| coefficient is null || free is null || coefficient.ContainsNode(x) || !TreeAnalyzer.IsZero(free))
9719+
return null;
9720+
// Simplified, since `2 i p/i` is not reduced to `2 p` short of it.
9721+
var k = (coefficient / MathS.i).Simplify();
9722+
// A rational multiple is the other rule's.
9723+
if (k.Evaled is Number.Rational)
9724+
return null;
9725+
argument = arctangents[0].Argument;
9726+
multiple = below ? -k : k;
9727+
}
9728+
if (argument is null || multiple is null)
9729+
return null;
9730+
var quadratic = 1 + MathS.Sqr(argument);
9731+
Entity constant = Number.Integer.One;
9732+
Entity plusPower = multiple / 2, minusPower = -multiple / 2;
9733+
foreach (var (factor, below) in factors)
9734+
{
9735+
if (factor is Powf(var e, var exponent) && e == MathS.e && exponent.ContainsNode(x))
9736+
continue;
9737+
if (!factor.ContainsNode(x))
9738+
{
9739+
constant = below ? constant / factor : constant * factor;
9740+
continue;
9741+
}
9742+
var (@base, power) = factor is Powf(var b, var q) ? (b, q) : (factor, (Entity)Number.Integer.One);
9743+
if (power.ContainsNode(x)
9744+
|| !TreeAnalyzer.TryGetPolyQuadratic(@base, x, out var a2, out var b2, out var c2)
9745+
|| !TreeAnalyzer.TryGetPolyQuadratic(quadratic, x, out var a3, out var b3, out var c3)
9746+
|| a2 is null || b2 is null || c2 is null || a3 is null || b3 is null || c3 is null || TreeAnalyzer.IsZero(a3))
9747+
return null;
9748+
var ratio = (a2 / a3).InnerSimplified;
9749+
if (!Functions.PartialFractions.IsZeroAsAValue((b2 - ratio * b3).InnerSimplified)
9750+
|| !Functions.PartialFractions.IsZeroAsAValue((c2 - ratio * c3).InnerSimplified))
9751+
return null;
9752+
var signed = below ? -power : power;
9753+
constant *= MathS.Pow(ratio, signed);
9754+
plusPower += signed;
9755+
minusPower += signed;
9756+
}
9757+
// Simplified, x-free and small: `-p + p` is a zero power only so.
9758+
plusPower = plusPower.Simplify();
9759+
minusPower = minusPower.Simplify();
9760+
Entity written = constant;
9761+
if (!TreeAnalyzer.IsZero(plusPower))
9762+
written *= MathS.Pow(1 + MathS.i * argument, plusPower);
9763+
if (!TreeAnalyzer.IsZero(minusPower))
9764+
written *= MathS.Pow(1 - MathS.i * argument, minusPower);
9765+
return Integration.ComputeAsAQuestionOfItsOwn(written, x, integrateByParts);
9766+
}
9767+
96819768
/// <summary>
96829769
/// An exponential of <c>i</c> times an inverse trigonometric function, written
96839770
/// algebraically: <c>e^(i arctan(L))</c> is <c>(1 + i L)/sqrt(1 + L^2)</c>,

‎Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
671671
// question asked, before the rules that spent the budget: `e^(3 i atan(a + b x))/x^2` ran out of
672672
// time where, rewritten, it is answered in half a second. It is asked again in its place below.
673673
if (AnsweringTheQuestionAsked && (answer = IndefiniteIntegralSolver.SolveByWritingAnExponentialOfAnInverseAlgebraically(expr, x, integrateByParts)) is { }) return answer;
674+
// The same for a symbolic multiple of the arctangent, beside powers of its quadratic.
675+
if (AnsweringTheQuestionAsked && (answer = IndefiniteIntegralSolver.SolveAnExponentialOfASymbolicMultipleOfAnArctangentAsPowersOfTheLinears(expr, x, integrateByParts)) is { }) return answer;
674676
// A polynomial over a symbolic quartic that splits into two quadratics in a hidden variable,
675677
// palindromic or even after a shift: the rules below spent seconds declining both.
676678
if ((answer = IndefiniteIntegralSolver.SolveAPolynomialOverASymbolicQuarticThatSplitsIntoTwoQuadratics(expr, x, integrateByParts)) is { }) return answer;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using System;
9+
using AngouriMath.Extensions;
10+
using Xunit;
11+
12+
namespace AngouriMath.Tests.Calculus
13+
{
14+
/// <summary>
15+
/// An exponential of a symbolic multiple of <c>i arctan(a x)</c> beside a power of its quadratic,
16+
/// written as powers of the two linears: Rubi's 5.3.6
17+
/// <c>(c + a^2 c x^2)^p/e^(2 i p arctan(a x))</c>, which was left unevaluated.
18+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
19+
/// </summary>
20+
[Trait("Area", "Calculus")]
21+
public sealed class AnExponentialOfASymbolicMultipleOfAnArctangentIntegralTest
22+
{
23+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
24+
{
25+
var integral = integrand.ToEntity().Integrate("x");
26+
Assert.DoesNotContain("integral(", integral.Stringize());
27+
28+
var pinned = integral.Substitute("C", 0);
29+
Entity original = integrand.ToEntity();
30+
foreach (var (name, value) in pins)
31+
{
32+
pinned = pinned.Substitute(name, value);
33+
original = original.Substitute(name, value);
34+
}
35+
var derivative = pinned.Differentiate("x");
36+
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
37+
{
38+
var got = derivative.Substitute("x", at).EvalNumerical();
39+
var want = original.Substitute("x", at).EvalNumerical();
40+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
41+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
42+
Assert.True(difference / scale < 1e-8,
43+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
44+
}
45+
}
46+
47+
[Theory]
48+
[InlineData("(c+a^2*c*x^2)^p/e^(2*i*p*atan(a*x))")]
49+
[InlineData("e^(2*i*p*atan(a*x))*(c+a^2*c*x^2)^p")]
50+
[InlineData("e^(i*n*atan(a*x))/(c+a^2*c*x^2)")]
51+
public void IsAnsweredAndDifferentiatesBack(string integrand)
52+
{
53+
DifferentiatesBack(integrand, ("a", 1.3), ("c", 0.7), ("p", 0.4), ("n", 0.6));
54+
DifferentiatesBack(integrand, ("a", -0.5), ("c", -1.5), ("p", 1.3), ("n", -2.2));
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)