Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ coefficients are the roots of that quadratic, and the polynomial over it is spli
| `"1/(4*a*c+4*c^2*x^2+4*c*d*x^3+d^2*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 3,920 characters |
| `"x/(4*a*c+4*c^2*x^2+4*c*d*x^3+d^2*x^4)".ToEntity().Integrate("x")` | `integral(...)` | 2,315 characters |

### An exponential of a quadratic over a power of the linear through its vertex is a moment about it

**Answers where there were none.** `f^(a + b x + c x^2)/(b + 2 c x)` was declined, and so were five more of Rubi's 2.3,
`f^(a + b x + c x^2)/(b + 2 c x)^n` and `f^(b x + c x^2)/(b + 2 c x)^n` for `n` up to 3. Under `u = b + 2 c x` the exponent is
`u^2/(4 c) + a - b^2/(4 c)`, with no linear part, and the integrand is a moment about 0 of the Gaussian: the
exponential integral of the square for an odd `n`, the error function for an even one
([#233](https://github.com/asc-community/AngouriMath/issues/233)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"f^(a+b*x+c*x^2)/(b+2*c*x)".ToEntity().Integrate("x")` | `integral(...)` | `f^(a - b^2/(4 c)) Ei(ln(f) (b + 2 c x)^2/(4 c))/(4 c)` |
| `"e^(x^2+2*x)/(x+2)".ToEntity().Integrate("x")` | `integral(...)` | `integral(...)`, not through the vertex |

### `|` is divisibility, and was disjunction

**Read this one first.** It is the only entry here that changes what existing input *means* rather
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6995,6 +6995,65 @@ private static bool TryReadAMonomial(Entity term, Entity.Variable x, out ERation
return Functions.PartialFractions.Bare((constant * shifted / d * (sum + ei * MathS.Ei(rate * u)).Substitute(u, linear)).InnerSimplified);
}

/// <summary>
/// An exponential of a quadratic over a whole power of the linear through its vertex:
/// <c>F^(A x^2 + B x + C)/(p + q x)^n</c> with <c>2 A p = q B</c>, which is how
/// <c>f^(a + b x + c x^2)/(b + 2 c x)^n</c> arrives. Under <c>u = p + q x</c> the exponent is
/// <c>A/q^2 u^2 + C - B^2/(4 A)</c>, with no linear part, and the integrand
/// <c>F^(A/q^2 u^2 + C - B^2/(4 A))/(q u^n)</c> is a moment about 0 of the Gaussian: the
/// exponential integral of the square for an odd <c>n</c>, the error function for an even
/// one. Rubi's 2.3. https://github.com/asc-community/AngouriMath/issues/233
/// </summary>
/// <remarks>
/// Only through the vertex: anywhere else the shift leaves a linear part in the exponent,
/// and the moments about 0 do not answer it. The exponent in <c>u</c> is written from the
/// coefficients, not by substituting <c>x = (u - p)/q</c> and asking the simplifier to see
/// that the linear part cancels.
/// </remarks>
internal static Entity? SolveAnExponentialOfAQuadraticOverTheLinearThroughItsVertex(Entity expr, Entity.Variable x, bool integrateByParts)
{
if (expr is not Divf)
return null;
Powf? exponential = null;
Entity? linear = null;
var power = EInteger.Zero;
Entity constant = Number.Integer.One;
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
{
if (!factor.ContainsNode(x))
{
constant = underneath ? constant / factor : constant * factor;
continue;
}
if (!underneath && exponential is null && factor is Powf(var @base, var exponent) && !@base.ContainsNode(x) && exponent.ContainsNode(x))
{
exponential = (Powf)factor;
continue;
}
// The linear, or a whole power of it, below the bar; nothing else.
if (!underneath)
return null;
var (candidate, k) = factor is Powf(var below, Number.Integer n) && n.EInteger.Sign > 0 && n.EInteger.CanFitInInt32()
? (below, n.EInteger) : (factor, EInteger.One);
if (linear is not null && linear != candidate)
return null;
linear = candidate;
power += k;
}
if (exponential is null || linear is null || power.CompareTo(EInteger.FromInt32(32)) > 0
|| !TreeAnalyzer.TryGetPolyLinear(linear, x, out var q, out var p) || TreeAnalyzer.IsZero(q)
|| !TreeAnalyzer.TryGetPolyQuadratic(exponential.Exponent, x, out var a, out var b, out var c) || TreeAnalyzer.IsZero(a)
|| !VanishesIdentically(2 * a * p - q * b))
return null;
var u = Variable.CreateUnique(expr, "u");
var exponentInU = (a / MathS.Sqr(q)).InnerSimplified * MathS.Sqr(u) + (c - MathS.Sqr(b) / (4 * a)).InnerSimplified;
var question = MathS.Pow(exponential.Base, exponentInU) / (power.Equals(EInteger.One) ? u : MathS.Pow(u, Number.Integer.Create(power)));
if (Integration.ComputeAsAQuestionOfItsOwn(question, u, integrateByParts) is not { } answer
|| answer.Nodes.Any(node => node is Integralf || node == MathS.NaN))
return null;
return (constant / q * answer.Substitute(u, linear)).InnerSimplified;
}

/// <summary>
/// <c>int u^m e^(k u) du</c>, where <paramref name="exponential"/> is <c>e^(k u)</c>, as its
/// elementary part and the coefficient of <c>Ei(k u)</c> in it:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,8 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
if ((answer = IndefiniteIntegralSolver.SolveAProductOfExponentialsOfQuadratics(expr, x)) is { }) return answer;
// An exponential of a linear over a power of a linear, onto the exponential integral.
if ((answer = IndefiniteIntegralSolver.SolveAnExponentialOfALinearOverAPowerOfALinear(expr, x)) is { }) return answer;
// And of a quadratic over a power of the linear through its vertex, onto the moments about 0.
if ((answer = IndefiniteIntegralSolver.SolveAnExponentialOfAQuadraticOverTheLinearThroughItsVertex(expr, x, integrateByParts)) is { }) return answer;
// And a sum of exponentials, which is how sinh and cosh arrive, onto Shi and Chi.
if ((answer = IndefiniteIntegralSolver.SolveAHyperbolicOfALinearOverAPowerOfALinear(expr, x)) is { }) return answer;
// And either over several linears, split into partial fractions over them first, as the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// An exponential of a quadratic over a power of the linear through its vertex,
/// <c>f^(a + b x + c x^2)/(b + 2 c x)^n</c>. Under <c>u = b + 2 c x</c> the exponent has no linear
/// part, and the integrand is a moment about 0 of the Gaussian, which the integrator answers.
/// They were declined. Rubi's 2.3.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class AnExponentialOfAQuadraticOverTheLinearThroughItsVertexIntegralTest
{
[Theory]
[InlineData("f^(a + b*x + c*x^2)/(b + 2*c*x)")]
[InlineData("f^(a + b*x + c*x^2)/(b + 2*c*x)^2")]
[InlineData("f^(a + b*x + c*x^2)/(b + 2*c*x)^3")]
[InlineData("f^(b*x + c*x^2)/(b + 2*c*x)^3")]
[InlineData("e^(x^2 + 2*x)/(x + 1)")]
public void AMomentAboutTheVertex(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("f", 2.3);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -2.9, -0.4, 0.3, 0.8, 1.3, 2.1 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}

[Fact]
public void NotThroughTheVertexIsDeclined()
=> Assert.Contains("integral(", "e^(x^2 + 2*x)/(x + 2)".ToEntity().Integrate("x").Stringize());
}
}
Loading