Extreme Numerics.NET breaks new ground in usability and productivity for numerical software development. We spent a great deal of time and effort on maximizing the usability of our API. Usability is a broad subject. We will briefly discuss some of its aspects and how they are implemented in Numerics.NET. The resources at the bottom of this page give more information.
Consistency both with Microsoft’s Design Guidelines for Developing Class Libraries and internally, ensures that you can reuse what you have learnt building applications for the .NET framework. Some examples of consistency with the .NET framework design guidelines are:
- The library is fully compliant with the Common Language Specification (CLS).
- The order of parameters in constructors and methods is consistent. Required parameters appear before optional parameters. Optional parameters always appear in the same order.
When an exception is thrown, it is of the right type. For example, DimensionMismatchException, which indicates that the size of vectors and/or matrices aren’t compatible for the requested operation, inherits from ArgumentException.
All operator overloads have static (shared in Visual Basic) method equivalents.
All ToString methods are culture-aware.
- Boolean parameters always default to false.
Examples of internal consistency include:
- All classes that implement numerical integration and solving equations inherit from an abstract base class, IterativeAlgorithm, which specifies properties and methods shared by all these classes.
- Matrices and matrix decompositions all inherit from an abstract base class: LinearTransformation. Regardless of the type of matrix or decomposition, the syntax and semantics of the operation is the same.
Transparency means that the code is self-explanatory. It also means that, with the use of context sensitive help or Intellisense, you should be able to choose the correct classes, methods, and parameter values.
The classes in Numerics.NET are organized in namespaces according to their function. For example, all matrix and vector classes reside in the Extreme.Mathematics.LinearAlgebra namespace, while all numerical integrators reside in Extreme.Mathematics.Calculus.
Members and method parameters have descriptive, meaningful names. We don’t use abbreviations (LUDecomposition, not LUDecomp) or transcriptions of mathematical notation (Predictions, not Yhat). By using expressive names for language elements, you don’t need to spend mental resources on translating their meaning.
A progressive API makes it easy to write code incrementally, starting from a simple core scenario and moving on to increasingly complex ones. At each stage, the tools needed for the next step are available as methods and properties of objects that have been created up to that point. Only rarely is it necessary to construct a new object.
For example, a basic regression analysis can be written in only a few lines of code. The classes that represent regression analysis have methods and properties that let you perform other common tasks: inspect the computed model, perform validation tests for the model and retrieve objects that represent the regression parameters. These objects in turn expose detailed information about the regression parameters, and let you compute confidence intervals and perform tests on those parameters.
This is in contrast to many other libraries where you have to pull together functions from several, unrelated locations and then write the code that connects them.
Extensibility allows you to create specialized versions of objects in the library with minimal effort.
For example, to create a specialized Matrix type, only two members, the GetValue and SetValue methods, must be implemented. All operations on matrices, from arithmetic operations to decompositions, and from solving equations to retrieving rows, columns or submatrices, will work on your specialized matrix type. You may provide your own optimized implementations of almost any other method, but that choice is yours.
Other examples of extensibility include:
- To create a new Vector type, only the GetValue and SetValue methods must be implemented. You can evaluate arithmetic operations, norms, and other properties without writing any more code.
- To create a new Curve, you must supply the ValueAt method. You may want to also define the derivative function and the integral. Both will be evaluated numerically if no implementation is supplied.
- To create a class of functions for curve fitting, all you need to do is derive from FunctionBasis and implement the FillValues method.
- The AdaptiveIntegrator class lets you supply your own integration rule tailored to a specific type of integrand.
- Functionality that is common between different classes is implemented using abstract base classes rather than interfaces. The IterativeAlgorithm mentioned above and LinearTransformation are examples. This means that future extensions can be implemented without having to define new interfaces.
Correspondence of objects in the library with the mathematical concepts you use to describe your problems makes translating your solution into code that much easier.
Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries by Krzysztof Cwalina and Brad Abrams.
Design Guidelines for Developing Class Libraries from Microsoft.
Measuring API Usability by Steven Clarke. Dr. Dobbs Journal Special Windows/.NET supplement, May 2004.
Steven Clarke’s Weblog has information on the Cognitive Dimensions framework for API usability.