expression_v1¶
Expression parser version 1 and namespace.
The syntax of an expression is as follows:
Integers or decimal numbers are denoted in the usual way. Examples:
1
,1.2
,.2
. A number may not start with a zero, except when followed by a dot:0.1
is valid, but01
is not.Variables are denoted with a string of alphanumeric characters. The first character may not be a numeral. Unlike Python variables, underscores are not allowed, as they have a special meaning. If the variable is an array with one or more axes, all those axes should be labeled with a latin character, the index, and appended to the variable with an underscore. For example an array
a
with two axes can be denoted witha_ij
. Optionally, a single numeral may be used to select an item at the concerning axis. Example: ina_i0
the first axis ofa
is labeledi
and the first element of the second axis is selected. If the same index occurs twice, the trace is taken along the concerning axes. Example: the trace of the first and third axes ofb
is denoted byb_iji
. It is invalid to specify an index more than twice. The following names cannot be used as variables:n
,δ
,$
. The variable namedx
, or the value of argumentdefault_geometry_name
, has a special meaning, detailed below.A term, the product of two or more arrays or scalars, is denoted by space-separated variables, constants or compound expressions. Example:
a b c
denotes the product of the scalarsa
,b
andc
. A term may start with a number, but a number is not allowed in other parts of the term. Example:2 a
denotes two timesa
;2 2 a
and2 a 2`
are invalid. When two arrays in a term have the same index, this index is summed. Example:a_i b_i
denotes the inner product ofa
andb
andA_ij b_j`
a matrix vector product. It is not allowed to use an index more than twice in a term.The operator
/
denotes a fraction. Example: ina b / c d
a b
is the numerator andc d
the denominator. Both the numerator and the denominator may start with a number. Example:2 a / 3 b
. The denominator must be a scalar. Example:2 / a_i b_i
is valid, but2 a_i / b_i
is not.Warning
This syntax is different from the Python syntax. In Python
a*b / c*d
is mathematically equivalent toa*b*d/c
.The operators
+
and-
denote add and subtract. Both operators should be surrounded by whitespace, e.g.a + b
. Both operands should have the same shape. Example:a_ij + b_i c_j
is a valid, provided that the lengths of the axes with the same indices match, buta_ij + b_i
is invalid. At the beginning of an expression or a compound-
may be used to negate the following term. Example: in-a b + c
the terma b
is negated before addingc
. It is not allowed to negate other terms:a + -b
is invalid, so isa -b
.An expression surrounded by parentheses is a compound expression and can be used as single entity in a term. Example:
(a_i + b_i) c_i
denotes the inner product ofa_i + b_i
withc_i
.Exponentiation is denoted by a
^
, where the left and right operands should be a number, variable or compound expression and the right operand should be a scalar. Example:a^2
denotes the square ofa
,a^-2
denotesa
to the power-2
anda^(1 / 2)
the square root ofa
.An argument is denoted by a name — following the same rules as a variable name — prefixed with a question mark. An argument is a scalar or array with a yet unknown value. Example:
basis_i ?coeffs_i
denotes the inner product of a basis with unknown coefficient vector?coeffs
. If possible the shape of the argument is deduced from the expression. In the previous example the shape of?coeffs
is equal to the shape ofbasis
. If the shape cannot be deduced from the expression the shape should be defined manually (seeparse()
). Arguments and variables live in separate namespaces:?x
andx
are different entities.An argument may be substituted by appending without whitespace
(arg = value)
to a variable of compound expression, wherearg
is an argument andvalue
the substitution. The substitution applies to the variable of compound expression only. The value may be an expression. Example:2 ?x(x = 3 + y)
is equivalent to2 (3 + y)
and2 ?x(x=y) + 3
is equivalent to2 (y) + 3
. It is possible to apply multiple substitutions. Example:(?x + ?y)(x = 1, y = )2
is equivalent to1 + 2
.The gradient of a variable to the default geometry — the default geometry is variable
x
unless overriden by the argumentdefault_geometry_name
— is denoted by an underscore, a comma and an index. If the variable is an array with more than one axis, the underscore is omitted. Example:a_,i
denotes the gradient of the scalara
to the geometry andb_i,j
the gradient of vectorb
. The gradient of a compound expression is denoted by an underscore, a comma and an index. Example:(a_i + b_j)_,k
denotes the gradient ofa_i + b_j
. The usual summation rules apply and it is allowed to use a numeral as index. The surface gradient is denoted with a semicolon instead of a comma, but follows the same rules as the gradient otherwise. Example:a_i;j
is the sufrace gradient ofa_i
to the geometry.The normal of the default geometry is denoted by
n_i
, where the indexi
may be replaced with an index of choice.A dirac is denoted by
δ
or$
and takes two indices. The shape of the dirac is deduced from the expression. Example: letA
be a square matrix with three rows and columns, thenδ_ij
in(A_ij - λ δ_ij) x_j
has three rows and columns as well.An expression surrounded by square brackets or curly braces denotes the jump or mean, respectively, of the enclosed expression. Example:
[ a_i ]
denotes the jump ofa_i
and{ a_i + b_i }
denotes the mean ofa_i + b_i
.A function call is denoted by a name — following the same rules as for a variable name — optionally followed by
_
and indices, optionally followed by:
and indices, directly followed by the left parenthesis(
, without a space. The arguments to the function are separated by a comma and at least one space. The function is applied pointwise to the arguments and summation convection is applied to the result. Example: assumemul(...)
returns the product of its arguments, thenmul(x_i, y_j)
is equivalent tox_i y_j
andmul(x_i, y_i)
tox_i y_i
. Functions and variables share a namespace: defining a variable with the same name as a function renders the function inaccessible. Functions of the formf_i(...)
andf_ij(...)
etc. generate one and two axes, respectively. Functions of the formf:i(...)
andf:ij(...)
etc. consume the axes labelledi
andi
andj
respectively. If all axes are consumed, it is allowed to omit the axes:sum(u)
is equivalent tosum:i(u_i)
.A stack of two or more arrays along an axis is denoted by a
<
followed by comma and space separated arrays followed by>
and an index. All arguments must have the same shape and must not have an axis labelled with the stack axis. Example:<1, 2>_i
creates an array with components1
and2
.
- nutils.expression_v1.parse(expression, variables, indices, arg_shapes={}, default_geometry_name='x', fixed_lengths=None, fallback_length=None, functions=None)¶
Parse
expression
and return AST.This function parses a tensor expression according to the syntax described in module
nutils.expression_v1
and returns an Abstract Syntax Tree (AST).- Parameters
expression (
str
) – The expression to parse. Seeexpression_v1
for the expression syntax.variables (
dict
ofstr
andnutils.function.Array
pairs) – Adict
of variable names and array pairs. All variables used in theexpression
should exist invariables
.indices (
str
) – The indices used for aligning the resulting array. For example, letexpression
be'a_ij'
. Ifindices
is'ij'
, then the returned array is simplyvariables['a']
, but ifindices
is'ji'
the transpose ofvariables['a']
is returned. All indices of theexpression
should be listed precisely once.arg_shapes (
dict
ofstr
andtuple
orint
s pairs) – Adict
of argument names and shapes. Ifexpression
contains an argument not present inarg_shapes
the shape will be decuded from the expression and added to a copy ofarg_shapes
.default_geometry_name (
str
) – The name of the default geometry variable. When computing a gradient or the normal, e.g.'f_,i'
or'n_i'
, this variable is used as the geometry, unless the geometry is explicitly mentioned in the expression. Default:'x'
.fixed_lengths (
dict
ofstr
andint
pairs, optional) – Adict
of indices and lengths. All axes in the expression marked with an index of fixed length are asserted to have the fixed length.fallback_length (
int
, optional) – The fallback length of an axis if the length cannot be determined from the expression.
- Returns
ast (
tuple
) – The parsedexpression
as an abstract syntax tree (AST). The AST is atuple
of an opcode and arguments. The special opcodeNone
indicates that the single argument is used verbatim. All other opcodes have AST as arguments. The following opcodes exist:(None, const) ('group', group) ('arg', name, *shape) ('substitute', array, arg, value) ('call', func, arg) ('eye', length) ('normal', geom) ('getitem', array, dim, index) ('trace', array, n1, n2) ('sum', array, axis) ('concatenate', *args) ('grad', array, geom) ('surfgrad', array, geom) ('derivative', func, target) ('append_axis', array, length) ('transpose', array, trans) ('jump', array) ('mean', array) ('neg', array) ('add', left, right) ('sub', left, right) ('mul', left, right) ('truediv', left, right) ('pow', left, right)
arg_shapes (
dict
ofstr
andtuple
ofint
s pairs) – A copy ofarg_shapes
updated with shapes of arguments present in thisexpression
.
- class nutils.expression_v1.Namespace(*, default_geometry_name='x', fallback_length=None, functions=None, **kwargs)¶
Bases:
object
Namespace for
Array
objects supporting assignments with tensor expressions.The
Namespace
object is used to storeArray
objects.>>> from nutils import expression_v1, function >>> ns = expression_v1.Namespace() >>> ns.A = function.zeros([3, 3]) >>> ns.x = function.zeros([3]) >>> ns.c = 2
In addition to the assignment of
Array
objects, it is also possible to specify an array using a tensor expression string — seenutils.expression_v1
for the syntax. All attributes defined in this namespace are available as variables in the expression. If the array defined by the expression has one or more dimensions the indices of the axes should be appended to the attribute name. Examples:>>> ns.cAx_i = 'c A_ij x_j' >>> ns.xAx = 'x_i A_ij x_j'
It is also possible to simply evaluate an expression without storing its value in the namespace by passing the expression to the method
eval_
suffixed with appropriate indices:>>> ns.eval_('2 c') Array<> >>> ns.eval_i('c A_ij x_j') Array<3> >>> ns.eval_ij('A_ij + A_ji') Array<3,3>
For zero and one dimensional expressions the following shorthand can be used:
>>> '2 c' @ ns Array<> >>> 'A_ij x_j' @ ns Array<3>
Sometimes the dimension of an expression cannot be determined, e.g. when evaluating the identity array:
>>> ns.eval_ij('δ_ij') Traceback (most recent call last): ... nutils.expression_v1.ExpressionSyntaxError: Length of axis cannot be determined from the expression. δ_ij ^
There are two ways to inform the namespace of the correct lengths. The first is to assign fixed lengths to certain indices via keyword argument
length_<indices>
:>>> ns_fixed = expression_v1.Namespace(length_ij=2) >>> ns_fixed.eval_ij('δ_ij') Array<2,2>
Note that evaluating an expression with an incompatible length raises an exception:
>>> import numpy >>> ns = expression_v1.Namespace(length_i=2) >>> ns.a = numpy.array([1,2,3]) >>> 'a_i' @ ns Traceback (most recent call last): ... nutils.expression_v1.ExpressionSyntaxError: Length of index i is fixed at 2 but the expression has length 3. a_i ^
The second is to define a fallback length via the
fallback_length
argument:>>> ns_fallback = expression_v1.Namespace(fallback_length=2) >>> ns_fallback.eval_ij('δ_ij') Array<2,2>
When evaluating an expression through this namespace the following functions are available:
opposite
,sin
,cos
,tan
,sinh
,cosh
,tanh
,arcsin
,arccos
,arctan2
,arctanh
,exp
,abs
,ln
,log
,log2
,log10
,sqrt
andsign
.Additional pointwise functions can be passed to argument
functions
. All functions should takeArray
objects as arguments and must return anArray
with as shape the sum of all shapes of the arguments.>>> def sqr(a): ... return a**2 >>> def mul(a, b): ... return a[(...,)+(None,)*b.ndim] * b[(None,)*a.ndim] >>> ns_funcs = expression_v1.Namespace(functions=dict(sqr=sqr, mul=mul)) >>> ns_funcs.a = numpy.array([1,2,3]) >>> ns_funcs.b = numpy.array([4,5]) >>> 'sqr(a_i)' @ ns_funcs # same as 'a_i^2' Array<3> >>> ns_funcs.eval_ij('mul(a_i, b_j)') # same as 'a_i b_j' Array<3,2> >>> 'mul(a_i, a_i)' @ ns_funcs # same as 'a_i a_i' Array<>
- Parameters
default_geometry_name (
str
) – The name of the default geometry. This argument is passed tonutils.expression_v1.parse()
. Default:'x'
.fallback_length (
int
, optional) – The fallback length of an axis if the length cannot be determined from the expression.length_<indices> (
int
) – The fixed length of<indices>
. All axes in the expression marked with one of the<indices>
are asserted to have the specified length.functions (
dict
, optional) – Pointwise functions that should be available in the namespace, supplementing the default functions listed above. All functions should return arrays with as shape the sum of all shapes of the arguments.
- __getstate__(self)¶
Pickle instructions
- __setstate__(self, d)¶
Unpickle instructions
- property default_geometry: str¶
The default geometry, shorthand for
getattr(ns, ns.default_geometry_name)
.
- __call__(*args, **subs)¶
Return a copy with arguments replaced by
subs
.Return a copy of this namespace with
Argument
objects replaced according tosubs
.
- copy_(self, *, default_geometry_name=None)¶
Return a copy of this namespace.
- __getattr__(self, name)¶
Get attribute
name
.
- __setattr__(self, name, value)¶
Set attribute
name
tovalue
.
- __delattr__(self, name)¶
Delete attribute
name
.
- __rmatmul__(expr: str) nutils.function.Array ¶
- __rmatmul__(expr: Union[Tuple[str, ...], List[str]]) Tuple[nutils.function.Array, ...]
Evaluate zero or one dimensional
expr
or a list of expressions.