Python

This page contains an overview of all classes and methods accessible through the Python API. Alternatively one can view it here.

class Expression:

A Symbolica expression.

Supports standard arithmetic operations, such as addition and multiplication.

Examples

>>> x = Expression.var('x')
>>> e = x**2 + 2 - x + 1 / x**4
>>> print(e)

def var (_cls, name: str ) -> symbolica.symbolica.Expression:

Creates a Symbolica expression that is a single variable.

Examples

>>> var_x = Expression.var('x')
>>> print(var_x)
x

def vars (_cls, *names: str ) -> Sequence[symbolica.symbolica.Expression]:

Create a Symbolica variable for every name in *names.

def fun ( _cls, name: str, is_symmetric: bool = False ) -> symbolica.symbolica.Function:

Creates a new Symbolica function with a given name.

Examples

>>> f = Expression.fun('f')
>>> e = f(1,2)
>>> print(e)
f(1,2)

def funs (_cls, *names: str ) -> Sequence[symbolica.symbolica.Function]:

Create a Symbolica function for every name in *names.

def parse (_cls, input: str ) -> symbolica.symbolica.Expression:

Parse a Symbolica expression from a string.

Parameters

input: str An input string. UTF-8 character are allowed.

Examples

>>> e = Expression.parse('x^2+y+y*4')
>>> print(e)
x^2+5*y

Raises

ValueError If the input is not a valid Symbolica expression.

def pretty_str ( self, terms_on_new_line: bool = False, color_top_level_sum: bool = True, color_builtin_functions: bool = True, print_finite_field: bool = True, explicit_rational_polynomial: bool = False, number_thousands_separator: Optional[str] = None, multiplication_operator: str = '*', square_brackets_for_function: bool = False, num_exp_as_superscript: bool = True, latex: bool = False) -> str:

Convert the expression into a human-readable string, with tunable settings.

Examples

>>> a = Expression.parse('128378127123 z^(2/3)*w^2/x/y + y^4 + z^34 + x^(x+2)+3/5+f(x,x^2)')
>>> print(a.pretty_str(latex=True))

def get_type (self ) -> symbolica.symbolica.AtomType:

Get the type of the atom.

def to_atom_tree (self ) -> symbolica.symbolica.AtomTree:

Convert the expression to a tree.

def get_name (self) -> Optional[str]:

Get the name of a variable or function if the current atom is a variable or function.

def transform (self ) -> symbolica.symbolica.Transformer:

Convert the input to a transformer, on which subsequent transformations can be applied.

def req_len ( self, min_length: int, max_length: int | None ) -> symbolica.symbolica.PatternRestriction:

Create a pattern restriction based on the wildcard length before downcasting.

def req_type ( self, atom_type: symbolica.symbolica.AtomType ) -> symbolica.symbolica.PatternRestriction:

Create a pattern restriction that tests the type of the atom.

Examples

>>> from symbolica import Expression, AtomType
>>> x, x_ = Expression.vars('x', 'x_')
>>> f = Expression.fun("f")
>>> e = f(x)*f(2)*f(f(3))
>>> e = e.replace_all(f(x_), 1, x_.req_type(AtomType.Num))
>>> print(e)

Yields f(x)*f(1).

def req_lit (self ) -> symbolica.symbolica.PatternRestriction:

Create a pattern restriction that treats the wildcard as a literal variable, so that it only matches to itself.

def req ( self, filter_fn: Callable[[symbolica.symbolica.Expression], bool] ) -> symbolica.symbolica.PatternRestriction:

Create a new pattern restriction that calls the function filter_fn with the matched atom that should return a boolean. If true, the pattern matches.

Examples

>>> from symbolica import Expression
>>> x_ = Expression.var('x_')
>>> f = Expression.fun("f")
>>> e = f(1)*f(2)*f(3)
>>> e = e.replace_all(f(x_), 1, x_.req(lambda m: m == 2 or m == 3))

def req_cmp ( self, other: symbolica.symbolica.Expression | int, cmp_fn: Callable[[symbolica.symbolica.Expression, symbolica.symbolica.Expression], bool] ) -> symbolica.symbolica.PatternRestriction:

Create a new pattern restriction that calls the function cmp_fn with another the matched atom and the match atom of the other wildcard that should return a boolean. If true, the pattern matches.

Examples

>>> from symbolica import Expression
>>> x_, y_ = Expression.vars('x_', 'y_')
>>> f = Expression.fun("f")
>>> e = f(1)*f(2)*f(3)
>>> e = e.replace_all(f(x_)*f(y_), 1, x_.req_cmp(y_, lambda m1, m2: m1 + m2 == 4))

def map ( self, transformations: symbolica.symbolica.Transformer ) -> symbolica.symbolica.Expression:

Map the transformations to every term in the expression. The execution happen in parallel.

Examples

>>> x, x_ = Expression.vars('x', 'x_')
>>> e = (1+x)**2
>>> r = e.map(Transformer().expand().replace_all(x, 6))
>>> print(r)

def set_coefficient_ring ( self, vars: Sequence[symbolica.symbolica.Expression] ) -> symbolica.symbolica.Expression:

Set the coefficient ring to contain the variables in the vars list. This will move all variables into a rational polynomial function.

Parameters

vars : List[Expression] A list of variables

def expand (self ) -> symbolica.symbolica.Expression:

Expand the expression.

Derive the expression w.r.t the variable x.

def to_polynomial ( self, vars: Optional[Sequence[ symbolica.symbolica.Expression]] = None ) -> symbolica.symbolica.Polynomial:

Convert the expression to a polynomial, optionally, with the variables and the ordering specified in vars.

def to_rational_polynomial ( self, vars: Optional[Sequence[ symbolica.symbolica.Expression]] = None ) -> symbolica.symbolica.RationalPolynomial:

Convert the expression to a rational polynomial, optionally, with the variables and the ordering specified in vars. The latter is useful if it is known in advance that more variables may be added in the future to the rational polynomial through composition with other rational polynomials.

Examples

>>> a = Expression.parse('(1 + 3*x1 + 5*x2 + 7*x3 + 9*x4 + 11*x5 + 13*x6 + 15*x7)^2 - 1').to_rational_polynomial()
>>> print(a)

def to_rational_polynomial_small_exponent ( self, vars: Optional[Sequence[ symbolica.symbolica.Expression]] = None ) -> symbolica.symbolica.RationalPolynomial:

Similar to [PythonExpression::to_rational_polynomial()], but the power of each variable limited to 255.

def match ( self, lhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, cond: Optional[symbolica.symbolica. PatternRestriction] = None ) -> symbolica.symbolica.MatchIterator:

Return an iterator over the pattern self matching to lhs. Restrictions on pattern can be supplied through cond.

Examples

>>> x, x_ = Expression.vars('x','x_')
>>> f = Expression.fun('f')
>>> e = f(x)*f(1)*f(2)*f(3)
>>> for match in e.match(f(x_)):
>>>    for map in match:
>>>        print(map[0],'=', map[1])

def replace ( self, lhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, rhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, cond: Optional[symbolica.symbolica. PatternRestriction] = None ) -> symbolica.symbolica.ReplaceIterator:

Return an iterator over the pattern self matching to lhs. Restrictions on pattern can be supplied through cond.

Examples

>>> x, x_ = Expression.vars('x','x_')
>>> f = Expression.fun('f')
>>> e = f(x)*f(1)*f(2)*f(3)
>>> for match in e.match(f(x_)):
>>>    for map in match:
>>>        print(map[0],'=', map[1])

def replace_all ( self, lhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, rhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, cond: Optional[symbolica.symbolica. PatternRestriction] = None ) -> symbolica.symbolica.Expression:

Replace all patterns matching the left-hand side lhs by the right-hand side rhs. Restrictions on pattern can be supplied through cond.

Examples

>>> x, w1_, w2_ = Expression.vars('x','w1_','w2_')
>>> f = Expression.fun('f')
>>> e = f(3,x)
>>> r = e.replace_all(f(w1_,w2_), f(w1_ - 1, w2_**2), (w1_ >= 1) & w2_.is_var())
>>> print(r)

w

Create a wildcard from a variable name by appending a _ if none is present yet.

class Function:

A function class for python that constructs an Expression when called with arguments. This allows to write:

f = Expression.fun("f")
e = f(1,2,3)

Function (is_symmetric: Optional[bool])

w

class Transformer:

Operations that transform an expression.

def expand (self ) -> symbolica.symbolica.Transformer:

Expand products and powers.

Create a transformer that derives self w.r.t the variable x.

def replace_all ( self, pat: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, rhs: symbolica.symbolica.Transformer | symbolica.symbolica.Expression | int, cond: Optional[symbolica.symbolica. PatternRestriction] = None ) -> symbolica.symbolica.Transformer:

Create a transformer that replaces all patterns matching the left-hand side self by the right-hand side rhs. Restrictions on pattern can be supplied through cond.

Examples

>>> x, w1_, w2_ = Expression.vars('x','w1_','w2_')
>>> f = Expression.fun('f')
>>> e = f(3,x)
>>> r = e.transform().replace_all(f(w1_,w2_), f(w1_ - 1, w2_**2), (w1_ >= 1) & w2_.is_var())
>>> print(r)

class Polynomial:

def parse (_cls, input: str, vars: List[str] ) -> symbolica.symbolica.Polynomial:

Parse a polynomial with rational coefficients from a string. The input must be written in an expanded format and a list of all the variables must be provided.

If these requirements are too strict, use Expression.to_polynomial() or RationalPolynomial.parse() instead.

Examples

>>> e = Polynomial.parse('3/4*x^2+y+y*4', ['x', 'y'])

Raises

ValueError If the input is not a valid Symbolica polynomial.

def to_integer_polynomial (self ) -> symbolica.symbolica.IntegerPolynomial:

Convert the polynomial to a polynomial with integer coefficients, if possible.

Divide self by rhs, returning the quotient and remainder.

Compute the greatest common divisor (GCD) of two polynomials.

class IntegerPolynomial:

def parse (_cls, input: str, vars: List[str] ) -> symbolica.symbolica.Polynomial:

Parse a polynomial with integer coefficients from a string. The input must be written in an expanded format and a list of all the variables must be provided.

If these requirements are too strict, use Expression.to_polynomial() or RationalPolynomial.parse() instead.

Examples

>>> e = Polynomial.parse('3*x^2+y+y*4', ['x', 'y'])

Raises

ValueError If the input is not a valid Symbolica polynomial.

Divide self by rhs, returning the quotient and remainder.

Compute the greatest common divisor (GCD) of two polynomials.

class RationalPolynomial:

A Symbolica rational polynomial.

RationalPolynomial ( den: symbolica.symbolica.Polynomial )

def parse ( _cls, input: str, vars: List[str] ) -> symbolica.symbolica.RationalPolynomial:

Parse a rational polynomial from a string. The list of all the variables must be provided.

If this requirements is too strict, use Expression.to_polynomial() instead.

Examples

>>> e = Polynomial.parse('3/4*x^2+y+y*4', ['x', 'y'])

Raises

ValueError If the input is not a valid Symbolica rational polynomial.

Compute the greatest common divisor (GCD) of two rational polynomials.

class RationalPolynomialSmallExponent:

A Symbolica rational polynomial with variable powers limited to 255.

def parse ( _cls, input: str, vars: List[str] ) -> symbolica.symbolica.RationalPolynomial:

Parse a rational polynomial from a string. The list of all the variables must be provided.

If this requirements is too strict, use Expression.to_polynomial() instead.

Examples

>>> e = Polynomial.parse('3/4*x^2+y+y*4', ['x', 'y'])

Raises

ValueError If the input is not a valid Symbolica rational polynomial.

Compute the greatest common divisor (GCD) of two rational polynomials.

class NumericalIntegrator:

def continuous ( n_dims: int, n_bins: int = 128, min_samples_for_update: int = 100, bin_number_evolution: List[int] = None, train_on_avg: bool = False ) -> symbolica.symbolica.NumericalIntegrator:

Create a new continuous grid for the numerical integrator.

def discrete ( bins: List[Optional[ symbolica.symbolica.NumericalIntegrator]], max_prob_ratio: float = 100.0, train_on_avg: bool = False ) -> symbolica.symbolica.NumericalIntegrator:

Create a new discrete grid for the numerical integrator. Each bin can have a sub-grid.

Examples

>>> def integrand(samples: list[Sample]):
>>>     res = []
>>>     for sample in samples:
>>>         if sample.d[0] == 0:
>>>             res.append(sample.c[0]**2)
>>>         else:
>>>             res.append(sample.c[0]**1/2)
>>>     return res
>>>
>>> integrator = NumericalIntegrator.discrete(
>>>     [NumericalIntegrator.continuous(1), NumericalIntegrator.continuous(1)])
>>> integrator.integrate(integrand, True, 10, 10000)

def sample (self, num_samples: int ) -> List[symbolica.symbolica.Sample]:

Sample num_samples points from the grid.

def add_training_samples (self, samples: List[symbolica.symbolica.Sample], evals: List[float]):

Add the samples and their corresponding function evaluations to the grid. Call update after to update the grid and to obtain the new expected value for the integral.

def update (self, learning_rate: float ) -> Tuple[float, float, float]:

Update the grid using the learning_rate.

Examples

>>> from symbolica import NumericalIntegrator, Sample
>>>
>>> def integrand(samples: list[Sample]):
>>>     res = []
>>>     for sample in samples:
>>>         res.append(sample.c[0]**2+sample.c[1]**2)
>>>     return res
>>>
>>> integrator = NumericalIntegrator.continuous(2)
>>> for i in range(10):
>>>     samples = integrator.sample(10000 + i * 1000)
>>>     res = integrand(samples)
>>>     integrator.add_training_samples(samples, res)
>>>     avg, err, chi_sq = integrator.update(1.5)
>>>     print('Iteration {}: {:.6} +- {:.6}, chi={:.6}'.format(i+1, avg, err, chi_sq))

def integrate ( self, integrand: Callable[[List[symbolica.symbolica.Sample ]], List[float]], max_n_iter: int = 10000000, min_error: float = 0.01, n_samples_per_iter: int = 10000, show_stats: bool = True ) -> Tuple[float, float, float]:

Integrate the function integrand that maps a list of Samples to a list of floats. The return value is the average, the statistical error, and chi-squared of the integral.

With show_stats=True, intermediate statistics will be printed. max_n_iter determines the number of iterations and n_samples_per_iter determine the number of samples per iteration. This is the same amount of samples that the integrand function will be called with.

For more flexibility, use sample, add_training_samples and update. See update for an example.

Examples

>>> from symbolica import NumericalIntegrator, Sample
>>>
>>> def integrand(samples: list[Sample]):
>>>     res = []
>>>     for sample in samples:
>>>         res.append(sample.c[0]**2+sample.c[1]**2)
>>>     return res
>>>
>>> avg, err = NumericalIntegrator.continuous(2).integrate(integrand, True, 10, 100000)
>>> print('Result: {} +- {}'.format(avg, err))

class Sample:

A sample from the Symbolica integrator. It could consist of discrete layers, accessible with d (empty when there are not discrete layers), and the final continous layer c if it is present.

weights: List[float]

The weights the integrator assigned to this sample point, given in descending order: first the discrete layer weights and then the continuous layer weight.

c: List[float]

A sample in the continuous layer. Empty if not present.

d: List[int]

A sample point per (nested) discrete layer. Empty if not present.

class AtomType:

Specifies the type of the atom.

Num = AtomType.Num

Var = AtomType.Var

Add = AtomType.Add

Mul = AtomType.Mul

Pow = AtomType.Pow

class AtomTree:

A Python representation of a Symbolica expression. The type of the atom is provided in atom_type.

The head contains the string representation of:

  • a number if the type is Num
  • the variable if the type is Var
  • the function name if the type is Fn
  • otherwise it is None.

The tail contains the child atoms:

  • the summand for type Add
  • the factors for type Mul
  • the base and exponent for type Pow
  • the function arguments for type Fn

The list of child atoms of this atom.

The type of this atom.

head: Optional[str]

The string data of this atom.