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
48 changes: 48 additions & 0 deletions AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using AngouriMath.Functions.Algebra.NumericalSolving;
using AngouriMath.Functions.Boolean;
using AngouriMath.Core.Exceptions;
using System.Diagnostics.CodeAnalysis;

namespace AngouriMath.Core
{
Expand Down Expand Up @@ -862,6 +863,9 @@ public static class Boolean

public static class Utils
{
/// <summary>
/// Performs the expansion operation over the given variable
/// </summary>
public static Entity SmartExpandOver(Entity expr, Variable x)
{
var linChildren = Sumf.LinearChildren(expr);
Expand All @@ -878,6 +882,50 @@ public static Entity SmartExpandOver(Entity expr, Variable x)

return TreeAnalyzer.MultiHangBinary(nodes, (a, b) => a + b);
}

/// <summary>
/// Extracts a polynomial with integer powers
/// </summary>
/// <param name="expr">From which to extract the polynomial</param>
/// <param name="variable">Over which variable to extract the polynomial</param>
/// <param name="dst">
/// Where to put the dictionary, whose keys
/// are powers, and values - coefficients
/// </param>
/// <returns>Whether the input expression is a valid polynomial</returns>
public static bool TryGetPolynomial(Entity expr, Variable variable,
[NotNullWhen(true)] out Dictionary<EInteger, Entity>? dst)
=> TreeAnalyzer.TryGetPolynomial(expr, variable, out dst);

/// <summary>
/// Extracts the linear coefficient and the bias over a variable
/// a x + b
/// </summary>
/// <param name="expr">From which to extract the linear function</param>
/// <param name="variable">Over which to extract</param>
/// <param name="a">The linear coefficient</param>
/// <param name="b">The bias</param>
/// <returns>Whether the extract was successful</returns>
public static bool TryGetPolyLinear(Entity expr, Variable variable,
[NotNullWhen(true)] out Entity? a,
[NotNullWhen(true)] out Entity? b)
=> TreeAnalyzer.TryGetPolyLinear(expr, variable, out a, out b);

/// <summary>
/// Extracts the quadratic coefficient, linear coefficient and the bias over a variable
/// a x ^ 2 + b x + c
/// </summary>
/// <param name="expr">From which to extract the quadratic function</param>
/// <param name="variable">Over which to extract</param>
/// <param name="a">The quadratic coefficient</param>
/// <param name="b">The linear coefficient</param>
/// <param name="c">The bias</param>
/// <returns>Whether the extract was successful</returns>
public static bool TryGetPolyQuadratic(Entity expr, Variable variable,
[NotNullWhen(true)] out Entity? a,
[NotNullWhen(true)] out Entity? b,
[NotNullWhen(true)] out Entity? c)
=> TreeAnalyzer.TryGetPolyQuadratic(expr, variable, out a, out b, out c);
}
}
}
2 changes: 1 addition & 1 deletion AngouriMath/Functions/Continuous/Algebra/Integration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public Complex DefiniteIntegral(Variable x, (EDecimal Re, EDecimal Im) from, (ED
}
namespace AngouriMath.Functions.Algebra
{
internal static class Integration
public static partial class Integration

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to keep this internal. Should be consistent with Limit, so that you expose it only in MathS.Compute

{
/// <summary>Numerical definite integration, see more in <see cref="Entity.DefiniteIntegral(Entity.Variable, EDecimal, EDecimal)"/></summary>
internal static Complex Integrate(Entity func, Entity.Variable x, (EDecimal Re, EDecimal Im) from, (EDecimal Re, EDecimal Im) to, int stepCount)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Linq;

namespace AngouriMath.Functions.Algebra
{
static class IndefiniteIntegralSolver
{
internal static Entity? SolveBySplittingSum(Entity expr, Entity.Variable x)
{
var splitted = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.ContainsNode(x));
if (splitted is null || splitted.Count < 2) return null; // nothing to do, let other solvers do the work
splitted[0] = Integration.ComputeIndefiniteIntegral(splitted[0], x); // base case for aggregate
var result = splitted.Aggregate((e1, e2) => e1 + Integration.ComputeIndefiniteIntegral(e2, x));
return result;
}

internal static Entity? SolveAsPolynomialTerm(Entity expr, Entity.Variable x) => expr switch

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be simpler to use existing polynomial parser?

{
Entity.Mulf(var m1, var m2) =>
!m1.ContainsNode(x) ?
m1 * Integration.ComputeIndefiniteIntegral(m2, x) :
!m2.ContainsNode(x) ?
m2 * Integration.ComputeIndefiniteIntegral(m1, x) :
null,

Entity.Divf(var div, var over) =>
!div.ContainsNode(x) ?
over is Entity.Powf(var @base, var power) ?
div * Integration.ComputeIndefiniteIntegral(MathS.Pow(@base, -power), x) :
div * Integration.ComputeIndefiniteIntegral(MathS.Pow(over, -1), x) :
!over.ContainsNode(x) ?
Integration.ComputeIndefiniteIntegral(div, x) / over :
null,

Entity.Powf(var @base, var power) =>
!power.ContainsNode(x) && @base == x ?
power == -1 ?
MathS.Ln(@base) : // TODO: here should be ln(abs(x)) but for now I left it as is
MathS.Pow(x, power + 1) / (power + 1) :
null,

Entity.Variable(var v) =>
v == x ? MathS.Pow(x, 2) / 2 : v * x,

_ => null
};

internal static Entity? SolveIntegratingByParts(Entity expr, Entity.Variable x)
{
static Entity? IntegrateByParts(Entity v, Entity u, Entity.Variable x, int currentRecursion = 0)
{
if (v == 0) return 0;
if (currentRecursion == MathS.Settings.MaxExpansionTermCount) return null;

var integral = Integration.ComputeIndefiniteIntegral(u, x);
var differential = v.Derive(x).InnerSimplify();
var result = IntegrateByParts(differential, integral, x, currentRecursion + 1);
return (result is null) ? null : v * integral - result;
}

if (expr is Entity.Mulf(var f, var g))
{
if (MathS.TryPolynomial(f, x, out var fPoly))
{
return IntegrateByParts(fPoly, g, x);
}
if (MathS.TryPolynomial(g, x, out var gPoly))
{
return IntegrateByParts(gPoly, f, x);
}
else return null;
}
else return null;
}

internal static Entity? SolveLogarithmic(Entity expr, Entity.Variable x) => expr switch
{
Entity.Logf(var @base, var arg) =>
@base.ContainsNode(x) ?
Integration.ComputeIndefiniteIntegral(MathS.Ln(arg) / MathS.Ln(@base), x) :
arg is Entity.Powf(var y, var pow) ? // log(b, y^p) = ln(y^p) / ln(b) = ln(p) / ln(b) * ln(y)
Integration.ComputeIndefiniteIntegral(pow / MathS.Ln(@base) * MathS.Ln(y), x) :
null,

_ => null
};

internal static Entity? SolveExponential(Entity expr, Entity.Variable x) => expr switch
{
Entity.Powf(var @base, var pow) =>
@base.ContainsNode(x) ?
Integration.ComputeIndefiniteIntegral(MathS.Pow(MathS.e, MathS.Ln(@base) * pow), x) :
null,

_ => null
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace AngouriMath.Functions.Algebra
{
internal static class IntegralPatterns
{
internal static Entity? TryStandardIntegrals(Entity expr, Entity.Variable x) => expr switch
{
Entity.Sinf(var arg) =>
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
-MathS.Cos(arg) / a :
null,

Entity.Cosf(var arg) =>
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
MathS.Sin(arg) / a :
null,

Entity.Tanf(var arg) =>
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
-MathS.Ln(MathS.Cos(arg)) / a :
null,

Entity.Cotanf(var arg) =>
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
MathS.Ln(MathS.Sin(arg)) / a :
null,

Entity.Logf(var @base, var arg) =>
!@base.ContainsNode(x) && TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) ?
((b / a + x) * MathS.Ln(arg) - x) / MathS.Ln(@base) :
null,

Entity.Powf(var @base, var power) =>
!@base.ContainsNode(x) && TreeAnalyzer.TryGetPolyLinear(power, x, out var a, out _) ?
MathS.Pow(@base, power) / (a * MathS.Ln(@base)) :
null,

_ => null
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace AngouriMath.Functions.Algebra
{
public static partial class Integration
{
public static Entity ComputeIndefiniteIntegral(Entity expr, Entity.Variable x)
{
if (!expr.ContainsNode(x)) return expr * x; // base case, handle here

Entity? answer = null;

answer = IntegralPatterns.TryStandardIntegrals(expr, x);
if (answer is { }) return answer;

answer = IndefiniteIntegralSolver.SolveBySplittingSum(expr, x);
if (answer is { }) return answer;

answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x);
if (answer is { }) return answer;

answer = IndefiniteIntegralSolver.SolveIntegratingByParts(expr, x);
if (answer is { }) return answer;

answer = IndefiniteIntegralSolver.SolveLogarithmic(expr, x);
if (answer is { }) return answer;

return new Entity.Integralf(expr, x, 1); // return as integral if nothing can be done with expression

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with limits, we return null if cannot compute, although it might be reconsidered

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,22 @@ static Entity TryDowncast(Entity equation, Variable x, Entity root)
// // //
}

// if no replacement worked, try trigonometry solver
// if no replacement worked, try trigonometric solver
if (TrigonometricSolver.TrySolveLinear(expr, x, out var trig) && trig is FiniteSet elsTrig)
return (Set)elsTrig.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //

// if no trigonometric rules helped, common denominator might help
// if no trigonometric rules helped, try exponential-multiplicative solver
if (ExponentialSolver.SolveMultiplicative(expr, x) is { } expMul && expMul is FiniteSet elsExpMul)
return (Set)elsExpMul.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //

// if no exponential-multiplicative rules helped, try exponential-linear solver
if (ExponentialSolver.SolveLinear(expr, x) is { } expLin && expLin is FiniteSet elsExpLin)
return (Set)elsExpLin.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //

// if no exponential-linear rules helped, common denominator might help
if (CommonDenominatorSolver.TrySolveGCD(expr, x, out var commonDenom) && commonDenom is FiniteSet elsCd)
return (Set)elsCd.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using AngouriMath.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using static AngouriMath.Entity.Set;

namespace AngouriMath.Functions.Algebra.AnalyticalSolving
{
class ExponentialSolver
{
internal static Entity.Set? SolveLinear(Entity expr, Entity.Variable x)
{
var replacement = Entity.Variable.CreateTemp(expr.Vars);

Func<Entity, Entity> preparator = e => e switch
{
Entity.Powf(var @base, var arg) when
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) =>
MathS.Pow(MathS.e, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a),

_ => e,
};

Func<Entity, Entity> replacer = e => e switch
{
Entity.Powf(var @base, var arg)
when @base == MathS.e && arg == x
=> replacement,

_ => e,
};

expr = expr.Replace(preparator);
expr = expr.Replace(replacer);

if (expr.ContainsNode(x)) return null; // cannot be solved, not a pure exponential

expr = expr.InnerSimplify();
if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els && els.Any())
return (Entity.Set)els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSet()).Unite().InnerSimplified;
else
return null;
}

internal static Entity.Set? SolveMultiplicative(Entity expr, Entity.Variable x)
{
Entity? substitution = null;
Entity ApplyPowerTransform(Entity @base, Entity arg)
{
var mults = Entity.Mulf.LinearChildren(arg);
if (mults.Count() == 0) return MathS.Pow(@base, arg);

Entity innerPower = 1;
Entity outerPower = 1;
foreach(var mult in mults)
{
if (mult.InnerSimplify() is Entity.Number)
outerPower = outerPower * mult;
else
innerPower = innerPower * mult;
}
substitution = MathS.Pow(@base, innerPower);
return MathS.Pow(substitution, outerPower.InnerSimplified);
}

Func<Entity, Entity> powerTransform = e => e switch
{
Entity.Powf(var @base, var arg)
when @base == x && !arg.ContainsNode(x) =>
ApplyPowerTransform(@base, arg),

_ => e,
};

expr = expr.Replace(powerTransform);
if (substitution is null) return null;

var replacement = Entity.Variable.CreateTemp(expr.Vars);
expr = expr.Substitute(substitution, replacement);

if (expr.ContainsNode(x)) return null; // cannot be solved, not a multiplicative exponenial equation

expr = expr.InnerSimplify();
if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els && els.Any())
return (Entity.Set)els.Select(sol => substitution.Invert(sol, x).ToSet()).Unite().InnerSimplified;
else
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ internal static class AnalyticalInequalitySolver
/// </summary>
internal static Set Solve(Entity expr, Variable x)
{
if (expr is Minusf(var v, var c) && v == x && !c.ContainsNode(x))
return new Interval(c, false, Number.Real.PositiveInfinity, false);
if (expr is Minusf(var c1, var v1) && v1 == x && !c1.ContainsNode(x))
return new Interval(Number.Real.NegativeInfinity, false, c1, false);
// TODO: poly parser
throw FutureReleaseException.Raised("Inequalities are not implemented yet", "1.2.1");
}
}
Expand Down
Loading