NMath API updated with .NET Func<> delegates

At CenterSpace we are always working hard to keep the core NMath processing kernel start of the art, however changes to our libraries’ API usually lag behind any .NET framework developments in a process of deprecation and introduction. The .NET Func<> and Action<> delegates have been a part of the .NET framework now since .NET 3.5 and VS2008. Therefore, we are now deprecating all of our custom delegates in the NMath API and replacing them with .NET Func<> generic delegates. This improves interoperability between NMath and our customers’ code and between our C# API and other .NET languages. Customers will see this change in the NMath 5.2 and NMathStats 3.5.

For those not familiar with the usage and definition of Func<> types within .NET there is a short primer at the end of this post.

Compiler Warning for [Obsolete] NMath defined delegates

All of our NMath delegate types have been deprecated using the .NET [Obsolete()] attribute. The generated compiler warnings will provide the suggested Func type replacement.

To further clarify, the following table lists the deprecated NMath float delegate types side by side with their replacements. All NMath delegates should be redefined in your code using the generic Func types.

List of float-precision deprecated NMath delegate types

Old NMath delegates

Replacement

float FloatFunction()

Func< float >

float FloatUnaryFunction( float x )

Func< float, float >

float FloatBinaryFunction( float x, float y )

Func< float, float, float >

float FloatIntFunction( float x, int y )

Func< float, int, float >

float FloatVectorFloatFunction( FloatVector v )

Func< FloatVector, float >

FloatVector
FloatVectorFloatVectorFunction( FloatVector x )

Func< FloatVector, FloatVector >

There are many other deprecated NMath delegates that should be similarly replaced in your code.

Code examples for updating your code

Where previously you might have created a delegate that takes a double argument and returns a double like this:

All methods in NMath and NMath Stats now take Func<> type delegates. If you use the old NMath delegates types to call a NMath method, an obsolescence warning will be issued by the compiler.

NMath and NMath Stats contain many functors that have now been obsolesced and redefined using a generic Func<> type. The new functor naming convention simply drops the “tion” from “Function” to make the new name. For example, the delegate NMathFunctions.AtanFunction should now be replaced with NMathFunctions.AtanFunc. The old delegate has a type of NMathFunctions.DoubleUnaryFunction, and the new delegate has a type of Func< double, double >. This kind of replacement can be done safely with a global search and replace operation to rid yourself of compiler obsolescence warnings. The table below list a few functor redefinitions that demonstrate the renaming pattern that was applied across the API.

Sampling of deprecated NMath functors

Old NMath functor

Replacement

DoubleUnaryFunction AbsFunction

Func< double, double > AbsFunc

DoubleUnaryFunction AcosFunction

Func< double, double > AcosFunc

DoubleUnaryFunction AsinFunction

Func< double, double > AsinFunc

DoubleBinaryFunction PowFunction

Func< double, double, double > PowFunc

DoubleIntFunction RoundFunction

Func< double, int, double > RoundFunc

DoubleVectorDoubleFunction
DoubleNaNMedianFunction

Func< DoubleVector, double > DoubleNaNMedianFunc

Potential Issues

The deprecation process requires us to maintain the old custom delegate defintions in the API methods while introducing the new Func<> based API. As a result, every method call in our API which previously consumed a custom NMath delgate type, a functionally identical signature was added which consumes a Func<> based delegate. This duplication can cause an easy-to-fix compiler error in situations when an anonymous delegate was used instead of a NMath delegate type. Consider the follow line of code which inverts the singular values of a SVD.

Error 43
The call is ambiguous between the following methods or properties:
'CenterSpace.NMath.Core.DoubleVector.Apply(
CenterSpace.NMath.Core.NMathFunctions.DoubleUnaryFunction )'
and
'CenterSpace.NMath.Core.DoubleVector.Apply( System.Func )' ....

In this error message, the first Apply() is the old method signature and the second Apply() is the new Func<> based signature. The C# compiler can't choose between the two when provided an anonymous delegate. Exactly why this ambiguity error is generated by the compiler is carefully explained by Eric Lippert over on stackoverflow. Briefly, the ambiguity error arises because both methods equally satisfy the Apply() method signature and the C# standard does not provide a way to judge which would be 'better', and so reports an error. The fix is simple, we just specified explicitly that we want to use the method that takes a generic Func<>.

A brief Func<> & Action<> primer

The generic delegate types, Func<> and Action<>, were introduced with the .NET 3.5 framework to unify delegate types across various .NET libraries. This allows a .NET library developer like CenterSpace to create an API that accepts generic delegates that are defined elsewhere in client code. The Func and Action are differentiated by their return values: Actions have no return value, Funcs always have a single return value of type TResult. For each of them, the .NET framework defines a type signature that takes between 0 and 16 generically typed arguments.

.NET Func<> and Action<> definitions

Func

Action

TResult Func()

void Action<>()

TResult Func( T arg )

void Action( T arg )

...

...

TResult Func(
T1 arg1,
T2 arg2,
...
T15 arg15,
T16 arg16 )

void Action(
T1 arg1,
T2 arg2,
...
T15 arg15,
T16 arg16 )

This is still all abstract. Here's how we create a Func using a lambda expression.

This Func delegate takes single double value and returns a double value. Lamda expressions are commonly used today to generate delegates, however an anonymous delegate could also be used for the same purpose.

I find the lambda syntax to be more expressive and succinct. From the table above, T1 = double, and TResult = double.

Action types are not used within the NMath API because we are always dealing with functions and return values. However, Actions are similarly defined, and may be used for example in queuing code where a queue manager takes work in the form of Action delegates.

4 thoughts on “NMath API updated with .NET Func<> delegates”

Another .net thing that would be good for you to support would be the conventional ToArrary() and ToList() functions on double vectors. You could achieve this by making double vector implement IEnumerable.

Also maybe to add a ToVector() extension method to IEnumerable and IEnumerable, and a ToMatrix on double[,].

We have done these internally using extension methods, and it means that we get very clear code when translating between nMath objects and native collections.

We tend to expose native collections as the inputs and outputs at the outer layer of our libraries, so this translation tends to happen just a couple of times and not in the middle of performance critical code which just uses nMath objects or native objects and doesn’t switch between the two.

By the way, your commenting system filters out angle brackets, which is a bit unfortunate when posting .net generics code. My examples in the previous comment were meant to be generic IEnumerables of double and float, but it just left them as IEnumerable

We appreciate the input Mark. We have a ticket to implement IEnumerable for both the float and double vector types. I agree that will noticeably improve interop between NMath types and native .NET types, and enable users to use LINQ expressions across our types.

I’ll check on the angle bracket filtering – that’s not good for a programming blog!

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website

Notify me of follow-up comments by email.

Notify me of new posts by email.

Testimonials

"Thank you very much for your lightning fast support!"

Abdelali Zahi, Allianz

"To answer your question whether I had success in utilizing the NMath libraries in my software development work, my answer is a resounding "YES". Your libraries have significantly helped me cut down my development time and I would like to extend a great big thank you to your development team for a truly outstanding job in develo...

"I spent a year looking for a stats package that was native .NET as well as robust. After looking at dozens of websites and trying a half dozen demos, I bought the full NMath package--and it's great! I use it to analyze fixed income portfolios, where time is of the essence--and it's very fast. It couldn't be easier to integrate ...

Eric Carlino, Ronin Capital

"I've been developing numerical scientific software for fifteen years. I typically write custom-tailored numerical codes, and I've regularly used IMSL, ESSL, Numerical Recipes and Matlab. The development environment of NMath with C# is by far the most elegant, flexible, and amenable to reuse that I've ever encountered. The more ...