Skip to content

Commit 53a0fa4

Browse files
Read an exponential of an inverse below the bar as a negative multiple (#1935)
* Read an exponential of an inverse below the bar as a negative multiple x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2)) wrote the exponential as (1 + i a x)/sqrt(1 + a^2 x^2) below the bar, a linear with i in it the rational rules do not read there. Taken above the bar with its exponent negated, a negative whole multiple is written over the conjugate, (1 - i L)^(-n) (1 + L^2)^(n/2): x^2 (1 - i a x)/(c^(3/2) (1 + a^2 x^2)^2), which is answered (Rubi's 5.3.6:513). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the exponentials of i arctan below the bar 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 2bc0561 commit 53a0fa4

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,21 @@ no numeric root finder applies, and its discriminant vanishes identically. Such
595595
| `"1/(x^3-3*b*x+2*b^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 122 characters |
596596
| `"1/(-9*b*x+9*x^3+2*b^(3/2)*sqrt(3))".ToEntity().Integrate("x")` | `integral(...)` | 176 characters |
597597

598+
### An exponential of i arctan below the bar is read as a negative multiple
599+
600+
**Answers where there were none.** `x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))` was left unevaluated: the exponential
601+
below the bar was read as `n = 1`. It is taken above the bar with its exponent negated, and for a negative whole `n`,
602+
`e^(n i arctan(L))` is written over the conjugate, `(1 - i L)^(-n) (1 + L^2)^(n/2)`, which puts the linear with `i` in
603+
it above the bar where the rational rules read it
604+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
605+
606+
| Input | Was (2.5.0) | Now |
607+
|---|---|---|
608+
| `"x^2/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 211 characters |
609+
| `"x/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 109 characters |
610+
| `"1/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | 132 characters |
611+
| `"x^2/(e^(5*i*atan(a*x))*(c+a^2*c*x^2)^(27/2))".ToEntity().Integrate("x")` | `integral(...)` | 189 characters |
612+
598613
### A symbol shared by a numerator sum's coefficients is taken out before the partial fractions
599614

600615
**Answers where there were none.** `(b d + 2c d x)^9/(a + b x + c x^2)^3` ran past a minute, where

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9778,6 +9778,21 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
97789778
/// </summary>
97799779
internal static Entity? SolveByWritingAnExponentialOfAnInverseAlgebraically(Entity expr, Entity.Variable x, bool integrateByParts)
97809780
{
9781+
// An exponential below the bar taken above it with its exponent negated, so that a
9782+
// negative multiple is read as one: `x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))` is
9783+
// `x^2 e^(-i arctan(a x))/(c + a^2 c x^2)^(3/2)`, which is written over the conjugate.
9784+
if (expr is Divf(var overIt, var underIt) && Mulf.LinearChildren(underIt).Any(IsAnExponentialOfAnInverse))
9785+
{
9786+
Entity kept = Number.Integer.One, raised = Number.Integer.One;
9787+
foreach (var factor in Mulf.LinearChildren(underIt))
9788+
if (IsAnExponentialOfAnInverse(factor) && factor is Powf(_, var power))
9789+
raised *= MathS.Pow(MathS.e, -power);
9790+
else
9791+
kept *= factor;
9792+
expr = overIt * raised / kept;
9793+
}
9794+
bool IsAnExponentialOfAnInverse(Entity node) => node is Powf(var e0, var power0) && e0 == MathS.e && power0.ContainsNode(x)
9795+
&& power0.Nodes.Any(inner => inner is Arctanf or Arcsinf or Arccosf);
97819796
var rewrote = false;
97829797
// A power of a quadratic in x outside the exponentials, which `(1 + L^2)^(-n/2)` meets.
97839798
var quadraticElsewhere = expr.Nodes.Any(node => node is Powf(var q, _) && q.ContainsNode(x) && !q.Nodes.Any(inner => inner is Arctanf or Arcsinf or Arccosf)
@@ -9846,6 +9861,16 @@ node is Powf(var @base, Number.Rational power) && power is not Number.Integer &&
98469861
var halfPower = Number.Rational.Create(n.ERational.Negate().Divide(2));
98479862
if (n is not Number.Integer)
98489863
return MathS.Pow((1 + MathS.i * argument) / (1 - MathS.i * argument), Number.Rational.Create(n.ERational.Divide(2)));
9864+
// A negative n over the conjugate, `(1 + i L)^(-1)` being `(1 - i L)/(1 + L^2)`:
9865+
// the linear with `i` in it is then above the bar, where the rational rules read
9866+
// it, and `x^2/((1 + i a x)(1 + a^2 x^2))` beside it was left unevaluated where
9867+
// `x^2 (1 - i a x)/(1 + a^2 x^2)^2` is answered.
9868+
if (n is Number.Integer { IsNegative: true })
9869+
{
9870+
var conjugatePower = Number.Integer.Create(((Number.Integer)n).EInteger.Negate());
9871+
return (conjugatePower == Number.Integer.One ? 1 - MathS.i * argument : MathS.Pow(1 - MathS.i * argument, conjugatePower))
9872+
* MathS.Pow(1 + MathS.Sqr(argument), Number.Rational.Create(n.ERational.Divide(2)));
9873+
}
98499874
return (n == Number.Integer.One ? 1 + MathS.i * argument : MathS.Pow(1 + MathS.i * argument, n)) * MathS.Pow(1 + MathS.Sqr(argument), halfPower);
98509875
}
98519876
Entity unit = inverse is Arcsinf
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 <c>i arctan(a x)</c> below the bar, taken above it as a negative multiple and
16+
/// written over the conjugate: Rubi's 5.3.6 <c>x^2/(e^(i arctan(a x)) (c + a^2 c x^2)^(3/2))</c>,
17+
/// 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 AnExponentialOfAnArctangentBelowTheBarIntegralTest
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("x^2/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
49+
[InlineData("x/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
50+
[InlineData("1/(e^(i*atan(a*x))*(c+a^2*c*x^2)^(3/2))")]
51+
public void IsAnsweredAndDifferentiatesBack(string integrand)
52+
{
53+
DifferentiatesBack(integrand, ("a", 1.3), ("c", 0.7));
54+
DifferentiatesBack(integrand, ("a", -0.5), ("c", 2.5));
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)