-
Notifications
You must be signed in to change notification settings - Fork 3
🧮 Enhanced `param` Class for Safe Evaluations in Pizza 1.0x
read also the updated Wiki page: Mathematical Notations and Capabilities in
param():
The param class within the pizza.private.mstruct module has been significantly extended to manage complex mathematical expressions securely. This enhancement allows for the evaluation of expressions both within and outside ${...} placeholders without relying on Python's eval function, thereby mitigating potential security risks. The solution employs a param.safe_fstring() method to safely evaluate expressions, ensuring that mathematical computations are handled efficiently and securely.
- Overview
- Key Features
- Shorthand Syntax for NumPy Operations
- Supported Functions and Operators
- Usage Examples
- Implementation Details
- Important Considerations
- Conclusion
The enhanced param class facilitates the secure and intuitive evaluation of mathematical expressions within string templates using ${...} placeholders. Expressions can be embedded within strings or used as standalone scalar expressions outside of ${...}. While expressions inside ${...} support complex operations, including matrix indexing and slicing, those outside are primarily limited to scalar computations. This dual capability ensures flexibility while maintaining system security.
-
Safe Evaluation: Utilizes the
safe_fstring()method to evaluate expressions without usingeval, ensuring system security. -
Intuitive Syntax: Employs
${...}placeholders for embedding expressions within strings, making the syntax familiar and easy to use. - Shorthand Syntax: Provides simplified expressions for creating and manipulating vectors, matrices, and operations.
- Dynamic Context: Variables and expressions can depend on each other, with automatic resolution and type preservation.
-
Supported NumPy Operations: Includes advanced operations like matrix multiplication (
@), transposition (.T), and slicing. - Comprehensive Function Set: Offers a rich set of mathematical and random functions for computations.
- Error Handling: Provides robust error management for undefined variables or invalid operations.
-
Vectors:
-
$[...]: Creates a NumPy vector and ensures it is at least 2D.- Example:
$[1, 2, 3]→np.atleast_2d(np.array([1, 2, 3])).
- Example:
-
-
Matrices:
-
$[[...]]: Creates a 2D NumPy array.- Example:
$[[1, 2], [3, 4]]→np.array([[1, 2], [3, 4]]).
- Example:
-
-
Variable Conversion:
-
@{var}: Forces a variable (var) to be recognized as a NumPy array and ensures it is at least 2D.- Example:
@{n}→np.atleast_2d(np.array(n)).
- Example:
-
-
Indexing and Slicing:
-
${expr}: Supports indexing and slicing operations.- Example:
${p[1,1]}→ Accesses the element at(1, 1)in matrixp. - Example:
${p[:,1]}→ Retrieves the second column ofp.
- Example:
-
-
Operations:
- Supports standard arithmetic (
+,-,*,/,**) and advanced matrix operations (@,.T).
- Supports standard arithmetic (
-
Trigonometric Functions:
sin,cos,tan,asin,acos,atan,atan2,radians,degrees -
Exponential and Logarithmic Functions:
exp,log,log10,pow,sqrt -
Rounding and Modulus Functions:
ceil,floor,fmod,modf -
Absolute and Hypotenuse Functions:
fabs,hypot -
Constants:
pi,e
-
gauss,uniform,randint,choice
-
Arithmetic Operators:
+,-,*,/,** -
Matrix Operators:
@(multiplication),.T(transpose) -
Indexing and Slicing:
[ ],[:, ]
from pizza.private.mstruct import param
p = param(debug=True) # to show eventual evaluation errors
p.a = 1.0
p.b = "10.0"
p.c = "${a} + ${b}" # Scalar addition
print(repr(p))Output
# repr(p)
-------------:----------------------------------------
a: 1.0
b: 10.0
= 10.0
c: ${a} + ${b}
= 11.0
-------------:----------------------------------------
parameter list (param object) with 3 definitionsp.c = "$[${a}, 2, 3] * ${b}" # Creates a NumPy vector and scales it
p.n = "$[0, 0, 1]" # Another vector
p.o = "@{n}.T * 2" # Transpose and scale
print(repr(p))Output
# repr(p):
-------------:----------------------------------------
a: 1.0
b: 10.0
= 10.0
c: $[${a}, 2, 3] * ${b}
= [10 20 30] (double)
n: $[0, 0, 1]
= [0 0 1] (int64)
o: @{n}.T * 2
= [0 0 2]T (int64)
-------------:----------------------------------------
parameter list (param object) with 5 definitionsp.p = "$[[1, 2], [3, 4]]" # Create a 2D NumPy array
p.q = "${p[1, 1]}" # Indexing: retrieves 4
p.r = "@{p}[:,1] + 1" # Add 1 to the second column
p.s = "@{p}[:, 1].reshape(-1, 1) @ @{r}" # perform p(:,1)'*s in Matlab sense
p.t = "np.linalg.eig(@{s})" # Compute eigenvalues and eigenvectors
p.u = "The first eigenvalue is ${t.eigenvalues[0]}" # Print the first eigenvalue
p.v = "The second is ${t.eigenvalues[1]}" # Print the second eigenvalue
print(repr(p))Ouput
# repr(p):
-------------:----------------------------------------
a: 1.0
b: 10.0
= 10.0
c: $[${a}, 2, 3] * ${b}
= [10 20 30] (double)
p: $[[1, 2], [3, 4]]
= [2×2 int64]
q: ${p[1, 1]}
= 4
r: @{p}[:,1] + 1
= [3 5] (int64)
s: @{p}[:, 1].reshape(-1, 1) @ @{r}
= [2×2 int64]
t: np.linalg.eig(@{s})
= EigResult(eigenvalue [...] 576, -0.89442719]]))
u: The first eigenvalue [...] ${t.eigenvalues[0]}
= The first eigenvalue is 0.0
v: The second is ${t.eigenvalues[1]}
= The second is 26.0
-------------:----------------------------------------
parameter list (param object) with 10 definitionsError Messages
Some error messages are shown before the output when debug=True:
DEBUG u: Error in Evaluating: The first eigenvalue is 0.0
< invalid syntax (<unknown>, line 1) >
DEBUG v: Error in Evaluating: The second is 26.0
< invalid syntax (<unknown>, line 1) >They appear because the string content “The first value…” and “The second…” cannot be interpreted anymore as valid mathematical or NumPy expressions. The content is converted into a string when they cannot be anymore interpreted.
Extensions
The mathematical interpretation can be pushed further:
p.w = "${t.eigenvalues[0]} + ${t.eigenvalues[1]}" # sum of eigen values
p.x = "$[[0,${t.eigenvalues[0]}+${t.eigenvalues[1]}]]" # horizontal concat à la MatlabOutput
-------------:----------------------------------------
...
w: ${t.eigenvalues[0]} [...] ${t.eigenvalues[1]}
= 26.0
x: $[[0,${t.eigenvalues [...] {t.eigenvalues[1]}]]
= [0 26] (double)
-------------:---------------------------------------- Creating a param Instance
Create a param instance named context, initializing it with a NumPy array f:
from pizza.private.mstruct import param
import numpy as np
# Create a Param instance with a NumPy array
context = param(
f = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
])
)Defining Variables and Expressions
Define additional variables a and b, and create a list of expressions using ${...} placeholders to reference these variables:
# Define additional variables 'a' and 'b' in the context
context.update(
a =[1.0, 0.2, 0.03, 0.004],
b = np.array([[1, 0.2, 0.03, 0.004]])
)
# Example expressions using ${...} placeholders
expressions = [
"${a[1]}", # Should return 0.2
"${b[0,1]} + ${a[0]}", # Should return 1.2
"${f[0:2,1]}" # Should return the second column of 'f' as [2, 6]
]** Evaluating Expressions in Placeholders ${...}**
Iterate over the list of expressions, evaluating each one using the safe_fstring() method and printing the results:
# Evaluate each expression ${} and print the results
for expr in expressions:
result = param.safe_fstring(expr, context)
print(f"Expression (1): {expr} => Result: {result}")Expected Output:
# (1): Substitutions are applied only to placeholders
# note that placeholders cannot be nested
Expression (1): ${a[1]} => Result: 0.2
Expression (1): ${b[0,1]} + ${a[0]} => Result: 0.2 + 1.0
Expression (1): ${f[0:2,1]} => Result: [2, 6]Generalization to Full Evaluation
The previous expressions can be fully evaluated with formateval() method
# (2): Using formateval()
for expr in expressions:
result = context.formateval(expr)
print(f"Expression (2): {expr} => Result: {result}")Expected Output:
# (2): Using full evaluation
Expression (2): ${a[1]} => Result: 0.2
Expression (2): ${b[0,1]} + ${a[0]} => Result: 1.2
Expression (2): ${f[0:2,1]} => Result: [2, 6]Explanation:
-
${a[1]}:-
Evaluation: Accesses the second element of list
a, which is0.2. -
Result:
0.2
-
Evaluation: Accesses the second element of list
-
${b[0,1]} + ${a[0]}:-
Evaluation: Adds
b[0,1](0.2) toa[0](1.0). -
Result:
1.2
-
Evaluation: Adds
-
${f[0:2,1]}:-
Evaluation: Slices array
fto extract elements from rows0to1(inclusive) and column1, resulting in[2, 6]. -
Result:
[2 6]
-
Evaluation: Slices array
The param.eval() method drives the evaluation process, supported by the SafeEvaluator class for secure expression parsing. The replace_matrix_shorthand method ensures proper conversion of shorthands to valid NumPy syntax.
-
Dynamic Context:
- Variables and expressions can depend on each other and are evaluated dynamically.
-
Error Handling:
- Robust error detection for undefined variables and invalid operations.
-
Precision:
- Numerical precision may vary when embedding values in text strings.
-
Security:
- Eliminates the need for
eval, ensuring safe execution of expressions.
- Eliminates the need for
The enhanced param class offers a robust, secure, and intuitive system for managing mathematical expressions and NumPy operations. By supporting shorthand syntax, advanced operations, and a comprehensive function set, it is a powerful tool for dynamic computations in Python.
Version: v1.0x | Maintainer: INRAE\olivier.vitrac
💡 For more examples, visit the GitHub Pages Documentation.
📦 Download the latest release from the GitHub Releases.
🔗 Related Links:
🌟 If you find Pizza3 useful, consider starring the repository!