### Bacovia: a symbolic math library

Named after the great symbolist poet, George Bacovia, I created this library to symbolically manipulate mathematical expressions in a simple and elegant way.

Before writing a symbolic math library, this was a somewhat mysterious subject to me, but in this post I would like to try to demystify and illustrate the beauty and satisfaction that comes from writing this simple, but powerful, symbolic math library.

The first thing in creating a new project, is the selection of the right programming language for the project. Just for fun, I decided to implement this library in the Sidef programming language, using recursion and multiple dispatch; two very powerful features that turned out to be just perfect for this task.

####

Represents a symbolic value. Optionally, it can have a numeric value.

####

Represents a symbolic fraction.

####

Represents a symbolic difference.

####

Represents a symbolic exponentiation in a symbolic base.

####

Represents the natural logarithm of a symbolic value.

####

Represents the natural exponentiation of a symbolic value.

####

Represents a summation of an arbitrary (finite) number of symbolic values.

####

####

####

Based on the

In selecting the shortest expressions, it also calls the

####

The library also supports numerical evaluation, recursively evaluating each expression numerically.

Most of the other classes are implemented in a similar way, which for brevity, I will not include them in this post.

Here, in the

Bellow I included a few examples to give the reader an idea for what can be done with this library. First, the library can be included using the

As a first example, we implement a closed-form to the Riemann zeta function for positive even integers, as defined here:

The second example illustrates the

In this third example we take a look at some trigonometric functions applied on symbolic values. First, let's define a symbolic value with an associated numerical value.

The first library requires the Sidef programming language.

Before writing a symbolic math library, this was a somewhat mysterious subject to me, but in this post I would like to try to demystify and illustrate the beauty and satisfaction that comes from writing this simple, but powerful, symbolic math library.

The first thing in creating a new project, is the selection of the right programming language for the project. Just for fun, I decided to implement this library in the Sidef programming language, using recursion and multiple dispatch; two very powerful features that turned out to be just perfect for this task.

## # Design

The library has a simple class hierarchy, with one base class, and several other classes inheriting from it.class {}class< {}class< {}class< {}class< {}class< {}class< {}class< {}class< {}

## # Classes

The classes provided by the Bacovia library, are described bellow:####
`Symbol(name, value=nil)`

Represents a symbolic value. Optionally, it can have a numeric value.####
`Fraction(numerator, denominator)`

Represents a symbolic fraction.
####
`Difference(minuend, subtrahend)`

Represents a symbolic difference.
####
`Power(base, power)`

Represents a symbolic exponentiation in a symbolic base.####
`Log(x)`

Represents the natural logarithm of a symbolic value.####
`Exp(x)`

Represents the natural exponentiation of a symbolic value.####
`Sum(a, b, c, ...)`

Represents a summation of an arbitrary (finite) number of symbolic values.####
`Product(a, b, c, ...)`

Represents a product of an arbitrary (finite) number of symbolic values.

## # Special methods

To make the library more interesting, I included some special methods that do some interesting stuff with the self-expression. This methods are described bellow.

####

####
`* alternatives()`

The most exciting feature is the support for alternative representations, which uses common mathematical identities to create symbolically equivalent expressions from the self-expression, returning an array with the alternative representations, each as a distinct object.

**Example:**((3) * 2) -> .each { .say }

**Output:**(((3), 2)) (3, 2) 9

####
`* pretty()`

This method returns a human-readable representation for the self-expression, recursively calling the

*pretty()*method on each object value.**Example:**

Exp(4)**(((1, 2), 3)) -> .say

**Output:**

exp(log((1 / 2)^3) * 4)

####
`* simple()`

Based on the *alternatives()*method, the

*simple()*method returns the shortest expression from the list of all the possible alternative expressions.

In selecting the shortest expressions, it also calls the

*pretty()*method and computes the length of the human-readable text of each alternative expression. Then, based on this lengths, it selects the shortest alternative expression and returns it.

**Example:**((((((('x'))))))) -> .say

**Output:**('x')

####
`* numeric()`

The library also supports numerical evaluation, recursively evaluating each expression numerically.**Example:**((3, 42), 5) -> .say

**Output:**

` 21883797826302471841.8`

## # Bacovia

The**Bacovia**base class implements all the generic operations, which are the default operations for all the other classes that inherit from it.

class {method+( ) { (self, ) }method-( ) { (self, ) }method*(Bacovia ) { (self, ) }method/(Bacovia ) { (self, ) }method**(Bacovia ) { (self, ) }method{ (0, self) }method{ (1, self) }method{ [self] }method{self.. { .. } } }

## # Power

The**Power**class takes two arguments: the base and the exponent, which can be implemented something like this:

class(, ) {method**( ) { (, * ) }method==( ) { ( == .) && ( == .) }method==() { }method{ (, .) }method() {gather{ for , (. ~ .) {take((() * ))take((, ))take( ** )# Identity: x^log(y) = y^log(x)if (.()) {take(.**()) } } }. { . } }method{ . ** . }method() { "(#{v.pretty})^(#{n.pretty})" }method{ "Power(#{v}, #{n})" } }

## # Product

The**Product**and the**Sum**classes are slightly different that the other classes: they can take an arbitrary number of arguments which are internally stored inside an array. The interesting part comes in manipulating all of this values symbolically.class(*values) {method*( ) { (values..., .values...) }method/( ) { (values..., .values.map{.}...) }method/( ) { = [values...] if (.()) { (...) } else { (..., .) } }method*( ) { = [values...] if (.(.)) { (...) } else { (..., ) } }method**( ) { (values.map{ ** }...) }method{ Product(-1, values...) }method() { { values.map{.}. { |*| (.('*')) } }. { . } }method==( ) { values == }method==() { }method{ (values.map { . }...) }method{ values.map { . }. }method() { '(' + values.map{.}.join(' * ') + ')' }method{ "Product(#{values.join(', ')})" } }

Here, in the

*alternatives()*method, we use the Cartesian product, which generates all the possible combinations of alternative representations. In combination with*simple()*, it can find the shortest possible alternative representation, based on the identities specified in each class.## # Examples

The library can be used for various things, such as finding alternative representations for a certain expression, or for symbolically simplifying expressions, making them shorter and easier to write them down.Bellow I included a few examples to give the reader an idea for what can be done with this library. First, the library can be included using the

*include()*keyword and the path to the main file,**"bacovia.sf"**:('lib/bacovia.sf')

As a first example, we implement a closed-form to the Riemann zeta function for positive even integers, as defined here:

= ((-1) * -2) () { (-1)**(+1) * (2*) * **(2*) / ((2*)! * 2) } for (1..5) { say (). }

**Output:**log(-1)^2 * -4 * (1 / 6) * (1 / 4) log(-1)^4 * 16 * (1 / 30) * (1 / 48) log(-1)^6 * -64 * (1 / 42) * (1 / 1440) log(-1)^8 * 256 * (1 / 30) * (1 / 80640) log(-1)^10 * -1024 * (5 / 66) * (1 / 7257600)

The second example illustrates the

**Fraction**class, which includes some interesting identities for manipulating fractions in a non-reducible way.= (0, 1) for (0..3) { += (1**, +1) -> .say }

**Output:**( 1 + 0) / 1 ( 2 + 1) / 2 ( 4 + 3) / 6 (16 + 6) / 24(The real part of the numerator is A281964, and the imaginary part is A282132)

In this third example we take a look at some trigonometric functions applied on symbolic values. First, let's define a symbolic value with an associated numerical value.

= ('n', 42)Using the symbolic value defined above, we can start investigating two well-known trigonometric functions.

cos(()) -> ..say sin(()) -> ..say

**Output:**The library also has built-in support for hyperbolic trigonometric functions (including an inverse to each trigonometric function).(^- + ^) / 2 (^ - ^-) / (2)

(()) -> ..say (()) -> ..say

**Output:**When a symbolic value has a numeric value associated with it, we have the possibility of evaluating the expression numerically.(1 + ^2) / (2 * ) (^2 - 1) / (2 * )

cos() -> .say #=> -0.3999853149883512939... sin() -> .say #=> -0.9165215479156337858...Inverses to hyperbolic trigonometric functions:

(()) -> .say #=> 42 (()) -> .say #=> 42

## # Implementations

The code of the library is freely available on GitHub.

**Sidef:**https://github.com/trizen/bacovia**Perl:**https://github.com/trizen/Math-BacoviaThe first library requires the Sidef programming language.

## Comments

## Post a Comment