Jace.NET – Just another calculation engine for .NET
I work as a .NET technical architect in the financial industry and I get in touch with all sorts of mathematical formulas representing insurance or banking products. Most of the time, these formulas are hard coded by programmers and are thus not very flexible to be change dynamically at runtime by end-users.
This inspired me to start developing a calculation engine for the .NET platform that can execute any mathematical formula stored as a string both with or without variables. I wiped the dust of my university book: “Modern compiler implementations” and started the development a couple of weeks ago. I decided to name the calculation engine Jace.NET: Just another calculation engine for .NET.
Jace.NET is designed to run on .NET 4.0, Windows Phone 7, Windows Phone 8 and Windows RT.
How to use it
Jace.NET can be used in a couple of ways:
By directly executing a given mathematical function using the provided variables:
Dictionary<string, double> variables = new Dictionary<string, double>(); variables.Add("var1", 2.5); variables.Add("var2", 3.4); CalculationEngine engine = new CalculationEngine(); double result = engine.Calculate("var1*var2", variables);
By building a .NET Func accepting a dictionary as input containing the values for each variable:
CalculationEngine engine = new CalculationEngine(); Func<Dictionary<string, double>, double> function = engine.Build("var1+2/(3*otherVariable)"); Dictionary<string, double> variables = new Dictionary<string, double>(); variables.Add("var1", 2); variables.Add("otherVariable", 4.2); double result = function(variables);
By building a typed .NET Func:
CalculationEngine engine = new CalculationEngine(); Func<int, double, double> function = (Func<int, double, double>)engine.Function("var1+2/(3*otherVariable)") .Parameter("var1", DataType.Integer) .Parameter("otherVariable", DataType.FloatingPoint) .Result(DataType.FloatingPoint) .Build(); double result = function(2, 4.2);
Architecture
Jace.NET has an architecture similar to the one of modern compilers: interpretation and execution are performed in a number of steps. Each step focuses on one aspect of the parsing and interpretation of the formula. This keeps the overall complexity manageable.
The process starts with the tokenizing phase. During this phase the input string is converted into the various allowed tokens: integers, doubles, operations and variables. If a part of the input string contains text that does not match with any type of token, an exception is thrown and Jace will halt.
When tokenizing is successfully finished, an abstract syntax tree (AST) is constructed. This abstract syntax tree is a tree like data model that unambiguously represents the mathematical formula in memory. All mathematical precedence rules are taking into account when constructing the abstract syntax tree. Jace uses an algorithm inspired by the shunting-yard algorithm of Dijkstra to create this AST.
After AST creation, the optimizer will try to simplify the abstract syntax tree: if a part of the formula does not depend on variables but solely on constants. This part of the tree is already calculated and replaced by a constant in the tree.
The final phase is the OpCode generation. During this phase, a .NET dynamic method is created and the necessary MSIL is generated to execute the formula. This dynamic method is cached in memory. If the same formula is executed again in the future with other values for the variables. The interpretation steps are skipped and the dynamic method is directly executed. If the formulas of the calculations are frequently reoccurring, Jace.NET has near compiled code performance.
GitHub
If you are interested in the source code, please have a look at:
Wow! “Theory of the compilers” was one of my favorite courses at University, but I never had the opportunity to go back to my “books” as I seldom have to develop things requiring such an “advanced” topic….
I really like the idea of Dynamic Method Generation! As far as someone else would have been tempted to “interpret formula”, I am quite sure he would simply have reused the Calculation Engine of Aspose.Cells (in use by some projects at AG but for other purposes). This Engine supports almost all the Microsoft Excel’s built-in functions and formulas. Out-of-the-box, code would looks like this:
worksheet.Cells[“A1”].PutValue(2.5);
worksheet.Cells[“A2”].PutValue(3.5);
worksheet.Cells[“A3”].PutValue(5);
worksheet.Cells[“A4”].Formula = “=(A1+A3)*A2”;
workbook.CalculateFormula();
For sure, that could be encapsulated behind methods like “SetVariable(name, value)” and “Compute()” to improve the semantic, but you clearly offer a Roll Royce solution compare this “quick and cheap” approach.
Thanks for the reply 🙂
The idea was not to rely on some product like Aspose Cells, but to have a framework that only requires .NET. I also wanted code that could be ported to Windows Phone and Windows RT. I am not sure if Aspose Cells runs on these platforms?
+ it was/is a fun challenge to build such an “advanced” system 😉
Hi
I’m currently working on a simple (free) Windows Phone 8 app for evaluating simple expressions like Sin(3)*(2+(2%1)/3)+Sqrt(7) (no hidden meaning here 😉
Instead of building a good parser on my own (this is not my level of expertise to be honest) I searched for a free framework. First hit was NCalc, which is, as you mentioned, not working on WP8 though. Then I found your project – nice work! However, are you planning to provide some build-in functions, e.g. Sqrt, Pow, Sin, Cos, Tan, ASin, ACos, ATan and Log, in the near future? Otherwise I’ll try to figure something out myself 🙂
btw: Merry Christmas!
sin, cos and log are already supported. I will add support for the other functions in the next version. I am also working on improving the documentation for people to know what is supported in jace.
glad to hear that, thanks!
I have started documenting the functions of Jace:
https://github.com/pieterderycke/Jace/wiki/Standard-Functions
Jace.NET 0.7.1 has been released: support has been added for the modulo operator and the following functions: asin, acos, tag, cot, atan, acot and sqrt.
You can get it with NuGet.
Happy coding in 2013 😉
Thx, Great! Can’t wait to try it 🙂
Nice job indeed. Question: do you have a plan to implement custom functions and evaluation of parameters? For example: MyFunc1(P1,MyFunc2(P2)). Thanks.
Yes, this is planned for a next version (probably 0.8). Do you have specific suggestions or ideas you would like to have implemented in Jace.NET?
Thanks for asking. I am working on an expert system which needs to check lot of rules weighted by score points. Rules need to be loaded from external file, to easily change score and formula. I need to write lot of custom functions, and first version is implemented by using NCalc. NCalc is good but, performances are bad. I tested your tool and can confirm that Jace is faster. But requirement for custom functions and specially parameters evaluation, prevent me to switch project to work with Jace.
v0.8 beta (can be found on the dev branch on GitHub) has now support for custom functions. You can add new functions to a CalculationEngine instance by calling the function AddFunction. AddFunction supports two parameters: a string for the function name and a .NET Func containing the implementation). There are various overloads of AddFunction for supporting Func’s with multiple parameters.
Is this sufficient? Or do you have other proposals or remarks?
JACE fails on -(1+2+(3+4)) evaluating to 10 instead of -10
I believe no handling of unary minus is provided.
Hello Jay,
Thanks for contacting me. Your issue is a bug in Jace, I will fix it in the next release. Until then you could write -(1+2+(3+4)) (but I am aware that it is less convenient.
The bug has been corrected in the dev branch on GitHub. Jace.NET now correctly handles the unary minus. The fix will be released as part of Jace.NET 0.8.1.
nice 🙂
Do you already have any idea when 0.8.1 will be available?
Probably somewhere during next week. But there are some other small bugs I want to fix first.
Just to let you know that there is delay with the release of 0.8.1 due to a bug. I will postpone the release with a week.
I am happy to announce that version 0.8.1 has been released with support for the unary minus operator. You can get the latest version using NuGet.
I left this question on a separate thread, but it seems more appropriate here… is there a reason that Jace accepts only doubles as variables? I’m working on integrating Jace into a financial appliciation, but my inputs are all decimals. Thanks.
I used a double because it takes less space in memory and it is faster for calculations. Would a double be acceptable for your application? Or do you really need support for decimals?
Kind regards,
Pieter
The calculations I’m performing with Jace.net involve money which is represented by decimals in our application. I worry about losing precision when casting to double to use the Jace calculation engine. Support for decimals would be great, especially for anyone who wants to use Jace for calculations involving currency. Pretty impressive tool regardless.
Thanks!
I believe I have found a bug in the latest version of Jace, but please correct me if I am wrong.
I’m currently validating Jace for use in a larger software project and have run across an issue. Evaluating the expression 1+2-3*4/5+6-7*8/9+0 gives 0.82 with Jace instead of 0.378 which it should be according to the rules of precedence. I have traced the issue to building the AST, where the root of the tree becomes the first subtraction instead of the last addition.
To make a long story short, here is some code for the AstBuilder that replaces the else case in the “if (operatorStack.Count == 0)” statement. It ensures that all operators with the same or less precedence (etc) are popped instead of just the last one.
Token operation2Token = operatorStack.Peek();
bool isFunctionOnTopOfStack = operation2Token.TokenType == TokenType.Text;
char operation2 = (char)operation2Token.Value;
while (!isFunctionOnTopOfStack && ((IsLeftAssociativeOperation(operation1) && (operationPrecedence[operation1] == operationPrecedence[operation2])) ||
(operationPrecedence[operation1] 0)
{
operation2Token = operatorStack.Peek();
isFunctionOnTopOfStack = operation2Token.TokenType == TokenType.Text;
operation2 = (char)operation2Token.Value;
}
else
{
isFunctionOnTopOfStack = true; // not really, but it exits the loop nicely
}
}
operatorStack.Push(operation1Token);
Thanks for informing me about this issue and the detailed analysis. I will investigate it this weekend and keep you informed.
Hello Mikael,
I have now investigated the issue in detail and you are absolutely right regarding the bug and the proposed solution! (thanks for this :-)) I have also verified with the specs of the shunting yard algorithm and I made an implementation issue by forgetting the while loop.
I will fix it and release a version 0.8.2 of Jace. I will also update the unit tests of Jace.NET for covering this case.
Does Jace.NET further satisfies the needs of your project?
Kind regards,
Pieter
Does it use .NET DLR?
No, Jace does not rely on the .NET DLR.
1. Is there a plan to support logic operator like ‘and’ ‘or’ and relation operator like ‘>’ ‘<' '='
2. Does Jace support decimal?
Desperately need this functionality. any plan for implementation “and”,”or”.
I will look to introduce support for “and” and “or’
Hi, I’m wanting to automatically generate the list of parameters from a formula, then go find the values. Can this engine do it? eg:
CalculationEngine engine = new CalculationEngine();
Func<Dictionary, double> function = engine.Build(“var1+2/(3*otherVariable)”);
// automatically generate variables here
// Pseudo:
for each var in engine.variables
var.VariableValue = FindVariableValue(var.VariableName)
next
double result = function(engine.variables);
The reason I want to do this is because I have a lot of formulas, and each references only a few variables. So they need to find the variables from a large database of variables, via lookup, depending on the actual formula. I’d prefer not to have to write an engine to ‘pull out’ the variable names.
If you’re able to give me a idea of if this is possible, I’d greatly appreciate it.
THanks
You could do this if you would use the inner components of Jace directly. If you would use the TokenReader (https://github.com/pieterderycke/Jace/blob/master/Jace/Tokenizer/TokenReader.cs) and the AstBuilder (https://github.com/pieterderycke/Jace/blob/master/Jace/AstBuilder.cs) you will be able to easily extract the variables. Hopes that this answers your question.
Is it possible to express a variable as percentage directly in the formula text e.g. var1*10%, or due to the interpreter must be reduced to var1*(10/100) ?
Jace seems to support min() and max() functions. These are not documented in wiki. Is there a way to gather a complete list of built-in functions?
variable name with underscore is not working like => “NT_CUM”