9

Is there any calculation engine for C# that can automatically recalculate dependent fields when a value changes?

Let me freestyle for a second, I'm imagining something like this..

 Field<double> quantity = Field.Create<double>("Quantity");
 Field<double> unitCost = Field.Create<double>("Unit Cost");
 Field<double> total = Field.Create<double>("Total");

 total.Calculation((q,uc) => q * uc, quantity, value);
      // would have signature something like this:
      // void Calculation<TR,T1,T1>(Func<TR,T1,T2>, Field<T1>, Field<T2>)

This would set up fields that auto-propagate dependent values.

 quantity.Value = 5.0;
 unitCost.Value = 1.5;
 Assert.That(total.Value, Is.EqualTo(7.5));

Obviously this is a simple example, the end uses would be much more akin to the calculations of a complex spreadsheet.

Thinking further it would be amazing if the field/cells would support change notification.

skaffman
  • 398,947
  • 96
  • 818
  • 769
chillitom
  • 24,888
  • 17
  • 83
  • 118
  • 3
    Look up "rules engine" under Windows Workflow Foundation. – John Saunders Feb 01 '11 at 16:13
  • I don't know if it already exists, but it looks like a nice think to implement anyway, you 'just' need INotifyPropertyChanging and to decompose the Expression Tree to link depedencies. – Guillaume86 Feb 01 '11 at 17:20

5 Answers5

5

Have you seen http://ncalc.codeplex.com ?

It's extensible, fast (e.g. has its own cache) enables you to provide custom functions and varaibles at run time by handling EvaluateFunction/EvaluateParameter events. Example expressions it can parse:

Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)");

  e.Parameters["Pi2"] = new Expression("Pi * Pi");
  e.Parameters["X"] = 10;

  e.EvaluateParameter += delegate(string name, ParameterArgs args)
    {
      if (name == "Pi")
      args.Result = 3.14;
    };

  Debug.Assert(117.07 == e.Evaluate());

It also handles unicode & many data type natively. It comes with an antler file if you want to change the grammer. There is also a fork which supports MEF to load new functions.

It also supports logical operators, date/time's strings and if statements.

a solution

You could possibly do automatic recalculation by implementing INotifyPropertyChanged then doing something like

  • set a field's expression this.Field.Expression = new Expression("Field1+Field2");

On notifypropertyupdated in the class

  • for each field (with reflection) that is a Function
  • if it's expression refers to the changed field then recalculate the variable.
  • on recalculation you'd need to handle the EvaluateParameter event to use reflection to find the right field and extract its value (you could cache to avoid reflection if needed)
GreyCloud
  • 3,030
  • 5
  • 32
  • 47
  • Interesting project - does it handle dependencies? i.e. Can I build a series of expressions that rely on the results of other expressions? If they're in the wrong order (or if a later changes the value of something used by a former - will it handle the cascading update?) Documentation seems to avoid the topic (perhaps I'm just looking in the wrong place) – Rudu Feb 01 '11 at 16:37
  • @Rudu "does it handle dependencies", it'll handle a DAG of expressions moving from the first expression through expressions specified as parameter's to that expression and then to expressions specified as parameters of those expressions and so on. In this way you never get a circular dependancy & the calculation order should always be correct sicne dependancies are calculated first. – GreyCloud Feb 01 '11 at 16:40
  • @Rudu - last example on http://ncalc.codeplex.com/ shows how nesting can take place. On the subject of cascading updates. No these will not occur, the value you get is the value calculated at that time. You'd have to sort out the cascading updates via something like INotifyPropertyChanged on the class containing the data – GreyCloud Feb 01 '11 at 16:43
  • @GreyCloud a [DAG](http://en.wikipedia.org/wiki/Dag)? A clump of feces stuck to the wool of a sheep? – Rudu Feb 01 '11 at 16:44
  • @Rudu, sorry Directed Acyclic Graph - en.wikipedia.org/wiki/Directed_acyclic_graph :-p – GreyCloud Feb 01 '11 at 16:47
  • 3
    It was too much to hope it dealt with sheep feces too *sigh* – Rudu Feb 01 '11 at 16:49
2

There are calculation engines for scalar parameters, and there are higher-level calculation engines for tables, typically used for applications like financial planning, fee and commission calculations, network and contract computations...

Let me explain this shortly. Consider following formulas for scalars:

1) z = f1(x,y)
2) p = f2(z,n)
3) q = f3(x,p)
...

and so on. Configuring such functions and dependency trees requires a calculation engine with scalar parameters. I would (also) recommend following link for such a calculation engine written in c# as a good starting point: http://www.codeproject.com/Articles/246374/A-Calculation-Engine-for-NET

As mentioned, there are also calculation engines with table functions that take tables as parameters. The main principle is but the same:

1) (T4, T5) = TableFunction1(T1, T2, T3)  
2) (T7, T8) = TableFunction2(T2, T4)
...

and so on. Note that a table function can return multiple tables as outputs, as shown above.

There two key issues to be observed here:

a) The values of tables T7 and T8 depend on tables T2 and T4. Therefore, the tables T7 and T8 need to be updated by executing the function "TableFunction2" only if there is a change in one of the input parameters T2 or T4.

Similarly, T4 need to be updated only if T1, T2 or T3 is updated; dependency tree!

b) Separation of database from the calculation process: The calculation engine must work independent of any fixed data structure or database schema so that it can be integrated with any database and data structure.

You can find my related article where these principles are explained at:

Logical Architecture of a Rule-Based Calculation Framework http://finaquant.com/logical-architecture-of-a-rule-based-calculation-framework/1053

Now, a C#/.NET library for a calculation engine with tables as input and output parameters is being developed based on these principles.

Note to moderators: Please delete the link above if it is counted as self-promotion.

tuncalik
  • 1,124
  • 1
  • 14
  • 20
  • Another good starting point: http://www.codeproject.com/Articles/17853/Implementing-an-Excel-like-formula-engine – tuncalik Dec 20 '12 at 12:05
1

I would recommend to also give a look at Jace. Jace is a more modern calculation engine for the .NET framework. It is a lot faster then the above proposed NCalc. Furthermore it supports more platforms (.NET, WinRT, WP7 and WP8).

More information about Jace can be found on the GitHub page: https://github.com/pieterderycke/Jace

NuGet link: https://www.nuget.org/packages/Jace

MuSTaNG
  • 3,832
  • 2
  • 14
  • 6
1

I asked a similar question here: Truly declarative language?

As far as I know not, only just heard about NCalc, I'll look into it. I have a project that does pretty much what you describe and then some like caching/ cache dropping on changes to values or model structure. Think f it as a crossover between a database and Excel. You can also define classes etc and link them together into large models with millions of objects in graphs, not just trees. Client server etc. Also there's a model editor where you create models in a UI so you can analyse how all the calculations build on each other.

Why exactly are you asking?

Community
  • 1
  • 1
gjvdkamp
  • 9,929
  • 3
  • 38
  • 46
-1

One of problems with generic rule engine efficiency is recalculation of sums groups etc. Imagine that you want to calculates sum of VAT taxes. In your invoice event appeared that some product changed and vat changed from one to another. Now the simples solution is to recalculate all taxes. But we could imagine smart solution that we know that in given group we need to subtract vat amount and add it to another group. Another problem is cycles. We can have demand that our engine would calculate part of remaining to pay amount when we enter it into paid field. And in another case we would want to calculate paid field if we enter it into remaining to pay field. This could be done by virtual field "which field was entered by user" and if depending on it in the rule