Hi. The last 7 months I have been working on the greatest release of AngouriMath. There is something I want to tell you.
Briefly about the project
In November of 2019 I realized that it would be nice to have a symbolic algebra library for .NET, which could simplify expressions, solve equations, build latex code, and many more. That is how I decided to create one.
But this already exists...
As long as I tell people about what I am working on, they suggest different solutions, be those to rewrite SymPy, make a wrapper over SageMath for .NET, pirate Wolfram|Alpha, use primitive mathnet.symbolics (about which they mention themselves).
All of those have limitations or difficulties. In contrast, what I am working on is a very lightweight library, made and optimized for .NET specifically. It is open-source (under MIT).
Release 1.2
In August one of the main contributors, @HappyPig375, helped to rewrite a significant part of the library into a type hierarchy. Every operator/function has its own node (type). Thanks to it, the library now has a more obvious API, improved performance and security. Now, let us go over what has been done within these 7 months.
An expression is a record
For example, that is what Sumf
looks like
public sealed partial record Sumf(Entity Augend, Entity Addend) : NumericNode
Thanks to it, now we can apply pattern matching:
internal static Entity CommonRules(Entity x) => x switch
{
// (a * f(x)) * g(x) = a * (f(x) * g(x))
Mulf(Mulf(Number const1, Function func1), Function func2) => func1 * func2 * const1,
// (a/b) * (c/d) = (a*c)/(b*d)
Mulf(Divf(var any1, var any2), Divf(var any3, var any4)) => any1 * any3 / (any2 * any4),
// a / (b / c) = a * c / b
Divf(var any1, Divf(var any2, var any3)) => any1 * any3 / any2,
(it is a few examples of patterns used in the simplification algorithm)
Math
Here I am going to over over features related to math itself.
New functions
Secant
, cosecant
, arcsecant
, arccosecant
were added as separate nodes.
12 hyperbolic functions were added. They do not have their own nodes and return their symbolic expression (sinh(x)
as(e.Pow(x) - e.Pow(-x)) / 2
).
Abs
and Signum
. The syntax of Abs
is the following: (|x|)
. It looks similar to how we write in on a paper while at the same time allows to avoid ambiguouty (because | is symmetric, there might be problems with it).
Euler's totient function was added.
WriteLine(@"phi(8)".EvalNumerical());
WriteLine(@"(|-3 + 4i|)".EvalNumerical());
WriteLine(@"sinh(3)".Simplify());
WriteLine(@"sec(0.5)".Simplify());
Prints
4
5
(e ^ 3 - 1 / e ^ 3) / 2
sec(1/2)
Domains
They allow to limit every node's range. If a node is out of its domain, it turns into a NaN
, which makes the entire expression undefined. SpecialSet
s are used as values of domains.
Booleans and logic
Logic operators were finally added. At the beginning, there was an idea to make those similar to what we use in programming; instead, it was decided to make them literal. That is why the syntax is: not
, or
, xor
, and
, implies
.
To check whether an expression is evaluable into a Boolean
, you need to use EvaluableBoolean
. Same way, we use EvaluableNumerical
to check whether an expression is evaluable into a Number
.
WriteLine(@"(true or b) implies c".Simplify());
(printsc
)
Equality and inequality signs
They might carry the Boolean
type, if their operands are computed. Of course, the following were added: =
, <
, >
, <=
, >=
.
You can also combine inequalities. For example, a > b > c
is interpreted asa > b and b > c
.
WriteLine(@"a < b >= c".Simplify());
(Printsa < b and b >= c
)
Sets
Sets were added. They are parsable and have their own nodes.
The most naive type of set is FiniteSet
. Its syntax: { 1, 2, 3 }
.
Intervals
also have familiar syntax: [1; 2]
for both points included, (1; 2)
for both excluded, [1; 2)
for the right one excluded, and (1; 2]
for the left one excluded. Complex intervals were removed because of their clumsiness.
SpecialSet
are preset sets. Now we have CC
, RR
, QQ
, ZZ
, BB
for complex, real, rational, integer and boolean values.
ConditionalSet
is written in set-builder notation, for example: { x : x > 0 and x^2 = y }
(any x
such that greater than 0 and equalsy
).
WriteLine(@"({ 1, 2 } \/ { 5 }) /\ { x : x in [2; 3] and x > 0 } ".Simplify());
(Prints{ 2 }
)
Limits improved
First remarkable, second remarkable limits transformation rules were added, as well as the l'Hopital's rule.
WriteLine("tan(a x) / (b x)".Limit("x", 0));
WriteLine("(sin(t) - t) / t3".Limit("t", 0));
(Printsa / b
and -1/6
respectively)
"Provided" node
Allows to set constraints on an expression. For example, square root on real numbers we can define as sqrt(x) provided x >= 0
. It will turn into NaN
if you substitute negative x
.
If an expression being turned into NaN
is an element of a finite set, it will be excluded, instead of turning the whole expression into NaN
. It is the only exception for NaN
.
Piecewise
Piecewise
is a sequence ofProvided
s. Unlike the classical piecewise-defined function, here the order of elements matters, and when computingPiecewise
, the firstProvided
is returned such that its predicate is true.
That is an example of how we can define the absolute value function for real numbers via Piecewise
:
Entity abs = "piecewise(x provided x > 0, -x provided x <= 0)";
WriteLine(abs.Substitute("x", 3).EvalNumerical());
WriteLine(abs.Substitute("x", -3).EvalNumerical());
(Prints 3 in both cases)
Ease and convenience of use
This part of the article about features making the use of AngouriMath more comfortable.
Exceptions were reconsidered and refactored
Now all exceptions thrown by the library are somehow inherited from AngouriMathBaseException
. Because there is nop/invoke
or IO, you can be assured that any exception assignable to AngouriMathBaseException
does not bring any direct harm to the system or user's data. In other words, when catching exceptions, now you can catch this exception in the last instance of Catch
.
Performance improved
To be precise, the performance has been fluctuating throughout this all time. Nonetheless, it is still by far better than that of 1.1.0.5. Here you can find a performance report by every key commit.
F# is now supported
With this release, the API for AngouriMath is now supported natively for wonderful language F#. It might be not as functional as the main API, so in especially complicated cases, you still can call methods from the core library itself.
Interactive
I wrote an article some time ago about AngouriMath in Jupyter. AngouriMath.Interactive itself simplify converts ILatexiseable
into a LaTeX-code and renders it with MathJax (read more about it in the mentioned article).
Multithreading
All computations happen strictly in one thread. The library is not responsible for your threading problems. Moreover, all methods are thread-safe. The settings are local for every thread([ThreadStatic]
).
The main feature of this update is that you can interrupt computations of Solve
orSimplify
.
New settings
It is their third version. Even though I am not a big fan of the new solution, it is the best I could achieve. To set a new value of a setting, let us write
using var _ = MaxExpansionTermCount.Set(10);
// some code
Then this setting will be automatically rolled back to the previous value once the end of the scope is reached (the return type of Set
isIDisposable
).
That is about it
Thank you for your attention. I will be pleased to answer your questions.
Anybody willing to contribute is welcome (contact me or fork and make your changes directly).