Skip to content

Commit be7e92e

Browse files
Integrate f provided C as the integral of f, provided C, where C is free of x (#2057)
A condition with no x in it holds for every x or for none, so the integral of f provided C is the integral of f, provided C. Inner simplification writes one at the top of a question a rule hands on -- d^0 is 1 provided not d = 0 -- and no rule below read it: (x^2 sin(x)) provided not k = 0 was declined where x^2 sin(x) is answered. Asked as the same question, so that a rule scoped to the one asked still reads it, and only at the question asked: below it, the partial fractions of Rubi's 1.2.3.2 (d f + e f x)/(a + b (d + e x)^2 + c (d + e x)^4) hand on a piece under not e = 0 that, answered, took 0.9 GB for the answer the rest gives anyway. Over the whole corpus, the rule below the top moved no verdict. Part of #233. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b7977b2 commit be7e92e

3 files changed

Lines changed: 82 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,21 @@ powers of `c + d x` itself were read before ([#233](https://github.com/asc-commu
8383
| `"e^(k/(c+d*x))/(a+b*x)".ToEntity().Integrate("x")` | `integral(...)` | two `Ei`, of `k/(c + d x)` and of `k/(c + d x) - k b/(a d - b c)` |
8484
| `"f^(c/(a+b*x)^2)*x^4".ToEntity().Integrate("x")` | `integral(...)` | 1,262 characters, the error function and powers of `1/(a + b x)` |
8585

86+
### An integrand under a condition free of x is integrated under the condition
87+
88+
**Answers where there were none.** A condition with no x in it holds for every x or for none, so the integral of
89+
`f provided C` is the integral of f, provided C. `(x^2 sin(x)) provided not k = 0` was declined where `x^2 sin(x)` is
90+
answered, and so was `(1/((a + b x) 3 x)) provided not d = 0`. Inner simplification writes such a condition at the top
91+
of the questions rules hand on: `d^0` is `1 provided not d = 0`. Before this change master integrated
92+
`(1/x) provided a > 0` to `ln(x/a)`, which has a value where `a < 0` and the integrand has none; it is
93+
`ln(x) provided a > 0` ([#233](https://github.com/asc-community/AngouriMath/issues/233)).
94+
95+
| Input | Was (2.5.0) | Now |
96+
|---|---|---|
97+
| `"(x^2*sin(x)) provided not k = 0".ToEntity().Integrate("x")` | `integral(...)` | 72 characters |
98+
| `"(1/((a+b*x)*3*x)) provided not d = 0".ToEntity().Integrate("x")` | `integral(...)` | 289 characters |
99+
| `"(1/x) provided a > 0".ToEntity().Integrate("x")` | `integral(...)` | 26 characters, `(ln(x) provided a > 0) + C` |
100+
86101
### A transcendental function of one shifted linear is integrated in the linear
87102

88103
**Answers where there were none.** Rubi's 2.3 `F^(a + b/(c + d x)^3)/(c + d x)^19` and 3.4 `ln(c (d + e/(f + g x)^3)^q)`

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,17 @@ internal static bool ASumOverRootsWouldAnswer
684684
private static Entity? ComputeIndefiniteIntegralUncached(Entity expr, Entity.Variable x, bool integrateByParts)
685685
{
686686
if (!expr.ContainsNode(x)) return expr * x; // base case, handle here
687+
// A condition with no x in it holds for every x or for none, so the integral of `f provided C`
688+
// is the integral of f, provided C. Inner simplification writes one at the top of a question a
689+
// rule hands on -- `d^0` is `1 provided not d = 0` -- and no rule below reads it: AJ's kept
690+
// spelling of `1/((c + d x)(a + b (c + d x)^3))` lost eight rows to that. The same question,
691+
// so that a rule scoped to the one asked still reads it. Only the question asked: below it, the
692+
// partial fractions of Rubi's 1.2.3.2 `(d f + e f x)/(a + b (d + e x)^2 + c (d + e x)^4)` hand on a
693+
// piece with `provided not e = 0` that answered took 0.9 GB, for the answer the rest gives anyway.
694+
if (expr is Entity.Providedf(var underTheCondition, var condition) && !condition.ContainsNode(x) && AnsweringTheQuestionAsked)
695+
return ComputeAsTheSameQuestion(underTheCondition, x, integrateByParts) is { } underIt
696+
? new Entity.Providedf(underIt, condition)
697+
: null;
687698
if ((IntegralPatterns.TryStandardIntegrals(expr, x)) is { } answer) return answer;
688699
// The flag has to be handed on. Every one of these recurses, and a solver that
689700
// dropped it re-enabled integration by parts one level below the call that
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
/// A condition with no x in it holds for every x or for none, so the integral of <c>f provided C</c>
16+
/// is the integral of f, provided C. <c>(x^2 sin(x)) provided not k = 0</c> was declined where
17+
/// <c>x^2 sin(x)</c> is answered, and inner simplification writes such a condition at the top of
18+
/// the questions rules hand on: <c>d^0</c> is <c>1 provided not d = 0</c>.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class AConditionFreeOfTheVariableIntegralTest
23+
{
24+
[Theory]
25+
[InlineData("(x^2*sin(x)) provided not k = 0", "not k = 0")]
26+
[InlineData("(1/((a + b*x)*3*x)) provided not d = 0", "not d = 0")]
27+
[InlineData("(1/x) provided a > 0", "a > 0")]
28+
public void IsAnsweredUnderTheCondition(string integrand, string condition)
29+
{
30+
var integral = integrand.ToEntity().Integrate("x");
31+
var text = integral.Stringize();
32+
Assert.DoesNotContain("integral(", text);
33+
Assert.Contains($"provided {condition}", text);
34+
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("d", 0.9).Substitute("k", 0.4);
35+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
36+
var original = Pinned(integrand.ToEntity());
37+
foreach (var at in new[] { 0.3, 0.8, 1.4, 2.1 })
38+
{
39+
var want = original.Substitute("x", at).EvalNumerical();
40+
var got = derivative.Substitute("x", at).EvalNumerical();
41+
Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}");
42+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
43+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
44+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
45+
}
46+
}
47+
48+
/// <summary>Where the condition fails the integrand has no value, and neither has the answer.</summary>
49+
[Fact]
50+
public void HasNoValueWhereTheConditionFails()
51+
{
52+
var integral = "(1/x) provided a > 0".ToEntity().Integrate("x").Substitute("C", 0);
53+
Assert.True(integral.Substitute("a", -1).Substitute("x", 2).EvalNumerical().IsNaN);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)