Mathemagics

Symbolic algebra package written in Pharo Smalltalk

View on GitHub

Mathemagics

Symbolic algebra package written in Pharo Smalltalk

Pharo Smalltalk is an expressive and powerful language, but when you just working with it, some things are rather puzzling:

So I embark myself into experimenting with an implementation of symbolic algebra for Smalltalk. Mathematics is a huge language in itself, and therefore there is an implicit scope on this project, but most algebra and a little of calculus has been implemented. New functions can be added with very little effort and without changing other classes, since the design contemplates ambiguity (unary or multiple function names), precedence, Smalltalk vs Math names, partial name match (#sqrt vs #sqr) and other problems have been solved.

Why is called ‘Mathemagics’?

Originally the package was meant to be called MathEx (short for Mathematical Expressions, and to match RegEx) but nowadays there is more functionality already available. Apart from Mathematical Expressions, the package has a parser, a calculator, a repository for formulas, and more to come in the future.

Ok.. but what can I do with it?

A few examples of things you can do:

  "Notice ** gets replaced by ^ on printing, both accepted"
  MathExpVariable x ** 3 negated                                "x^-3"
  MathExpressionParser new parse: 'x + 2'.                      "x + 2"
  | fx |
  "Notice pi gets represented with a symbol"
  fx := MathExpression from: 'x*x - (pi * -1) + x*8/1'.
  fx simplify.                                                  "x^2 + 8·x + π"
  | fx |
  fx := MathExpression from: 'sin(x)*cos(x+x)/sin(sqr(x)/x)'.
  fx simplify.                                                  "cos(2·x)"
  | fx dFx |
  fx := MathExpression from: 'x*x + ln(x)'.
  dFx:= fx derivativeFor: 'x'.                                  "2·x + 1/x"
  | fx x |
  fx := MathExpression from: 'sqrt(x) + x*x*x + 1/x'.
  fx := fx simplify.                                            "√(x) + x^3 + 1/x"

  "expression with variables are not reduced to a Number"
  fx isNumber.                                                  "false"

  "Extract variable and evaluate for a number"
  x := fx variable: #x.
  x value: 5.
  fx asNumber.                                                  "127.43606797749979"
  "Expression to a Smalltalk BlockClosure"
  | fx x |
  "Math precedence means no parenthesis required"
  fx := MathExpression from: 'x + 3 * pi'.                      "x + π·3"

  "New method will generate code with Smalltalk precedence"
  fx asBlock                                                    "[ :x | x + (3 * Float pi) ]"

What are the main components of this package?

There are 4 basic components:

  1. MathExpression: This class and its subclasses can represent any mathematical expression the user can write using a number, mathematical constants, operators and functions. A complex expression is built as a tree of mathematical expressions. This class is aligned with the Number class, so you will find familiar messages such as +, #square, #isNumber, #asNumber, #positive, etc. On top of those message you will find new functionality such as #asPower, #simplify, #derivativeFor:, #dividend, #isFunction and others. The subclasses prefix are shorten to ‘MathExp’ for simplicity reasons, but you shouldn’t need to use them directly.
    • Unary expressions (sin, cos, abs)
    • Binary expressions (power, log, addition)
    • Value holders (numbers, mathematical constants, variables)
    • Dependencies: String, Number, Set, Dictionary
  2. MathExpressionParser: This class analyses a text and return a MathExpression representing it. The parser uses Maths precedence (not Smalltalk precedence). The parser has a fully implemented non-greedy Infix notation (and it should be possible to extend for Postfix or Polish notations).
    • Parser fits in just 1 class with barely 30 short methods
    • Dynamic analysis of MathExpression hierarchy determines parser capabilities
    • Uses Regular Expressions instead of parser generators to avoid external dependencies
    • The underlying implementation algorithm based on is Shunting Yard
    • Instantiates objects using #perform to run operations same way the user can do
    • Dependencies: MathExpression and subclasses, String, Number, Dictionary, Regex
  3. Calculator: This is a proxy to a simple Spec1 application (in the future can decide to call a Spec2 instead). It mainly contains an edit area, a results area, and a basic toolbar. The user types the expression so there is no need of buttons as in common calculators.
    • Uses the parser and holds MathExpressions with their results
    • Displays the expression as close as possible the user entered (settings available)
    • Simplify the expression if possible (e.g.: User enters x+x it will display the input and = 2*x)
    • Evaluates the expression to a Number if possible (e.g.: User enters 2+pi+2 it will print pi+4 = 7.1415..)
    • It allows copying to clipboard, inspection and manipulation of results
    • Dependencies: MathExpression, MathExpressionParser, Spec1 (atm).
  4. Formula Library: The idea is to have a simple repository of commonly used formulas that can be reused in the future.
    • Class protocols becomes a classification mechanism: Financial, Geometrical, Mathematical, etc.
    • Formula parameters can be numbers or variables for further evaluation
    • Dependencies: MathExpression

Highlighted features

Here are some of the most interesting methods available:

Examples using the package

Goals

Some of the reasons for the design:

For MathExpression

For MathExpressionParser

For Calculator

Formula Library

Tests

Availability

I originally coded and tested in Pharo 6. Uploaded to GitHub in Pharo 8.

To open the GitHub Page click here

Shortcomings and interesting facts

Authors

Gustavo Pistoia - This package was developed by myself, without help or instructions from any person or corporation, on my own time outside working hours and with my own resources. This software represents endless hours of planning, debugging and thinking. Please respect my work and mention my name if you use the software or extend it. Let me know of suggestions, improvements or errors. Feel free to join, colaborate, discuss or help out to make this better. My gmail account is the same as my github profile if you want to contact me.

License

See the LICENSE file for details.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Acknowledgements