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
95 changes: 42 additions & 53 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -868,28 +868,28 @@ public static Integer GreatestCommonDivisor(Integer a, Integer b)

/// <summary><a href="https://en.wikipedia.org/wiki/Gamma_function"/></summary>
/// <param name="a">Argument node of which gamma function will be taken</param>
/// <returns>Factorial node with one added to the argument</returns>
/// <returns>Factorial node with one subtracted from the argument</returns>
/// <example>
/// <code>
/// using System;
/// using static AngouriMath.MathS;
/// var expr = Factorial(5);
/// Console.WriteLine(expr);
/// Console.WriteLine(expr.Evaled);
/// var expr2 = Gamma(4);
/// var expr2 = Gamma(6);
/// Console.WriteLine(expr2);
/// Console.WriteLine(expr2.Evaled);
/// </code>
/// Prints
/// <code>
/// 5!
/// 120
/// (4 + 1)!
/// (6 - 1)!
/// 120
/// </code>
/// </example>
[MethodImpl(MethodImplOptions.AggressiveInlining), NativeExport]
public static Entity Gamma(Entity a) => new Factorialf(a + 1);
public static Entity Gamma(Entity a) => new Factorialf(a - 1);

/// <summary>https://en.wikipedia.org/wiki/Sign_function</summary>
/// <param name="a">Argument node of which Signum function will be taken</param>
Expand Down Expand Up @@ -1533,8 +1533,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand Down Expand Up @@ -1585,8 +1585,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand Down Expand Up @@ -1637,8 +1637,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand Down Expand Up @@ -1689,8 +1689,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand All @@ -1700,7 +1700,7 @@ public static class Hyperbolic
/// </code>
/// </example>
[MethodImpl(MethodImplOptions.AggressiveInlining), NativeExport]
public static Entity Arcotanh(Entity x) => 0.5 * Ln((x - 1) / (x + 1));
public static Entity Arcotanh(Entity x) => 0.5 * Ln((x + 1) / (x - 1));

/// <summary>
/// Inverse hyperbolic secant:
Expand Down Expand Up @@ -1741,8 +1741,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand Down Expand Up @@ -1793,8 +1793,8 @@ public static class Hyperbolic
/// 1/2 * ln((1 + x) / (1 - x))
/// 10
/// ----------------------
/// 1/2 * ln((x - 1) / (x + 1))
/// -10
/// 1/2 * ln((x + 1) / (x - 1))
/// 10
/// ----------------------
/// ln(1 / x + sqrt(1 / x ^ 2 - 1))
/// 10
Expand Down Expand Up @@ -5564,42 +5564,31 @@ public sealed record NewtonSetting
complexityCriteria ??= new Func<Entity, double>(expr =>
{
// Those are of the 2nd power to avoid problems with floating numbers
static double TinyWeight(double w) => w * 0.5;
static double MinorWeight(double w) => w * 1.0;
static double Weight(double w) => w * 2.0;
static double MajorWeight(double w) => w * 4.0;
static double HeavyWeight(double w) => w * 8.0;
static double ExtraHeavyWeight(double w) => w * 12.0;

// Number of nodes
var res = Weight(expr.Complexity);

// Number of variables
res += Weight(expr.Nodes.Count(entity => entity is Variable));

// Number of divides
res += MinorWeight(expr.Nodes.Count(entity => entity is Divf));

// Number of rationals with unit numerator
res += Weight(expr.Nodes.Count(entity => entity is Rational rat and not Integer
&& (rat.Numerator == 1 || rat.Numerator == -1)));

// Number of negative powers
res += HeavyWeight(expr.Nodes.Count(entity => entity is Powf(_, Real { IsNegative: true })));

// Number of logarithms
res += TinyWeight(expr.Nodes.Count(entity => entity is Logf));

// Number of phi functions
res += ExtraHeavyWeight(expr.Nodes.Count(entity => entity is Phif));

// Number of negative reals
res += MajorWeight(expr.Nodes.Count(entity => entity is Real { IsNegative: true }));

// 0 < x is bad. x > 0 is good.
res += Weight(expr.Nodes.Count(entity => entity is ComparisonSign && entity.DirectChildren[0] == 0));

return res;
const double TinyWeight = 0.5;
const double MinorWeight = 1.0;
const double Weight = 2.0;
const double MajorWeight = 4.0;
const double HeavyWeight = 8.0;
const double ExtraHeavyWeight = 12.0;

static double DefaultCriteria(Entity expr) => expr switch {
// Weigh provided predicates much less but nested provideds heavy
Providedf(var inner, var predicate) =>
DefaultCriteria(inner) + 0.1 * DefaultCriteria(predicate) + ExtraHeavyWeight * (inner.Nodes.Count(n => n is Providedf) + predicate.Nodes.Count(n => n is Providedf)),
Entity.Piecewise { Cases: var cases } =>
cases.Sum(@case =>
DefaultCriteria(@case.Expression) + 0.1 * DefaultCriteria(@case.Predicate) + ExtraHeavyWeight * (@case.Expression.Nodes.Count(n => n is Providedf) + @case.Predicate.Nodes.Count(n => n is Providedf))),
Variable => Weight, // Number of variables
Divf => MinorWeight + expr.DirectChildren.Sum(DefaultCriteria), // Number of divides
Rational(Integer(1 or -1), _) and not Integer => Weight + expr.DirectChildren.Sum(DefaultCriteria), // Number of rationals with unit numerator
Powf(_, Real { IsNegative: true }) => HeavyWeight + expr.DirectChildren.Sum(DefaultCriteria), // Number of negative powers
Logf => TinyWeight + expr.DirectChildren.Sum(DefaultCriteria), // Number of logarithms
Phif => ExtraHeavyWeight + expr.DirectChildren.Sum(DefaultCriteria), // Number of phi functions
Real { IsNegative: true } => MajorWeight + expr.DirectChildren.Sum(DefaultCriteria), // Number of negative reals
ComparisonSign when expr.DirectChildren[0] == 0 => Weight + expr.DirectChildren.Sum(DefaultCriteria), // 0 < x is bad. x > 0 is good.
_ => expr.DirectChildren.Sum(DefaultCriteria)
} + Weight; // Number of nodes
return DefaultCriteria(expr);
});
[ThreadStatic] private static Setting<Func<Entity, double>>? complexityCriteria;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ public sealed partial record Impliesf(Entity Assumption, Entity Conclusion) : St

public Entity NodeSecondChild => Conclusion;

private Impliesf New(Entity left, Entity right) =>
ReferenceEquals(Assumption, left) && ReferenceEquals(Conclusion, right) ? this : new(left, right);
private Impliesf New(Entity assumption, Entity conclusion) =>
ReferenceEquals(Assumption, assumption) && ReferenceEquals(Conclusion, conclusion) ? this : new(assumption, conclusion);
/// <inheritdoc/>
public override Entity Replace(Func<Entity, Entity> func)
=> func(New(Assumption.Replace(func), Conclusion.Replace(func)));
Expand Down
7 changes: 4 additions & 3 deletions Sources/AngouriMath/Core/Entity/Omni/Entity.Piecewise.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Core.HashCode;
using AngouriMath.Extensions;
using HonkSharp.Laziness;
using System;

namespace AngouriMath
{
Expand All @@ -28,7 +29,7 @@ internal Providedf New(Entity expression, Entity predicate)

/// <inheritdoc/>
public override Entity Replace(Func<Entity, Entity> func)
=> func(New(func(Expression), func(Predicate)));
=> func(New(Expression.Replace(func), Predicate.Replace(func)));

internal override Priority Priority => Priority.Provided;

Expand Down Expand Up @@ -82,7 +83,7 @@ public Piecewise Apply(Func<Providedf, Providedf> func)

/// <inheritdoc/>
public override Entity Replace(Func<Entity, Entity> func)
=> func(New(Cases.Select(c => c.New(func(c.Expression), func(c.Predicate)))));
=> func(New(Cases.Select(c => c.New(c.Expression.Replace(func), c.Predicate.Replace(func)))));

/// <summary>
/// Checks that two Piecewise are equal
Expand Down
4 changes: 3 additions & 1 deletion Sources/AngouriMath/Functions/Continuous/Differentiation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ partial record Powf
// (const ^ b)' = e^b * b'
/// <inheritdoc/>
protected override Entity InnerDifferentiate(Variable variable) =>
Exponent is Number exp
Exponent == Integer.One
? Base.InnerDifferentiate(variable).WithCondition(Base.DomainCondition) // don't create x^0 which is undefined for x=0
: Exponent is Number exp
? exp * Base.Pow(exp - 1) * Base.InnerDifferentiate(variable)
: Base is Number
? Base.Pow(Exponent) * MathS.Ln(Base) * Exponent.InnerDifferentiate(variable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ internal static class LimitSolvers
[ConstantField] private static readonly Real Infinity = Real.PositiveInfinity;
internal static Entity? SolveBySubstitution(Entity expr, Variable x)
{
// For now we remove all provideds from an expression
// Is it a correct thing to do?
var res = expr.Replace(e => e is Providedf(var expr, _) ? expr : e).Substitute(x, Infinity);
var res = expr.Substitute(x, Infinity);
if (res.Evaled is Complex limit)
{
MultithreadingFunctional.ExitIfCancelled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ internal static partial class LimitFunctional
private static Entity? SimplifyAndComputeLimitToInfinity(Entity expr, Variable x)
{
expr = expr.Simplify();
return ComputeLimitToInfinity(expr, x);
}
if (expr is Providedf(var expression, _)) expr = expression; // limits operate assuming a continuous expression even though some points may be undefined.

private static Entity? ComputeLimitToInfinity(Entity expr, Variable x)
{
var substitutionResult = LimitSolvers.SolveBySubstitution(expr, x);
if (substitutionResult is { }) return substitutionResult;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ private static Entity EquivalenceRules(Entity expr, Variable x, Entity dest)
Sinf or Tanf or Arcsinf or Arctanf => expr.DirectChildren[0],
_ => expr
};

private static Entity EvalAssumingContinuous(Entity expr) =>
expr.Evaled switch
{
Providedf(var inner, _) => inner,
var x => x
};
private static Entity ApplyFirstRemarkable(Entity expr, Variable x, Entity dest)
=> expr switch
{
Divf(var a, var b) div
when a.Limit(x, dest).Evaled == 0 && b.Limit(x, dest).Evaled == 0
when EvalAssumingContinuous(a.Limit(x, dest)) == 0 && EvalAssumingContinuous(b.Limit(x, dest)) == 0
=> div.New(EquivalenceRules(a, x, dest), EquivalenceRules(b, x, dest)),

_ => expr
Expand All @@ -38,7 +43,7 @@ private static Entity ApplySecondRemarkable(Entity expr, Variable x, Entity dest
// e ^ (g(x) * (f(x) - 1))
Powf(var xPlusOne, var xPower) when
xPlusOne.ContainsNode(x) && xPower.ContainsNode(x) &&
(xPlusOne - 1).Limit(x, dest).Evaled == 0 && IsInfiniteNode(xPower.Limit(x, dest)) =>
EvalAssumingContinuous((xPlusOne - 1).Limit(x, dest)) == 0 && IsInfiniteNode(xPower.Limit(x, dest)) =>
MathS.e.Pow(xPower * (xPlusOne - 1)),

_ => expr
Expand All @@ -53,7 +58,7 @@ private static bool IsFiniteNode(Entity expr)
private static Entity ApplylHopitalRule(Entity expr, Variable x, Entity dest)
{
if (expr is Divf(var num, var den))
if (num.Limit(x, dest).Evaled is var numLimit && den.Limit(x, dest).Evaled is var denLimit)
if (EvalAssumingContinuous(num.Limit(x, dest)) is var numLimit && EvalAssumingContinuous(den.Limit(x, dest)) is var denLimit)
if (numLimit == 0 && denLimit == 0 ||
IsInfiniteNode(numLimit) && IsInfiniteNode(denLimit))
if (num is not Number && den is not Number)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ internal static Set Solve(Entity expr, Variable x, bool compensateSolving = fals
if (expr == x)
return new Entity[] { 0 }.ToSet();

if (ProvidedLifter.ExtractProvidedPredicates(ref expr, out var predicate))
return ProvidedLifter.MergePredicateIntoSolveResult(Solve(expr, x, compensateSolving), x, predicate);

// Applies an attempt to downcast roots
static Entity TryDowncast(Entity equation, Variable x, Entity root)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,26 @@ internal static class ExponentialSolver
internal static Set? SolveLinear(Entity expr, Entity.Variable x)
{
var replacement = Variable.CreateTemp(expr.Vars);

Func<Entity, Entity> preparator = e => e switch
static Entity NonZeroPow(Entity @base, Entity exponent) => exponent == Integer.Zero ? Integer.One : MathS.Pow(@base, exponent);
Entity preparator(Entity e) => e switch
{
Powf(var @base, var arg) when
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) =>
MathS.Pow(@base, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a),

// Transformation base^(a*x + b) = base^b * e^(ln(base)*a*x) is safe when:
// - base ≠ 0 (ensures ln(base) is defined and no 0^b issues)
// For complex bases, ln uses the principal branch
@base.Evaled switch
{
Complex { IsZero: false } => NonZeroPow(@base.InnerSimplified, b) * NonZeroPow(NonZeroPow(MathS.e, x), MathS.Ln(@base.InnerSimplified) * a),
// If base is definitely 0, keep original form (will likely fail to solve anyway)
Complex { IsZero: true } => e,
// For symbolic bases that might be zero, add condition
_ => (NonZeroPow(@base, b) * NonZeroPow(NonZeroPow(MathS.e, x), MathS.Ln(@base) * a)).Provided([email protected](0))
},
_ => e,
};

Func<Entity, Entity> replacer = e => e switch
Entity replacer(Entity e) => e switch
{
Powf(var @base, var arg)
when @base == MathS.e && arg == x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ internal static Set Solve(Entity expr, Variable x)

Inf(var var, Set set) when var == x => set,

Providedf(var inner, var predicate) => ProvidedLifter.MergePredicateIntoSolveResult(Solve(inner, x), x, predicate),

// TODO: Although piecewise needed?
_ => Set.Empty
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ internal static class AnalyticalInequalitySolver
/// </summary>
internal static Set Solve(Entity expr, Variable x)
{
if (ProvidedLifter.ExtractProvidedPredicates(ref expr, out var predicate))
return ProvidedLifter.MergePredicateIntoSolveResult(Solve(expr, x), x, predicate);
{
if (MathS.Utils.TryGetPolyLinear(expr, x, out var a, out var b))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ internal static class AnalyticalSetSolver
{
internal static Set Solve(Entity left, Entity right, Variable x)
{
if (ProvidedLifter.ExtractProvidedPredicates(ref left, out var predicate))
return ProvidedLifter.MergePredicateIntoSolveResult(Solve(left, right, x), x, predicate);
if (ProvidedLifter.ExtractProvidedPredicates(ref right, out predicate))
return ProvidedLifter.MergePredicateIntoSolveResult(Solve(left, right, x), x, predicate);
left = left.Replace(Patterns.SetOperatorRules);
right = right.Replace(Patterns.SetOperatorRules);
if (left.DirectChildren.Count<Entity>(c => c == x)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace AngouriMath
partial record Entity
{
/// <summary>
/// Solves a <see cref="Statement"/>
/// Solves a <see cref="Statement"/> or <see cref="Providedf"/>
/// Statement is an Entity such that its value is true for
/// any x in X, where X is the result of this method.
/// See more about <see cref="Set"/>
Expand Down Expand Up @@ -89,7 +89,7 @@ partial record Entity
/// </example>
public Set Solve(Variable var)
{
if (this is Statement)
if (this is Statement or Providedf)
{
var res = StatementSolver.Solve(this, var);
return (Set)res.InnerSimplified;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ partial record Entity
{
public partial record Variable
{
// TODO: When target-typed conditional expression lands, remove the explicit conversion
private protected override Entity IntrinsicCondition => true;
/// <inheritdoc/>
protected override Entity InnerEval() => ConstantList.TryGetValue(Name, out var value) ? (Entity)value : this;
protected override Entity InnerEval() => ConstantList.TryGetValue(Name, out var value) ? value : this;

/// <inheritdoc/>
protected override Entity InnerSimplify() => this;
Expand Down
Loading
Loading