Symbolic Submodules

klampt.math.symbolic module

symbolic module

Overview

A framework for declaring and manipulating symbolic expressions. Suitable for automatic differentiation and developing computation graph models of functions for use in optimization.

Inspired by Sympy, SCIP models, Gurobi models, as well as TensorFlow / PyTorch, this framework has the following features:

  • Saving and loading of expressions is fully supported. Strings and JSON formats are supported.

  • Supports black-box functions inside expressions.

  • Allows references to arbitrary non-serializable user data.

  • Allows new functions to be user-defined in a straightforward way.

  • Symbolic auto-differentiation.

  • Conversion to and from Sympy.

  • Numeric, vector, and matrix variables are allowed. Many native numpy operations are supported.

Could it be used for learning? Yes, but it is not as fast as GPU-based learning libraries like TensorFlow / PyTorch.

How does it compare to Sympy? This module does a better job of handling large matrix expressions and variable indexing. For example, the multiplication of 5 symbolic 2x2 matrices takes ~200 microseconds, which is a bit slower than direct numpy evaluation of ~50 microseconds. On the other hand, the Sympy expression takes over 4s to construct, and each entry of the matrix contains hundreds of terms!

This module can also take derivatives with respect to vectors using matrix calculus. It can even auto-differentiate with nested lists/arrays! However, function simplification is not as complete as Sympy, and derivatives for some functions are not available. There are conversions to and from Sympy in the symbolic_sympy module, and these are complete for most “tame” expressions.

Basic usage

In standard usage, first create a Context and declare any extra functions to be used in your library. Then, and add variables and declare expressions as necessary.:

ctx = Context()
x = ctx.addVar("x",'N')
y = ctx.addVar("y",'N')
print(x + y)  #(x+y) is an Expression object, which has not been evaluated yet
print(flatten(x,y))   #flatten(x,y) is an Expression object, which has not been evaluated yet
print((x + y).eval({'x':5,'y':3}))  #prints 8 (a ConstantExpression)
print(flatten(x,y).eval({'x':5,'y':3}))  #prints [5,3] (a ConstantExpression
print(flatten(x,y).eval({'y':[1,2]}))  #prints [x,1,2] (an Expression object)
x.bind(5)  #x is now treated as a constant
y.bind(3)  #y is now treated as a constant
print((x+y).eval())  #prints 8 (a ConstantExpression)
print((x+y).evalf())  #prints 8 (as an integer)
x.unbind() #x is now a variable
y.unbind() #y is now a variable

An Expression can take on a form x OP y or OP(x,y,z), where x,y, and z are either Variable, Expression, or constant values, and OP is either a builtin function or a declared Function type. Expressions can be evaluated to other Expressions via Expression.eval(), or to constants using Expression.evalf().

There are two kinds of Variable:

  • Standard Variable: This type is managed by Context and contains information in the Variable class. Such a variable can either be numeric, vector, or matrix type. It will contain size information and will be saved to and loaded from disk with the Context.

  • User-data Variable. This type is an unmanaged variable. These have unlimited type, and can be complex Python objects, but the user must set these up in the context manually. Hence, if you wish to reuse Expressions of user-data variables loaded from disk, you will need to first set up the Context appropriately with a similar user-data object.

By default, expressions constructed in Python refer to user-data objects with strings, e.g., setConfig("robot",q) runs the symbolic_klampt.setConfig() Function on the “robot” user data and the q Variable.

The expr() function is run on each argument to a Function. This will leave a Variable and Expression as is, and most Python constants will be converted to a ConstantExpression. However, Python strings will be converted to references to user-data variables.

Constant values can be floats, integers, booleans, strings, lists, and numpy arrays (Dictionaries are not supported as constant values.) Constants are converted to expressions using const(), or expr().

Type specifiers are used for type checking and to determine the type of expressions, particularly for Jacobians. The Type class is used for this. The type member specifies the general type of object, and is given by a character:

  • N: generic numeric

  • I: integer

  • B: boolean

  • X: index (either an integer or a slice)

  • V: 1D vector

  • M: 2D matrix

  • A: n-D array

  • L: list

  • U: user data

  • None: indicates an unknown type

as well as an optional size and sub-type. For L and V types, size is the length of the array. For M and A types, size is the Numpy shape of the array. For the U type, the subtype member indicates __class__.__name__ if known.

Standard functions

Standard operations on expressions include:

  • expr(x): returns an Expression corresponding to x, whether x is a constant, Variable, or Expression

  • deriv(expr,var): take the derivative of expr with respect to var. If no derivative information is available for some of the expressions in expr, returns None.

  • simplify(expr,context=None): simplifies the expression in the optimal given context. Note: this is not terribly powerful in terms of rearranging expressions (like x - x is not converted to 0).

  • is_const(x): returns True if x evaluates to a constant.

  • to_const(x): returns the constant to which x evaluates, if is_const(x).

  • is_expr(x): returns True if x is an Expression.

  • is_var(x): returns True if x is a Variable or a VariableExpression (i.e., monomial).

  • to_var(x): returns the Variable corresponding to the Variable or monomial x.

  • is_op(x,func=None): returns True if x is an operator. If func is given, checks whether this is the the name of the function.

  • is_zero(x): returns True if x is the zero(…) operator or a scalar equal to 0.

  • is_scalar(x,val=None): returns True if x is known to evaluate to a scalar. If val is provided, it checks whether x is equal to val.

  • type_of(x): returns the Type corresponding to x.

Note

These are Python functions, not symbolic Functions.

Symbolic Functions

You can build an Expression by calling a symbolic Function, many of which duplicate the functions of plain Python, the math module, or Numpy. Calling a Function does not immediately evaluate the function on its arguments, but instead builds an Expression that is the root of a symbolic Expression Graph.

Built-in symbolic Functions include:

Shape functions

  • range_(n): Evaluates to range(n). The result can be treated like a vector or a list.

  • dims(x): Returns the dimensionality of the input. Equivalent to len(shape(x)).

  • len_(x): Evaluates to len(x), except if x is a scalar then it evaluates to 0. If x is a multi-dimensional array, this is the length of its first dimension. Undefined for other forms of user data.

  • count(x): Evaluates the number of numeric parameters in x. Works with scalars, arrays, and lists. If x is a scalar then it evaluates to 1. Lists are evaluated recursively. Undefined for other forms of user data.

  • shape(x): Evaluates to x.shape if x is a Numpy array, (len_(x),) if x is a vector, and () if x is a scalar. If x is a list, this is (len_(x),)+shape(item) if all of the items have the same shape, and otherwise it is a hyper-shape. (shortcut: “x.shape”)

  • reshape(x,s): Evaluates to x reshaped to the shape s. If x is a scalar, this evaluates to a constant matrix.

  • transpose(x): Evaluates to np.transpose(x). (shortcut: “x.T”)

  • transpose2(x,axes): Tensor transpose; evaluates to np.transpose(x,axes).

  • basis(i,n): Evaluates to the i’th elementary basis vector in dimension n.

  • eye(n): Evaluates to np.eye(n) if n > 0, otherwise returns 1.

  • zero(s): Evaluates to np.zeros(s) if s is a matrix shape or scalar > 0, otherwise evaluates to 0.

  • diag(x): Evaluates to np.diag(x).

  • flatten(*args): Evaluates to a vector where all arguments are stacked (concatenated) into a single vector. Arrays are reduced to vectors by Numpy’s flatten(), and complex objects are reduced via recursive flattening.

  • row_stack(*args): Evaluates to a matrix where all arguments are stacked vertically. 1D arrays are treated as row vectors. If only a single list element is provided, then all arguments are stacked.

  • column_stack(*args): Evaluates to a matrix where all arguments are stacked horizontally. 1D arrays are treated as column vectors. If only a single list element is provided, then all arguments are stacked.

Comparisons and logical functions

  • eq(lhs,rhs): Evaluates to lhs = rhs (shortcut: “lhs = rhs”).

  • ne(lhs,rhs): Evaluates to lhs != rhs (shortcut: “lhs != rhs”).

  • le(lhs,rhs): Evaluates to lhs <= rhs (shortcut: “lhs <= rhs”).

  • ge(lhs,rhs): Evaluates to lhs >= rhs (shortcut: “lhs >= rhs”).

  • not_(x): Evaluates to not x (shortcut: “not x”).

  • or_(x,y): Evaluates to x or y (shortcut: “x or y”).

  • and_(x,y): Evaluates to x and y (shortcut: “x and y”).

  • any_(*args): Evaluates to any(*args)

  • all_(*args): Evaluates to all(*args)

Arithmetic functions

  • neg(x): Evaluates to -x (shorcut “-x”).

  • abs_(x): Evaluates to abs(x) (shortcut: “abs(x)”). Works with arrays too (elementwise)

  • sign(x): Evaluates the sign of x. Works with arrays too (elementwise).

  • add(x,y): Evaluates to x + y (shortcut: “x + y”). Works with arrays too.

  • sub(x,y): Evaluates to x - y (shortcut: “x - y”). Works with arrays too, and vector - scalar.

  • mul(x,y): Evaluates to x * y (shortcut: “x * y”). Works with arrays too (elementwise multiplication).

  • div(x,y): Evaluates to x / y (shortcut: “x / y”). Works with arrays too (elementwise division), and vector / scalar.

  • pow_(x,y): Evaluates to pow(x,y) (shortcut “x**y”).

  • dot(x,y): Evaluates to np.dot(x,y).

  • outer(x,y): Evaluates to np.outer(x,y).

  • tensordot(x,y,axes): Evaluates to np.tensordot(x,y,axes).

  • max_(*args): Evaluates to the maximum of the arguments.

  • min_(*args): Evaluates to the minimum of the arguments.

  • argmax(*args): Evaluates to the index of the maximum of the argments.

  • argmin(*args): Evaluates to the index of the minimum of the argments.

  • cos(x): Evaluates to math.cos(x).

  • sin(x): Evaluates to math.sin(x).

  • tan(x): Evaluates to math.tan(x).

  • sqrt(x): Evaluates to math.sqrt(x).

  • exp(x): Evaluates to math.exp(x).

  • log(x): Evaluates to math.log(x) (base 10).

  • ln(x): Evaluates to math.ln(x) (natural log).

  • sum_(*args): Evaluates to sum(args). If arguments are vectors or matrices, then the result is also a vector or matrix. This is somewhat different behavior from sum(x) if x is a list.

  • weightedsum(v1,...,vn,w1,...,wn): Evaluates to \(w1*v1+...+wn*vn\).

Accessors

  • getitem(vec,index): Evaluates to vec[index]. This also supports slices and tuples, as well as lists (Numpy fancy indexing). (shortcut: “vec[index]”)

  • setitem(vec,index,val): Evaluates to vec except with vec[index] set to val. Equivalent to Python code:

    temp = vec[:]
    vec[index]=val
    return temp
    
  • getattr_(object,attr): returns the value of a given attribute under the given object. For example, getattr_(traj,const("milestones")) gets the milestone list of a user-data Trajectory named traj.

    If the result is a function, it will be called with no arguments. For example, getattr_(robot,const("getJointLimits")) will return the robot’s joint limits.

    (shortcut: “object.attr”, where object is a UserDataExpression)

  • setattr_(object,attr,val): returns a modified version of the given class object, where value is assigned to the attribute attr. For example, setattr_(traj,const("times"),[0,0.5,0.1]) sets the times attribute of a Trajectory to [0,0.5,1].

    Note: this operation modifies the object itself.

    If the attribute attr is a function, it will be called with the argument val. This allows setting operations to be called.

Conditionals

  • if_(cond,trueval,falseval): If cond evaluates to True, this expression evaluates to trueval. Otherwise, it evaluates to falseval.

Arrays

Arrays are mostly interchangable with vectors (numpy 1-D arrays), except they can contain objects of varying type/size. Arrays of varying type/size should only be used as arguments in the flatten or the special looping functions (forall, summation, etc). In many cases the Python construction [e1,e2,...,en] where en is an Expression, will be interpreted correctly as an array Expression. To handle nested lists of Expressions, and to ensure that your lists can be saved and loaded, you may need to use these functions.

  • array(*args): Creates a list or numpy array of the given arguments. This can accept arbitrary arguments, such as variable size vectors. A Numpy array is produced only if the items have compatible types and dimensions.

  • list_(*args): Creates a list of the given arguments. This can accept arbitrary arguments, such as variable size vectors. No attempt is made to convert to a numpy array.

  • tuple_(*args): Creates a tuple of the given arguments. This can accept arbitrary arguments, such as variable size vectors.

  • zip_(collection1,collection1,...): Does the same thing as the Python zip function, returning a list of tuples.

Looping

Special functions are available for temporary variable substitution and emulation of for loops. In each of the following, var can be a variable or userData referenced in the Expression expr:

  • subs(expr,var,value): evaluates expr with var substituted with value. For example, subs(const(2)*"i","i",3.5) yields 2*3.5

  • map_(expr,var,values): like the Python map function, evaluates to a list where each entry evaluates expr with var substituted with a value from the list values. For example, if x is a Variable, then map_(x**"i","i",range_(3)) yields the list [x**0, x**1, x**2]

  • forall(expr,var,values): True if, for every value in the list values, expr evaluates to nonzero when var is substituted with that value. Equivalent to all_(*[subs(expr,var,value) for value in values])

  • forsome(expr,var,values): True if, for some value in the list values, expr evaluates to nonzero when var is substituted with that value. Equivalent to any_(*[subs(expr,var,value) for value in values])

  • summation(expr,var,values): The sum of expr over var when var is substituted with each value in the list values. Equivalent to sum_(*[subs(expr,var,value) for value in values])

Nested iteration can be performed, such as summation(summation(expr("x")**"y","x",[1,2]),"y",[0,1])

Defining your own Functions

If you want to use your own Python functions, you can use Context.declare. By default this will use the same function signature that you used to define the function, or you can provide a new signature (name, arguments). The convention used in the built-in symbolic libraries is to prepend an underscore to the underlying Python function, and then declare the function to the undecorated name.:

def _f(x,y):
    return 2*x*y
ctx = Context()
f = ctx.declare(_f,"f")
print(f(3,4))   #prints f(3,4)
print(f(3,4).evalf())  #prints 24, since 2*3*4=12.

At this point, the module does not know how to take derivatives, so any deriv(f(...),arg) call will return None. To set derivatives, you can use the setDeriv or setJacobian functions:

f.setDeriv("x",(lambda x,y,dx:2*dx*y))
f.setDeriv("y",(lambda x,y,dy:2*x*dy))

OR:

f.setJacobian("x",(lambda x,y:2*y))
f.setJacobian("y",(lambda x,y:2*x))

Alternatively, you can declare an Expression as a Function. Here you need to define the function name and argument list so that unbound variables in the Expression are bound to the arguments.:

f = ctx.declare(const(2)*expr("x")*expr("y"),"f",["x","y"])

This form will automatically obtain derivatives for you.

Functions can also specify argument types and return types, which are used for type checking at Expression creation time. This is quite helpful for debugging, since you do not have to evaluate the Expression to find mismatched types, like indexing a vector with a list. To declare types for custom functions, use the setArgType and setReturnType methods:

#numeric input and output
f.setArgType(0,'N')
f.setArgType(1,'N')
f.setReturnType('N')

Gotchas

Most conversions of arguments to Expressions are done for you during the course of calling operators or calls to symbolic functions, but if you call operators on Python constants, you will invoke the standard Python operation. For example, 5 + 7 produces 12 because 5 and 7 are Python constants, but if you want to build the symbolic Expression that represents “5 + 7”, you will need to use const(5)+const(7) or expr(5)+expr(7).

Since raw strings are interpreted as references to user-data variables, to create string constants, you will need to wrap the string using the const() function, e.g., const("text"). This is mostly relevant for the get/setattr_ functions. Suppose Point is a class with members Point.x and Point.y, the expression that accesses the x member of the user data Point p is built with getattr_("p",const("x")). Using __getattr__ operator overloading you can also use expr("p").x

Python Lists as arguments are handled in somewhat of a tricky way. If the list is compatible with a Numpy array (i.e., each element is numeric or equal-sized arrays), it will be converted to a Numpy array. Otherwise, (e.g., a non- constant Expression is inside or the elements do not have the same size) it will be converted to an array(e1,...,en) Expression. Tuples are NOT converted in the same way, so that Numpy matrix indices can be preserved. Tuples of expressions are not supported implicitly, instead you will have to use tuple_(e1,...,en).

Comparison testing on Expressions, such as e != 0 or e1 == e2, do not return True or False. Instead, they return Expressions themselves! As a result, standard Python if statements cannot be used on Expressions. Instead, you will need to run eval() or evalf() on the result if you want to get its truth value. (eval() does return an Expression, but if the result of eval() is a ConstantExpression then it may be directly tested for its truth value.) To help you remember this, an exception will be raised if you try to use an expression as a condition in a Python if statement.

Note also that e1 == e2 does NOT test for whether the two expressions are logically equivalent. As an example, the expression x1+x2 == x2+x1 does not test equivalence as you might expect, nor would even eval() to true unless x1 and x2 were given. In order to test whether two expressions are syntactically equivalent, you can use e1.match(e2). It is computationally intractable in general to determine whether two expressions are logically equivalent, so we don’t even try.

Derivatives are handled as usual for scalar/vector quantities, giving Jacobian matrices in the form:

[df1/dx1 ... df1/dxn ]
[...                 ]
[dfm/dx1 ... dfm/dxn ].

However, if the function or variable has an “exotic” type, like a matrix, tensor, or nested list, then the Jacobian is taken with respect to the flattened version of the function and the variable. The way this is handled can be seen in some complex derivative expressions, which will have various reshape operations.

Many standard Python operators – +,-,*,/,**, and, or, not, comparison tests, the [] indexing operator, and list construction via [a,b] – are supported directly on Variables and Expressions. Compound slices (matrix[:,:]) have not yet been thoroughly tested. Other standard functions – len, min, max, sum, abs, any, and all – have direct analogues in this package, with a trailing underscore added. If statements and list comprehensions can be emulated via the if_(cond,trueval,falseval) statement and the map_(expr,var,range) statement. There is no support for the in statement, bitwise operators, or procedural code (e.g., statements with side-effects).

If-elif-…-else blocks can be emulated using a multiplexer array(expr1,expr2,...,exprn)[switch] where switch takes on the values 0,…,n-1. This expression is lazy-evaluated so that only the selected expression is expanded. Standard if_ statements are also lazy-evaluated.

Thread-safety: Expression evaluation, derivatives, and simplification are NOT thread safe. Common Expressions used between threads should be deep-copied before use.

Performance and Expression DAGs

If your expression repeatedly performs computationally expensive operations, it is helpful to gather sub-expressions into the same Python objects. As an example, consider a Variable a, and the expression d given by the following construction:

b = (a+a)
c = (b+b)
d = (c+c)

Expanded, d = (c+c) = ((b+b)+(b+b)) = (((a+a)+(a+a))+((a+a)+(a+a)). However, when d.eval({'a':1}) is called, this module is smart enough to evaluate a, b, and c only once. This becomes really helpful in derivative evaluation as well, since the derivative expression will also try to reuse common sub- expressions.

Now what if we want to evaluate several expressions at once? This naive code will evaluate a, b, c, and d 1000 times:

ds = [(d+i).eval({'a':1}) for i in range(1000)]

In order to take advantage of the sub-expression d, we can put everything into a single array() expression:

ds = array(*[(d+i) for i in range(1000)]).eval({'a':1})

which will only evaluate a,b,c, and d once.

IO

There are three forms of Expression IO in symbolic_io:

  1. Standard Python printing: str(expr) (output only)

  2. Human-readable strings: symbolic_io.toStr() (output) / symbolic_io.fromStr() (input)

  3. JSON-like objects: symbolic_io.toJson() (output) / symbolic_io.fromJson() (input)

Method 1 is the most readable when printed to the console, but can’t be read.

Method 2 is somewhat readable, and is compatible with the optimization problem editor. Note that there may be some subtle problems with IO for lists vs numpy arrays, since lists are automatically converted to numpy arrays when possible. It hasn’t been tested extremely thoroughly yet.

Method 3 is the least ambiguous but it uses a nested JSON structure to represent the expression graph. Hence it is rather verbose.

In Methods 2 and 3, if the expression graph is a DAG rather than a tree, a special notation is used to represent repeated references to nodes. In particular, any common ConstantExpression and OperatorExpression objects are “tagged” with a unique id string.

In Method 2, the first time a sub-expression appears it’s tagged with a suffix #id. Then, subsequent references are retrieved with the expression @id. As an example, the string (x+y)#1*@1 evaluates to (x+y)*(x+y), since ‘#1’ assigns (x+y) to the id ‘1’ and ‘@1’ retrieves it.

Code generation

  • context.makePyFunction(expr,varorder=None): creates a Python function that evaluates expr given arguments whose values are to be assigned to each Variable in varorder.

  • context.makeFlatFunction(expr,varorder=None): creates a Python function that evaluates expr given a flattened list or vector of parameters, which can be unraveled to values to be assigned to each Variable in varorder.

  • symbolic_io.latex(expr): produces a LaTex string representing the expression (requires Sympy)

  • symbolic_io.codegen(exprs,language): produces C / Matlab code to evaluate the Function or named expressions (requires Sympy).

Sympy integration

Sympy integration is quite automatic. See symbolic_sympy.exprToSympy() and symbolic_sympy.exprFromSympy() in symbolic_sympy.py. Built-in functions are converted mostly bidirectionally and hence support all aspects of both libraries.

Array operations are converted to Sympy Matrix operations, which expand all entries into scalar expressions, so array operations like dot() cannot be converted to Sympy and then back into the equvalent symbolic operation.

Custom symbolic functions are converted to sympy Function(f) objects, and it should be noted that these do not support code generation.

Special Sympy functions are automatically converted for use in the symbolic module, including differentiation. (Note that they cannot be saved to / loaded from disk, though.)

Wish list

  • Sparse matrix support when obtaining jacobians – especially in block matrix form.

  • Compiled code generation on vectors/matrices – maybe integration with TensorFlow / PyTorch?

Module summary

External interface

expr(x)

Converts x to an appropriate Expression as follows:

Type(type[, size, subtype])

A specification of a variable/expression type.

deriv(e, var)

simplify(e[, context, depth])

supertype(types)

type_of(x)

const(v)

is_const(v[, context, shallow])

Returns True if v is a constant value or constant expression.

to_const(v[, context, shallow])

Returns the value corresponding to a constant value or expression v.

is_scalar(v[, value])

Returns True if v evaluates to a scalar.

to_scalar(v)

Returns the value of v if v is a scalar

is_zero(v)

Returns true if v represents a 0 value

is_var(v)

Returns True if v is equivalent to a stand-alone variable.

to_var(v)

If v is equivalent to a stand-alone variable, returns the Variable.

is_expr(x)

is_op(x[, opname])

is_sparse(v[, threshold])

Returns true if v is a sparse array, with #nonzeros(v) < threshold(shape(v)).

to_monomial(v)

If v is a monomial of a single variable, returns (x,d) where x is the Variable and d is the degree.

to_polynomial(expr[, x, const_only])

If expr is a polynomial of a single variable, returns x,[c1,...,cn],[d1,...,dn] where expr = c1 x^d1 + .

Internal implementation

Type(type[, size, subtype])

A specification of a variable/expression type.

Context()

Base class for all symbolic operations.

Function(name, func[, argNames, returnType])

A symbolic function.

Variable(name, type[, ctx])

Wildcard([name])

Used for matching / finding

Expression()

ConstantExpression(value)

UserDataExpression(name)

VariableExpression(var)

OperatorExpression(finfo, args[, op])

A compound function of individual Expressions

Symbolic standard functions

range_

range(n)

len_

len(x)

count

count(x)

shape

shape(x)

reshape

reshape(x,s)

transpose

transpose(x)

transpose2

transpose2(x,axes)

dims

dims(x)

eye

eye(n)

basis

basis(i,n)

zero

zero(n)

diag

diag(x)

eq

eq(lhs,rhs)

ne

ne(lhs,rhs)

le

le(lhs,rhs)

ge

ge(lhs,rhs)

not_

not(x)

or_

or(x,y)

and_

and(x,y)

neg

neg(x)

abs_

abs(x)

sign

sign(x)

add

add(x,y)

sub

sub(x,y)

mul

mul(...)

div

div(x,y)

pow_

pow(x,y)

dot

dot(x,y)

outer

outer(x,y)

tensordot

tensordot(x,y,axes)

if_

if(cond,trueval,falseval)

max_

max(...)

min_

min(...)

argmax

argmax(...)

argmin

argmin(...)

cos

cos(x)

sin

sin(x)

tan

tan(x)

arccos

arccos(x)

arcsin

arcsin(x)

arctan

arctan(x)

arctan2

arctan2(x)

sqrt

sqrt(x)

exp

exp(x)

log

log(x)

ln

ln(x)

sum_

sum(...)

any_

any(...)

all_

all(...)

getitem

getitem(vec,index)

setitem

setitem(vec,index,val)

getattr_

getattr(object,attr)

setattr_

setattr(object,attr,val)

flatten

flatten(...)

row_stack

row_stack(...)

column_stack

column_stack(...)

array

array(...)

list_

list(...)

tuple_

tuple(...)

zip_

zip(...)

weightedsum

weightedsum(...)

subs

subs(expr,var,value)

map_

map(expr,var,values)

forall

forall(expr,var,values)

forsome

forsome(expr,var,values)

summation

summation(expr,var,values)

class klampt.math.symbolic.Type(type, size=None, subtype=None)[source]

Bases: object

A specification of a variable/expression type.

char

a character defining the type. Valid values are N, I, B, X, A, V, M, L, U, and None.

Type:

str

size

None (no size specified), the length of the array (L and V types), or the Numpy shape (A, M types)

subtype
  • U type: the name of the class, if known.

  • L type: either a single Type defining the list’s element types, or a list of Types defining individual element types (of the same length as size).

Type:

str, Type, or list of Types, optional

is_scalar()[source]
shape(hypershape=True)[source]

Returns the Numpy shape or hypershape of this type. If it’s array- like, the return value is a tuple. Otherwise, it’s a dict.

A ValueError is raised if no size is specified.

len()[source]

Returns the number of first-level sub-items of this type. None is returned if no size is specified.

count()[source]

Returns the number of free parameters in this type. None is returned if no size is specified.

dims()[source]

Returns the number of entries in the shape of this type. For compound list types, this is 1+subtype.dims().

match(obj, strict=False)[source]

Returns true if the given object type matches all aspects of this specification. Integers and booleans match with numeric, vectors and matrices match with array.

obj is a Type, str, or Python object. If obj is a str, it is either a character specifier or a class name, and sizes aren’t checked. If obj is an object this is equivalent to self.match(type_of(obj)).

If obj doesn’t have a size then it matches only strict = False, or if this specification also doesn’t have a size.

itemtype(index=None)[source]

If this is an array or list type, returns the Type of each item accessed by x[i]. Otherwise returns None.

info()[source]

Returns a verbose, human readable string representation of this type

class klampt.math.symbolic.Context[source]

Bases: object

Base class for all symbolic operations. Define variables, expressions, and user data here.

addVar(name, type='V', size=None)[source]

Creates a new Variable with the given name, type, and size.

Valid types include: - V: vector (default) - M: matrix - N: numeric (generic float or integer) - B: boolean - I: integer - A: generic array - X: index

size is a hint for vector variables to help determine how many entries the variable should contain. It can be None, in which case the size is assumed unknown.

addVars(prefix, N, type='N', size=None)[source]

Creates N new Variables with the names prefix+str(i) for i in 1,…,N. type is the same as in addVar, but by default it is numeric.

addUserData(name, value)[source]

Adds a new item to user data. These are un-serializable objects that can be referred to by functions, but must be restored after loading.

addExpr(name, expr)[source]

Declares a named expression

declare(func, fname=None, fargs=None)[source]

Declares a custom function. If fname is provided, this will be how the function will be referenced. Otherwise, the Python name of the function is used.

Parameters:
  • func (function or Expression) – the function / expression object

  • fname (str, optional) – the name of the new Function. If func is an Expression this is mandatory. Otherwise it is taken from the Python function name.

  • fargs (list of str, optional) – the argument names. If func is an Expression this is mandatory.

To convert Expressions into Functions, the fargs list declares the order in which variables in func will be bound to arguments. E.g., Context.declare(2*expr("x")*expr("y"),"twoxy",["x","y"]) produces a two argument function twoxy(x,y): 2*x*y.

include(context, prefix=None, modify=False)[source]

Adds all items inside another context as a sub-context.

If prefix != None, self.[prefix] is set to context, and all names in that context are prepended by the given prefix and a ‘.’.

If modify=True, then all the Variable and Function names inside the given sub-context are modified to fit the self context. This is useful if you are saving/loading expressions and using the convenience form self.[prefix].[functionName] to instantiate Functions embedded inside the sub-context.

Note

When using modify=True, be careful not to re-use the sub-context inside multiple super-contexts.

copy(copyFunctions=False)[source]

Returns a shallow copy of this context.

bind(vars, values)[source]

Assigns multiple variables to values

bindFunction(function, remapping=None)[source]

Produces an Expression that evalutes the function, where its arguments are bound to variables / user data in the current environment. The argument names should map to similarly named variables or user data.

If remapping is provided, then it maps function arguments to variables or values, either as

  • a dictionary mapping a function argument arg to remapping[arg].

  • a list or tuple mapping the i’th function argument to remapping[i].

listVars(doprint=True, indent=0)[source]
listExprs(doprint=True, indent=0)[source]
listFunctions(doprint=True, indent=0, builtins=False)[source]
expr(name)[source]

Retrieves a named expression

function(name)[source]

Retrieves a named function..

get(name, *args)[source]

Retrieves a named reference to a userData or variable.

Can be called as get(name), in which case a KeyError is raised if the key does not exist, or get(name,defaultValue) in which case the default value is returned if the key does not exist.

renameVar(item, newname)[source]

Renames a variable

renameUserData(itemname, newname)[source]

Renames a userData

makePyFunction(expr, varorder=None)[source]

Converts an Expression or Function to a Python function f(x) that takes x as a list of scalar values, maps those to Variable values, and returns the result of evaluating the expression / function.

Parameters:
  • expr (Function or Expression) – the function or expression to evaluate

  • varorder (list, optional) –

    If given, the list of Variables that should appear in the flattened argument list x.

    If this isn’t provided, then the Variables in expr are ordered by the order in which were added to this Context.

Returns:

A pair (f,varorder), where:

  • f(x) is a 1-argument Python function equivalent to expr

    but where x is a list of variable values.

  • varorder gives the order of variables that should be

    sent in x

Return type:

tuple

makeFlatFunction(expr, varorder=None, defaultsize=1)[source]

Given an expression expr, return (f,varorder), where f is an equivalent 1-argument Python function that takes a list of numbers or numpy array. The order of variables that should be provided to f in this tuple is returned in varorder.

If vector variables are not given size hints, then they are assumed to have defaultsize.

See also makePyFunction().

makeFlatFunctionDeriv(expr, varorder=None, defaultsize=1)[source]

Given a differentiable expression expr, return (df,varorder), where df is an 1-argument Python function that that takes a list of numbers or numpy array and outputs the derivative (Jacobian matrix) of expression expr.

The order of variables that should be provided to df in this tuple is returned in varorder.

If expr is not differentiable, then df=None is returned.

If vector variables are not given size hints, then they are assumed to have defaultsize.

See also makeFlatFunction().

getFlatVarRanges(varorder, defaultsize=1)[source]

If varorder is a variable order returned by makeFlatFunction, returns the list of index ranges r=[k0,…,kn] where variable index i corresponds with x[r[i]:r[i+1]]. The x vector must have size r[-1].

getFlatVector(varorder)[source]

If varorder is a variable order returned by makeFlatFunction, returns the x vector corresponding to the current variable values.

class klampt.math.symbolic.Function(name, func, argNames=None, returnType=None)[source]

Bases: object

A symbolic function. Contains optional specifications of argument and return types, as well as derivatives.

Parameters:
  • name (str) –

  • func (Python function or Expression) –

  • argNames (list of str, optional) –

  • returnType (str or function, optional) – sets self.returnType or self.returnTypeFunc.

If func is a Python function, the argument list will be derived from the declaration of the Python function. Otherwise, it must be an Expression, and argNames needs to be provided. The expression must be closed, meaning that all unspecified variables in the expression must be named in argNames.

Examples

Basic instantiation:

#standard function method
def f(x,y):
    ...
symfunc = Function("f",f)
a = context.addVar("a")
b = context.addVar("b")
c = context.addVar("c")
print(symfunc(a,b))   # prints f($a,$b)

Creating a Function from an Expression (sort of like a lambda function):

expr = a + 2*b
symfunc2 = Function("f2", expr, ["a","b"])  #when symfunc2 is called, its first argument will be bound to a, and its second will be bound to b
print(symfunc2(c,4))   #prints f2(c,4)
print(symfunc2(c,4).eval({'c':5}))  #prints 13, because c + 2*4 = 5 + 2*4 = 13

If func is an Expression, then you can automatically set derivatives if all the sub-expressions have derivatives. To do so, use the autoSetJacobians() method.

Defining derivatives:

There are three ways to define derivatives: setDeriv(), setJacobian(), and autoSetJacobians() (available if func is an expression. In setDeriv() you provide Jacobian-vector products. In setJacobian() you provide Jacobian matrices (or tensors).

name

name of function used in printing and IO.

Type:

str

description

text description of function.

Type:

str, optional

func

the function to be evaluated.

Type:

Python function or Expression

argNames

names of arguments.

Type:

list of strs, optional

argTypes

list giving each argument’s Type.

Type:

list of Type, optional

argDescriptions

strings describing each argument

Type:

list of strs, optional

returnType

return Type

Type:

Type, optional

returnTypeFunc

a function that takes argument types and produces a more specific return Type than returnType

Type:

function, optional

returnTypeDescription

description of the return type

Type:

str, optional

deriv

Can be either:

  1. a list of Jacobian-vector products with respect to a derivative of each argument: [df1(*args,darg1),...,dfn(*args,dargn)]. Here, dargi is an argument of the same shape as args[i], giving the derivative \(\frac{dargs[i]}{dx}\) w.r.t. some scalar parameter x. The function dfi gives \(\frac{df}{dargs[i]}\cdot\frac{dargs[i]}{dx}\) where \(\frac{df}{dargs[i]}\) is the partial of self with respect to the i’th argument.

  2. a function df(args,dargs) giving the total derivative of f given directional derivatives of each of the argmuments. Here, dargs is a list of derivatives [darg1,...,dargn]. The result should be equal to \(\frac{df}{dx} = \frac{df}{dargs[0]}\cdot \frac{dargs[0]}{dx} + ... + \frac{df}{dargs[n-1]}\cdot\frac{dargs[n-1]}{dx}\).

The return value should have the same shape as self.returnType()

Type:

optional

colstackderiv

same as deriv, except that each function accepts stacked argument derivatives. This can be more efficient than filling in deriv, since taking derivatives w.r.t. k variables x1,…,xk can be done by setting up dargi as a matrix [dflatten(argi)/dx1 | ... | dflatten(argi)/dxk], i.e.,. the column stacking of each of the flattened argument derivatives. dargi has shape (count(argi),k).

Type:

optional

rowstackderiv

same as colstackderiv, except that row- wise stacked argument derivatives are accepted. In other words, dargi is a matrix \([\frac{d\text{flatten(args[i])}}{dx1}, ... , \frac{d\text{flatten(args[i])}}{dxk}]\) which has shape (k,count(argi)). This is more efficient than colstackderiv but corresponds less directly to standard mathematical notation.

Type:

optional

jacobian

list of Jacobian functions with respect to each argument. Has the form [Jf1(*args),...,Jfn(*args)] where Jfi returns a matrix of shape (count(self),count(args[i])). The total derivative with respect to some variable x is \(\frac{df}{dx} =\) reshape(dot(Jf1,flatten(darg1)) + dot(Jfn,flatten(dargn)),shape(self))

Type:

list of functions, optional

presimplifier
Type:

function, optional

simplifier
Type:

function, optional

presimplifierDict

a nested dict, mapping argument signatures to simplification functions. See addSimplifier() for more details.

Type:

dict, optional

simplifierDict

a nested dict, mapping argument signatures to simplification functions. See addSimplifier() for more details.

Type:

dict, optional

properties

possible properties of this function, such as associativity, etc.

Type:

dict

printers

a dict containing code generation methods. Each entry, if present, is a function printer(expr,argstrs) that returns a string. Common key values are:

  • “str”: for printing to console

  • “parse”: for parseCompatible=True exprToStr

Type:

dict of functions

optimized(*args)[source]

Similar to self(*args).simplify(depth=1), but optimized to reduce complexity earlier. In particular, this directly applies the operation if all arguments are constants, and it will try to apply the simplifier immediately.

checkArg(arg)[source]

Verifies that a named or indexed argument is valid, and normalizes it. Returns an (index,name) tuple

setDeriv(arg, dfunc, asExpr=False, stackable=False)[source]

Declares a (partial) derivative of the function with respect to argument arg. The function dfunc is a function that takes arguments (arg1,...,argn,dx), where dx is the derivative of arg with respect to some variable x, and returns \(df/darg(arg1,...,argn) * darg/dx\).

For vector-valued arguments and functions, the * is a matrix-vector product, and dfunc is required to produce what’s commonly known as the Jacobian-vector product.

For matrix-valued arguments or functions, the * is a tensor product.

Parameters:
  • arg (int or str) – either an argument index or name.

  • dfunc

    either:

    1. an Expression of variables arg1,…,argn,darg,

    2. a Function of n+1 variables mapping to arg1,...,argn,dx,

    3. a Python function of n+1 variables (arg1,...,argn,dx)

      that either returns a value (setting asExpr=False) or an Expression of the derivative (if asExpr=True).

    4. None, to indicate that the derivative is not defined,

    5. 0, to indicate that the derivative is identically 0.

    In the normal case (stackable=False), the shape of dx is the same as arg, and the result has the same shape as self.

  • asExpr (bool, optional) – if dfunc is a Python function, this flag says that it will return an Expression. Using an Expression allows taking multiple derivatives.

  • stackable (bool or str, optional) –

    states whether dfunc can accept stacked derivative arguments. If True or 'col', then it can accept dvar as a column-stacked array of derivatives. In other words, dvar[...,0] is the derivative w.r.t. x0, dvar[...,1] is the derivative w.r.t. x1, etc.

    E.g., for vector arguments, dvar can be thought of as a matrix with k=``dvar.shape[-1]`` derivatives in its columns.

    In this case, dfunc should return an array of shape shape(self) x k (i.e., f’s derivatives are stacked in columns.)

    stackable can also be 'row' in which case dfunc accepts dvar as a row-stacked array of derivatives. I.e, dvar[0,...] is the derivative w.r.t. x0, dvar[1,...] is the derivative w.r.t., x1, etc. In this case, dfunc should return a list of derivatives.

    If the argument is not an array or its type cannot be determined (see setArgType) then dvar will be passed as a count(arg) x k matrix (for column-stacking) or a k x count(arg) matrix (for row stacking).

    If the function does not return an array or the return type cannot be determined (see setReturnType), the shape of the returned matrix must be count(self) x k (for column- stacking) or k x count(self) (for row-stacking).

setJacobian(arg, dfunc, asExpr=False)[source]

Declares a (partial) derivative of the function with respect to argument arg.

This only makes sense when the arguments are all arrays or scalars; no complex types are supported.

Parameters:
  • arg (int or str) – same as setDeriv.

  • dfunc – similar to setDeriv, but a function that takes n arguments (arg1,…,argn) and returns the matrix \(df/darg(arg1,...,argn)\).

The return value of dfunc must have shape shape(self) + shape(arg), so that:

  • If self and arg are scalars, the Jacobian is also a scalar

  • If self and arg are a scalar and a vector (or vice versa) the Jacobian is a vector.

  • If self and arg contain a scalar and an N-D array, the Jacobian is an N-D array.

  • If self and arg are both vectors, the Jacobian is a matrix.

autoSetJacobians(args=None)[source]

For Expression-based functions, can automatically set the Jacobians. If args is not None, only the arguments in args are set.

setReturnType(type)[source]

Sets a return type specifier.

Parameters:

type (Type, str, or function) – if a Type object, the return type is set directly. If a character type specifier, it is cast to Type. If it is a function, it is assumed to be a function that that takes in argument Types and returns a Type.

setArgType(arg, type)[source]

Sets an argument type specifier.

Parameters:
  • arg (int or str) – an index or string naming an argument.

  • type (Type or str) – a Type object or character type specifier for the specified argument.

getArgType(arg)[source]

Retrieves an argument type.

Parameters:

arg (int or str) – the index or string naming an argument.

addSimplifier(signatures, func, pre=False)[source]

For a signature tuple, sets the simplifier to func.

Parameters:
  • signatures (list) –

    a list of argument signatures. A signature can be:

    • the operation name for an OperatorExpression, passing the arg directly to func(…)

    • ’_scalar’: matches to a constant scalar, and passes that constant to func(…)

    • ’_const’: matches to constant, and passes that constant to func(...)

    • ’_returnType’: passes the argument’s returnType() to func(...)

    • None: match all. The arg is passed directly to func(…)

  • func (callable) – a callable that takes a list of function arguments, possibly transformed by _const or _returnType. This returns a simplified Expression or None.

  • pre (bool, optional) – if True, the simplifier is called before arguments are simplified.

If multiple matches are present then they are tested in order

  1. operation name

  2. _const

  3. _returnType

  4. None

simplify(args, pre=False)[source]

Performs simplification of OperatorExpression(self,args), either with the simplifier function or the simplifierDict.

info()[source]

Returns an text string describing the Function, similar to a docstring

class klampt.math.symbolic.Variable(name, type, ctx=None)[source]

Bases: object

isAssigned()[source]
bind(value)[source]
unbind()[source]
context()[source]
class klampt.math.symbolic.Expression[source]

Bases: object

returnType()[source]

Returns the output type of this expression.

depth(cache=True)[source]

Returns the depth of the deepest leaf of this expression tree (i.e., its height). If cache = True, depths are cached as attribute ‘depth’ to speed up subsequent depth() calls.

isConstant()[source]

Returns true if this expression is identically a constant. If so, then the expression can be safely replaced with its evalf() value without any possible change in meaning.

returnConstant(context=None)[source]

Returns true if the evaluation of the expression in the given context results in a constant. If so, then evalf() can safely be applied without error.

eval(context=None)[source]

Evaluates the expression while substituting all constant values. The result can be a constant or an Expression if the result is not constant.

Parameters:

context (Context or dict, optional) – a map of variable names to constant values.

evalf(context=None)[source]

Evaluates the expression while substituting all constant values, enforcing that the result is a constant. If the result is not constant, a ValueError is returned.

deriv(var, context=None)[source]

Returns a new expression where all bound variables and variables in context are reduced to constant values. If all arguments are constant, returns a constant.

Parameters:
  • var – either a Variable, a string naming a Variable, or a dictionary mapping variable names to derivatives. For example, in the latter case, if this is an expression f(x,y),and var is a dictionary {“x”:dx,”y”:dy}, then the result is df/dx(x,y)dx + df/dy(x,y)dy

  • context (Context or dict, optional) – a map of variable names to constant values.

vars(context=None, bound=False)[source]

Returns a list of all free Variables under this expression.

If bound is True, any Variables currently bound to values are returned, otherwise they are not.

match(val)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

find(val)[source]

Returns self if self contains the given expression as a sub-expression, or None otherwise.

replace(val, valReplace, error_no_match=False)[source]

Returns a copy of this expression where any sub-expression matching val gets replaced by valReplace. If val doesn’t exist as a subexpression, self is returned.

simplify(context=None, depth=None)[source]

Returns a simplified version of self, or self if it cannot be simplified.

Parameters:
  • context (dict or Context) – any Variable / UserData whose name appears in here will be substituted with its value.

  • depth (int, optional) – None for full-depth simplification, or the max depth to explore.

Returns:

Return type:

Expression

class klampt.math.symbolic.Wildcard(name='*')[source]

Bases: Expression

Used for matching / finding

match(val)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

class klampt.math.symbolic.ConstantExpression(value)[source]

Bases: Expression

returnType()[source]

Returns the output type of this expression.

isConstant()[source]

Returns true if this expression is identically a constant. If so, then the expression can be safely replaced with its evalf() value without any possible change in meaning.

returnConstant(context=None)[source]

Returns true if the evaluation of the expression in the given context results in a constant. If so, then evalf() can safely be applied without error.

match(val)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

class klampt.math.symbolic.UserDataExpression(name)[source]

Bases: Expression

returnType()[source]

Returns the output type of this expression.

isConstant()[source]

Returns true if this expression is identically a constant. If so, then the expression can be safely replaced with its evalf() value without any possible change in meaning.

returnConstant(context=None)[source]

Returns true if the evaluation of the expression in the given context results in a constant. If so, then evalf() can safely be applied without error.

match(val)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

vars(context=None, bound=False)[source]

Returns a list of all free Variables under this expression.

If bound is True, any Variables currently bound to values are returned, otherwise they are not.

class klampt.math.symbolic.VariableExpression(var)[source]

Bases: Expression

returnType()[source]

Returns the output type of this expression.

isConstant()[source]

Returns true if this expression is identically a constant. If so, then the expression can be safely replaced with its evalf() value without any possible change in meaning.

returnConstant(context=None)[source]

Returns true if the evaluation of the expression in the given context results in a constant. If so, then evalf() can safely be applied without error.

match(val)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

vars(context=None, bound=False)[source]

Returns a list of all free Variables under this expression.

If bound is True, any Variables currently bound to values are returned, otherwise they are not.

class klampt.math.symbolic.OperatorExpression(finfo, args, op=None)[source]

Bases: Expression

A compound function of individual Expressions

returnType()[source]

Returns the output type of this expression.

isConstant()[source]

Returns true if this expression is identically a constant. If so, then the expression can be safely replaced with its evalf() value without any possible change in meaning.

returnConstant(context=None)[source]

Returns true if the evaluation of the expression in the given context results in a constant. If so, then evalf() can safely be applied without error.

eval(context=None)[source]

Returns a new expression where all bound variables and variables in context are reduced to constant values. If all arguments are constant, returns a constant expression.

deriv(var, context=None)[source]

Returns an expression for the derivative dself/dvar, where all bound variables and variables in context are reduced to constant values.

Normally, var is a Variable or str.

The derivative/Jacobian size is determined as follows:

  • If both var and self are scalar, the result is a scalar.

  • If self is scalar and var is non-scalar, the result has the same shape as var.

  • If self is non-scalar and var is scalar, the result the same shape as self.

  • If both self and var are arrays, the result is an n-D array with shape shape(self)+shape(var).

  • If other self or var are compound (i.e., unstructured lists), the result is an m x n matrix, where m = count(self) and n = count(var).

If no derivative can be determined, this returns None.

If the expression evaluates to a constant, the result will be either 0 or a constant expression.

Another option is for var to be a dict of variable names to their derivatives.

vars(context=None, bound=False)[source]

Returns a list of all free Variables under this expression.

If bound is True, any Variables currently bound to values are returned, otherwise they are not.

match(expr)[source]

Returns true if self is equivalent to the given expression. Note: no rearrangement is attempted, so x+y does not match y+x.

find(term)[source]

Returns self if self contains the given expression as a sub-expression, or None otherwise.

replace(term, termReplace, error_no_match=True)[source]

Returns a copy of this expression where any sub-expression matching val gets replaced by valReplace. If val doesn’t exist as a subexpression, self is returned.

klampt.math.symbolic.supertype(types)[source]
klampt.math.symbolic.type_of(x)[source]
klampt.math.symbolic.const(v)[source]
klampt.math.symbolic.is_const(v, context=None, shallow=False)[source]

Returns True if v is a constant value or constant expression.

If context is not None, then Variables and user data that are bound to values are considered to be constants. Otherwise, they are not considered to be constants.

If shallow=True, this doesn’t try to convert Expressions.

klampt.math.symbolic.to_const(v, context=None, shallow=False)[source]

Returns the value corresponding to a constant value or expression v. Otherwise returns None.

If context is not None, then Variables and user data that are bound to values are considered to be constants. Otherwise, they are not considered to be constants.

If shallow=True, this doesn’t try to convert Expressions.

klampt.math.symbolic.is_scalar(v, value=None)[source]

Returns True if v evaluates to a scalar. If value is provided, then returns True only if v evaluates to be equal to value

klampt.math.symbolic.to_scalar(v)[source]

Returns the value of v if v is a scalar

klampt.math.symbolic.is_zero(v)[source]

Returns true if v represents a 0 value

klampt.math.symbolic.is_var(v)[source]

Returns True if v is equivalent to a stand-alone variable.

klampt.math.symbolic.to_var(v)[source]

If v is equivalent to a stand-alone variable, returns the Variable. Otherwise returns None.

klampt.math.symbolic.is_sparse(v, threshold='auto')[source]

Returns true if v is a sparse array, with #nonzeros(v) < threshold(shape(v)).

Parameters:
  • v (Expression) – the array

  • threshold (optional) – either ‘auto’, a constant, or a function of a shape. If threshold=auto, the threshold is sqrt(product(shape))

klampt.math.symbolic.expr(x)[source]

Converts x to an appropriate Expression as follows:

  • If x is a Variable, returns a VariableExpression.

  • If x is a non-str constant, returns a ConstantExpression.

  • If x is a str, then a UserDataExpression is returned.

  • If x is already an Expression, x is returned.

  • If x is a list containing expressions, then array(*x) is returned.

    This adds some ambiguity to lists containing strings, like expr(['x','y']). In this case, strings are recursively converted to UserDataExpressions.

klampt.math.symbolic.is_expr(x)[source]
klampt.math.symbolic.is_op(x, opname=None)[source]
klampt.math.symbolic.deriv(e, var)[source]
klampt.math.symbolic.simplify(e, context=None, depth=None)[source]
klampt.math.symbolic.to_monomial(v)[source]

If v is a monomial of a single variable, returns (x,d) where x is the Variable and d is the degree. Otherwise returns None.

klampt.math.symbolic.to_polynomial(expr, x=None, const_only=True)[source]

If expr is a polynomial of a single variable, returns x,[c1,…,cn],[d1,…,dn] where expr = c1 x^d1 + … + cn x^dn. Otherwise returns None.

If x is given, the polynomial must be a polynomial in x. If const_only=True, the coefficients and degrees must be constants,

klampt.math.symbolic.dotshape(x, y)[source]

A symbolic expression producing the shape of the dot product of expressions x and y

klampt.math.symbolic.dottype(x, y)[source]
klampt.math.symbolic.dot_setitem_simplifier1(x, y)[source]
klampt.math.symbolic.dot_setitem_simplifier2(x, y)[source]

klampt.math.symbolic_io module

I/O from symbolic expressions.

Functions:

byteify(input)

indent(s, spaces)

exprToStr(expr[, parseCompatible, ...])

Converts an Expression to a printable or parseable string.

exprFromStr(context, string[, fmt, add])

Returns an Expression from a string.

exprToJson(expr)

exprFromJson(context, jsonObj[, ...])

Creates an Expression from a JSON object previously saved by expr.toJson()

typeToJson(type)

typeFromJson(jsonObj)

contextToJson(ctx[, saveFunctions])

Produces a JSON object from a context.

contextFromJson(context, jsonObj)

Creates a context from a JSON object previously saved by context.toJson().

toStr(obj[, parseCompatible])

toJson(obj)

latex(expr)

Returns LaTeX code for the Expression expr.

pprint(expr)

Pretty-prints the Expression expr.

codegen(name_expr[, language])

Similar to sympy.codegen.

klampt.math.symbolic_io.byteify(input)[source]
klampt.math.symbolic_io.indent(s, spaces)[source]
klampt.math.symbolic_io.exprToStr(expr, parseCompatible=True, expandSubexprs='auto')[source]

Converts an Expression to a printable or parseable string.

Parameters:
  • expr (Expression) – the Expression to convert

  • parseCompatible (bool, optional) – if True, the result is readable via exprFromStr()

  • expandSubexprs (str or bool, optional) –

    whether to expand subexpressions. Can be:

    • ’auto’: if parseCompatible, equivalent to False. if parseCompatible=False, equivalent to True.

    • True: expands all common subexpressions

    • False: does not expand common subexpressions.

    • ’show’: Internally used.

Returns:

a printable or parsable string representing expr.

Return type:

(str)

klampt.math.symbolic_io.exprFromStr(context, string, fmt=None, add=False)[source]

Returns an Expression from a string. In auto mode, this reads in constants in klampt.loader JSON- compatible format, standard variables in the form “x”, user data in the form of strings prepended with $ (e.g., “$x”), and named expression references in the form of strings prepended with @.

Parameters:
  • context (Context) – the context containing possible functions in string

  • string (str) – the string to parse.

  • fmt (str, optional) – specifies a format for the string. Can be None (auto), ‘auto’, or ‘json’

  • add (bool, optional) – if true, adds all variables referenced in the string to the context. Otherwise, undefined variables are referred to as user data.

An exception is raised on parsing failure.

(Parsing is a little slow, so try not to use it in tight inner loops)

Returns:

the expression represented by str.

Return type:

(Expression)

klampt.math.symbolic_io.exprToJson(expr)[source]
klampt.math.symbolic_io.exprFromJson(context, jsonObj, taggedExpressions=None)[source]

Creates an Expression from a JSON object previously saved by expr.toJson()

klampt.math.symbolic_io.typeToJson(type)[source]
klampt.math.symbolic_io.typeFromJson(jsonObj)[source]
klampt.math.symbolic_io.contextToJson(ctx, saveFunctions=False)[source]

Produces a JSON object from a context. Only the names for userData and customFunctions are saved. If saveFunctions=False, customFunctions are not saved

klampt.math.symbolic_io.contextFromJson(context, jsonObj)[source]

Creates a context from a JSON object previously saved by context.toJson(). userData is not restored and customFunctions are not restored, but rather, userData and customFunctions are assumed to have been set up with exactly the same keys as when toJson was called.

Modifies context in-place.

klampt.math.symbolic_io.toStr(obj, parseCompatible=True)[source]
klampt.math.symbolic_io.toJson(obj)[source]
klampt.math.symbolic_io.latex(expr)[source]

Returns LaTeX code for the Expression expr. Requires Sympy.

klampt.math.symbolic_io.pprint(expr)[source]

Pretty-prints the Expression expr. If Sympy is installed it will use the sympy pretty-printer.

klampt.math.symbolic_io.codegen(name_expr, language=None, **options)[source]

Similar to sympy.codegen. Generates one or more expressions in the target language. Requires Sympy.

Parameters:
  • name_expr

    multiple interpretations:

    • A single (name, Expression) tuple: generates code for one function

    • A list of (name, Expression) tuples: generates code for multiple functions

    • one or more Function objects: generates code for one or more functions, as long as the Function is defined via symbolic Expressions rather than Python functions.

    • A list of (Variable == Expression) expressions. Generates a function with multiple return values

  • language (str, optional) – any language accepted by Sympy.

  • options – other options for sympy.codegen.

See the Sympy codegen documentation for more information.

klampt.math.symbolic_klampt module

Defines many functions of Klampt as symbolic Functions.

Currently implemented:

  • so3, se3

  • ik

  • some collide functions

  • RobotModel kinematics

TODO:

  • RobotModel dynamics

  • Trajectories

  • Geometries

  • support polygons

Classes:

SO3Context()

Defines some functions in the so3 module:

SE3Context()

Defines some functions in the se3 module under the se3 namespace

IKContext()

Performs operations on IKObjective user data objects.

GeometryContext()

Performs operations on Geometry3D user data objects.

CollideContext()

Defines the functions --

KlamptContext([world])

Includes all Klampt-related functions.

class klampt.math.symbolic_klampt.SO3Context[source]

Bases: Context

Defines some functions in the so3 module:

  • identity, matrix, inv, mul, apply, rotation, error, distance

  • from_matrix, from_rpy, rpy, from_quaternion, quaternion, from_rotation_vector, rotation_vector

  • eq_constraint: equality constraint necessary for SO3 variables

  • quaternion_constraint: equality constraint necessary for quaternion variables

Completeness table

Function

Derivative

Simplification

identity

N/A

N/A

matrix

Y

Y

inv

Y

Y

mul

apply

Y,Y

rotation

N,Y

from_matrix

Y

from_rpy

rpy

from_quaternion

Y

quaternion

from_rotation_v

rotation_vector

axis

angle

Y

error

distance

eq_constraint

Y

quaternion_cons

Y

class klampt.math.symbolic_klampt.SE3Context[source]

Bases: Context

Defines some functions in the se3 module under the se3 namespace

  • make(R,t): makes an SE3 element from a rotation and a translation (equivalent to list(R,t))

  • identity, homogeneous, matrix (alias for homogeneous), inv, mul, apply

  • from_homogeneous: converts from a 4x4 matrix

  • rotation(T): retrieves the rotation corresponding to T

  • translation(T): retrieves the translation corresponding to T

Completeness table

Function

Derivative

Simplification

make

Y

N/A

identity

Y

homogeneous

Y

matrix

Y

from_homogeneou

Y

inv

Y

mul

apply

N,Y

rotation

Y

Y

translation

Y

Y

class klampt.math.symbolic_klampt.IKContext[source]

Bases: Context

Performs operations on IKObjective user data objects.

Defines the functions

  • link(ikobj): returns the link index of an IKObjective

  • robot(ikobj): returns the RobotModel of an IKObjective

  • targetPos(ikobj): returns the target position of an IKObjective

  • targetRot(ikobj): returns the target rotation of an IKObjective

  • targetXform(ikobj): returns the target transform of an IKObjective

  • localPos(ikobj): returns the local position of an IKObjective

  • worldPos(ikgoal,robot): returns the world position of the IKObjective at the robot’s current configuration (same as klampt.worldPos(robot,link(ikobj),localPos(ikobj)))

  • worldRot(ikgoal,robot): returns the world rotation of the IKObjective at the robot’s current configuration (same as klampt.worldRot(robot,link(ikobj)))

  • residual(ikgoal,robot): returns the combined position and orientation residual of an IKObjective at the robot’s current configuration

Completeness table

Function

Derivative

Simplification

link

N/A

N/A

robot

N/A

N/A

targetPos

N/A

N/A

targetRot

N/A

N/A

targetXform

N/A

N/A

localPos

N/A

N/A

worldPos

N/A,Y(1)

N/A

worldRot

N/A,Y(1)

N/A

residual

N/A,Y(1)

N/A

Y(1): yes, for the first derivative

class klampt.math.symbolic_klampt.GeometryContext[source]

Bases: Context

Performs operations on Geometry3D user data objects. Derivatives of geom arguments are taken with respect to the geometry transforms.

Defines the functions:

  • geometry(object): calls the function object.geometry() (e.g., object can be a RobotModelLink)

  • setTransform(geom,T): sets the current transform of the geometry and returns it.

  • setCollisionMargin(geom,margin): sets the current collision of the geometry and returns it.

  • bbox(geom): returns the bounding box of the geometry at its current transform.

  • collision(geom1,geom2): returns True if the geometries are colliding.

  • distance(geom1,geom2): returns the distance between the geometries, and if penetrating and the two geometries support signed distance, returns the negative penetation distance.

  • closestPoints(geom1,geom2): returns the pair of closest points between geom1 and geom2.

  • distancePoint(geom,pt): returns the closest point from geom to pt.

  • closestPoint(geom,pt): returns the closest point to pt on geom.

  • rayCast(geom,src,dir): returns the distance t>=0 along the ray src + t*dir that intersects geom, or inf if there is no intersection.

NOT IMPLEMENTED YET

Completeness table

Function

Derivative

Simplification

setTransform

N/A

N/A

setCollisionMar

N/A

N/A

bbox

N/A

collision

Y

N/A

distance

N/A

closestPoints

N/A

distancePoint

N/A

closestPoint

N/A

rayCast

N/A

class klampt.math.symbolic_klampt.CollideContext[source]

Bases: Context

Defines the functions –

  • robotSelfCollision(q,robot): returns True if the robot has a collision at q

  • robotCollision(q,context): returns True if the robot has a collision in the world. Saves a collider into the context

  • robotSelfCollisionFree(q,robot): the opposite of robotSelfCollision

  • robotCollisionFree(q,context): the opposite of robotCollision

class klampt.math.symbolic_klampt.KlamptContext(world=None)[source]

Bases: Context

Includes all Klampt-related functions.

Namespaces:

  • so3: SO3Context

  • se3: SE3Context

  • ik: IKContext

  • collide: CollideContext

  • [main]: functions to perform kinematics operations on WorldModel, RobotModel, and RobotModelLink user data objects.

The main namespace defines the functions:

  • robot(world,index): returns the index’th robot in the world

  • rigidObject(world,index): returns the index’th robot in the world

  • terrain(world,index): returns the index’th terrain in the world

  • config(robot): returns the current configuration of the robot or object

  • setConfig(robot,q): sets the current configuration of the robot and returns the robot

  • link(robot,index): returns the index’th link of the robot. (If you want to use a named string as the index, use const(name))

  • transform(object): gets the current transform of the object

  • setTransform(object,T): sets the current transform of the object and returns the object

  • velocity(robot): returns the current velocity of the robot

  • setVelocity(robot,dq): sets the current velocity of the robot and returns the robot

  • worldPos(robot,link,localPos): returns the world position of the point on the given link in the robot’s current configuration.

  • localPos(robot,link,worldPos): returns the local position of the point on the given link in the robot’s current configuration.

  • worldRot(robot,link): returns the rotation matrix of the given link in the robot’s current configuration.

  • com(robot): returns the center of mass of robot at its current configuration

  • gravityTorque(gravity,robot): returns the generalized gravity vector

  • inJointLimits(q,robot): returns True if q is in the robot’s joint limits

  • getJson(object,path): returns the value of a given path under the given object’s json representation. For example, getJson(ikobjective,const(“endPosition[0]”)) retrieves the x coordinate of the target position of an IKObjective.

  • setJson(object,path,val): returns a modified copy of the given object, where value is assigned to the given path under the objects json representation. For example, setJson(ikobjective,const(“endPosition[0]”),p) sets the x coordinate of the target position of an IKObjective to p. Note: this operation returns a copy of the object, modified.

Also includes modules linalg, so3, se3, ik, and collide

Function

Derivative

Simplification

robot

N/A

N/A

rigidObject

N/A

N/A

terrain

N/A

N/A

config

Y

N/A

setConfig

Y

N/A

link

N/A

N/A

transform

N/A

setTransform

N/A

velocity

N/A

setVelocity

N/A

worldPos

Y(1)

N/A

localPos

N/A

worldRot

N/A

com

Y(1)

N/A

gravityTorque

N/A

inJointLimits

N/A

N/A

getJson

N/A

N/A

setJson

N/A

N/A

Y(1): yes, for the first derivative

klampt.math.symbolic_linalg module

Defines symbolic linear algebra functions.

Function list:

  • norm(x): returns the L-2 norm of a vector

  • norm2(x): returns the squared L-2 norm of a vector

  • norm_L1(x): returns the L-1 norm of a vector

  • norm_Linf(x): returns the L-infinity norm of a vector

  • norm_fro(A): returns the Frobeneus norm of a matrix

  • distance(x,y): returns the L-2 distance between two vectors

  • distance2(x,y): returns the squared L-2 distance between two vectors

  • distance_L1(x,y): returns the L-1 distance between two vectors

  • distance_Linf(x,y): returns the L-infinity distance between two vectors

  • mahalanobis_distance(x,y,A): returns the mahalanobis distance between two vectors weighted by A, i.e. \(\sqrt((x-y)^T A (x-y))\)

  • mahalanobis_distance2(x,y,A): returns the squared mahalanobis distance between two vectors weighted by A

  • unit(x): returns the unit vector in the direction x

  • inv(A): returns the inverse of a matrix

  • pinv(A): returns the pseudoinverse of a matrix

  • linear(x,A): the linear function \(A x\)

  • quadratic(x,A): the quadratic function \(x^T A x\)

  • bilinear(x,y,A): the bilinear function \(x^T A y\)

  • bound_contains(xmin,xmax,x): returns True if xmin <= x <= xmax element-wise

  • bound_margin(xmin,xmax,x): returns the distance from x to boundaries of the bounding box [xmin,xmax], if inside (positive is inside)

  • bound_overlaps(xmin,xmax,ymin,ymax): returns True if xmin <= x <= xmax element-wise

Also defines a context LinAlgContext that can be included into a Context under “linalg”

Completeness table

Function

Derivative

Simplification

norm

Y

norm2

Y

norm_L1

Y

norm_Linf

Y

norm_fro

Y

distance

Y

distance2

Y

distance_L1

Y

distance_Linf

Y

mahalanobis_dis

Y,Y,N

mahalanobis…2

Y,Y,N

unit

Y

inv

Y

pinv

Y

linear

Y

quadratic

Y,Y,N

bilinear

Y,Y,N

bound_contains

N/A

bound_margin

bound_overlaps

N/A

Module contents

norm

norm(x)

norm2

norm2(x)

norm_L1

norm_L1(x)

norm_Linf

norm_Linf(x)

norm_fro

norm_fro(A)

distance

distance(x,y)

distance2

distance2(x,y)

distance_L1

distance_L1(x,y)

distance_Linf

distance_Linf(x,y)

mahalanobis_distance

mahalanobis_distance(x,y,A)

mahalanobis_distance2

mahalanobis_distance2(x,y,A)

unit

unit(x)

inv

inv(A)

pinv

pinv(A)

linear

linear(x,A)

quadratic

quadratic(x,A)

bilinear

bilinear(x,A,y)

bound_contains

bound_contains(xmin,xmax,x)

bound_margin

bound_margin(xmin,xmax,x)

bound_overlaps

bound_overlaps(xmin,xmax,ymin,ymax)

LinAlgContext()

class klampt.math.symbolic_linalg.LinAlgContext[source]

Bases: Context

klampt.math.symbolic_sympy module

Functions:

exprToSympy(expr)

exprFromSympy(context, sexpr[, addFuncs])

Converts a Sympy expression to a symbolic.py expression.

Classes:

SympyFunction(name, expr[, symbol_order])

Defines a Function from a Sympy expression.

SympyFunctionAdaptor(name, func[, argnames])

Defines a Function from a Sympy function.

klampt.math.symbolic_sympy.exprToSympy(expr)[source]
class klampt.math.symbolic_sympy.SympyFunction(name, expr, symbol_order=None)[source]

Bases: Function

Defines a Function from a Sympy expression.

Example:

x,y = sympy.symbols("x y")
twoxy = SympyFunction("twoxy",2*x*y)
  • name: the symbolic module name of the function.

  • expr: the Sympy function

  • symbol_order: if you don’t want to use lexicographical order for the unbound variables in expr, this will contain the desired argument order.

class klampt.math.symbolic_sympy.SympyFunctionAdaptor(name, func, argnames=None)[source]

Bases: Function

Defines a Function from a Sympy function.

Example:

heaviside = SympyFunctionAdaptor("heaviside",sympy.Heaviside,["x"])
Parameters:
  • name (str) – the symbolic module name of the function.

  • func (sympy.Function) – the Sympy function

  • argnames (list of strs, optional) – provided if you don’t want to use ‘x’,’y’, ‘z’ for the argument names.

klampt.math.symbolic_sympy.exprFromSympy(context, sexpr, addFuncs=True)[source]

Converts a Sympy expression to a symbolic.py expression.

Parameters:
  • context (Context) – a Context object that captures variable references and custom functions. This may be None.

  • sexpr (sympy.Expr) – the Sympy expression.

  • addFuncs (bool, optional) – if True, any Sympy functions without a direct match to symbolic functions are added to sexpr’s customFunctions list.