smats  0.0.1
Satisfability Modulo Arithmetic Theories Symbols
Loading...
Searching...
No Matches
smats::Expression< T > Class Template Reference

#include <expression.h>

Public Member Functions

 Expression ()
 
 Expression (const T &constant)
 
 Expression (const Variable &var)
 
ExpressionKind kind () const
 
Variables variables () const
 
bool equal_to (const Expression< T > &e) const
 
bool less (const Expression< T > &e) const
 
bool is_polynomial () const
 
evaluate (const Environment< T > &env={}) const
 
Expression< T > evaluate_partial (const Environment< T > &env) const
 
bool is_expanded () const
 

Static Public Member Functions

static Expression< T > zero ()
 
static Expression< T > one ()
 
static Expression< T > pi ()
 
static Expression< T > e ()
 
static Expression< T > NaN ()
 

Friends

class ExpressionCell< T >
 
class ExpressionAddFactory< T >
 
class ExpressionMulFactory< T >
 

Detailed Description

template<class T>
class smats::Expression< T >

Represents a symbolic form of an expression.

Its syntax tree is as follows:

E := Var | Constant | E + ... + E | E * ... * E | E / E | log(E)
   | abs(E) | exp(E) | sqrt(E) | pow(E, E) | sin(E) | cos(E) | tan(E)
   | asin(E) | acos(E) | atan(E) | atan2(E, E) | sinh(E) | cosh(E) | tanh(E)
   | min(E, E) | max(E, E) | ceil(E) | floor(E) | if_then_else(F, E, E)
   | NaN | uninterpreted_function(name, {v_1, ..., v_n})

In the implementation, Expression directly stores a shared pointer to a const ExpressionCell class that is a super-class of different kinds of symbolic expressions (i.e., ExpressionAdd, ExpressionMul, ExpressionLog, ExpressionSin), which makes it efficient to copy, move, and assign to an Expression.

Note
-E is represented as -1 * E internally.
A subtraction E1 - E2 is represented as E1 + (-1 * E2) internally.

The following simple simplifications are implemented:

E + 0             ->  E
0 + E             ->  E
E - 0             ->  E
E - E             ->  0
E * 1             ->  E
1 * E             ->  E
E * 0             ->  0
0 * E             ->  0
E / 1             ->  E
E / E             ->  1
pow(E, 0)         ->  1
pow(E, 1)         ->  E
E * E             ->  E^2 (= pow(E, 2))
sqrt(E * E)       ->  |E| (= abs(E))
sqrt(E) * sqrt(E) -> E

Constant folding is implemented:

E(c1) + E(c2)  ->  E(c1 + c2)    // c1, c2 are constants
E(c1) - E(c2)  ->  E(c1 - c2)
E(c1) * E(c2)  ->  E(c1 * c2)
E(c1) / E(c2)  ->  E(c1 / c2)
f(E(c))        ->  E(f(c))       // c is a constant, f is a math function

For the math functions which are only defined over a restricted domain (namely, log, sqrt, pow, asin, acos), we check the domain of argument(s), and throw std::domain_error exception if a function is not well-defined for a given argument(s).

Relational operators over expressions (==, !=, <, >, <=, >=) return symbolic::Formula instead of bool. Those operations are declared in formula.h file. To check structural equality between two expressions a separate function, Expression::EqualTo, is provided.

Regarding the arithmetic of an Expression when operating on NaNs, we have the following rules:

  1. NaN values are extremely rare during typical computations. Because they are difficult to handle symbolically, we will round that up to "must never occur". We allow the user to form ExpressionNaN cells in a symbolic tree. For example, the user can initialize an Expression to NaN and then overwrite it later. However, evaluating a tree that has NaN in its evaluated sub-trees is an error (see rule (3) below).
  2. It's still valid for code to check isnan in order to fail-fast. So we provide isnan(const Expression&) for the common case of non-NaN value returning False. This way, code can fail-fast with mpq_class yet still compile with Expression.
  3. If there are expressions that embed separate cases (if_then_else), some of the sub-expressions may be not used in evaluation when they are in the not-taken case (for NaN reasons or any other reason). Bad values within those not-taken branches does not cause exceptions.
  4. The isnan check is different than if_then_else. In the latter, the ExpressionNaN is within a dead sub-expression branch. In the former, it appears in an evaluated trunk. That goes against rule (1) where a NaN anywhere in a computation (other than dead code) is an error.

Constructor & Destructor Documentation

◆ Expression() [1/2]

template<class T >
smats::Expression< T >::Expression ( )

Construct a new expression, Default to zero object.

◆ Expression() [2/2]

template<class T >
smats::Expression< T >::Expression ( const Variable & var)

Constructs an expression from var.

Precondition
var is not a BOOLEAN variable.

Member Function Documentation

◆ equal_to()

template<class T >
bool smats::Expression< T >::equal_to ( const Expression< T > & e) const
nodiscard

Checks structural equality.

Two expressions e1 and e2 are structurally equal when they have the same internal AST(abstract-syntax tree) representation. Please note that we can have two computationally (or extensionally) equivalent expressions which are not structurally equal. For example, consider:

\[ e1 = 2 * (x + y) \\ e2 = 2x + 2y \]

Obviously, we know that e1 and e2 are evaluated to the same value for all assignments to x and y. However, e1 and e2 are not structurally equal by the definition. Note that e1 is a multiplication expression (is_multiplication(e1) is true) while e2 is an addition expression (is_addition(e2) is true).

One main reason we use structural equality in EqualTo is due to Richardson's Theorem. It states that checking ∀x. E(x) = F(x) is undecidable when we allow sin, asin, log, exp in E and F. Read https://en.wikipedia.org/wiki/Richardson%27s_theorem for details.

Note that for polynomial cases, you can use Expand method and check if two polynomial expressions p1 and p2 are computationally equal. To do so, you check the following:

p1.Expand().EqualTo(p2.Expand())

◆ evaluate()

template<class T >
T smats::Expression< T >::evaluate ( const Environment< T > & env = {}) const
nodiscard

Evaluates using a given environment (by default, an empty environment).

Exceptions
std::exceptionif there exists a non-random variable in this expression whose assignment is not provided by env
std::exceptionif an unassigned random variable is detected while random_generator is nullptr
std::exceptionif NaN is detected during evaluation.

◆ evaluate_partial()

template<class T >
Expression< T > smats::Expression< T >::evaluate_partial ( const Environment< T > & env) const
nodiscard

Partially evaluates this expression using an environment env.

Internally, this method uses env to substitute (Variable → Constant) and call Evaluate::Substitute with it.

Exceptions
std::exceptionif NaN is detected during evaluation.

◆ is_expanded()

template<class T >
bool smats::Expression< T >::is_expanded ( ) const
nodiscard

Returns true if this symbolic expression is already expanded. Expression::Expand() uses this flag to avoid calling ExpressionCell::Expand() on an pre-expanded expressions. Expression::Expand() also sets this flag before returning the result.

Note
This check is conservative in that false does not always indicate that the expression is not expanded. This is because exact checks can be costly and we want to avoid the exact check at the construction time.

◆ is_polynomial()

template<class T >
bool smats::Expression< T >::is_polynomial ( ) const
nodiscard

Check whether the expression is polynomial.

Returns
true if the expression is polynomial
false if the expression is not polynomial

◆ kind()

template<class T >
ExpressionKind smats::Expression< T >::kind ( ) const
nodiscard

Get read-only access to the kind of the expression.

Returns
kind of the expression

◆ less()

template<class T >
bool smats::Expression< T >::less ( const Expression< T > & e) const
nodiscard

Compare two expressions to determine the order between them.

Parameters
oother object to compare against
Returns
true if this object is less than the other object
false if this object is not less than the other object

◆ variables()

template<class T >
Variables smats::Expression< T >::variables ( ) const
nodiscard

Get read-only access to the variables of the expression.

Returns
variables of the expression

The documentation for this class was generated from the following files: