Skip to content

SymPy

Utility functions for parsing and evaluating SymPy expressions.

from prairielearn.sympy_utils import ...

ASTSympyType

__str__

__str__() -> str

Returns self.value

AssumptionsDictT module-attribute

AssumptionsDictT = dict[str, dict[str, Any]]

A dictionary of assumptions for variables in the expression.

Examples:

>>> {"x": {"positive": True}, "y": {"real": True}}

BaseSympyError

Exception base class for SymPy parsing errors

HasInvalidVariableError dataclass

Deprecated / unused.

LocalsForEval

A class with type signatures for the locals_for_eval dict

SympyJson

A class with type signatures for the SymPy JSON dict

ast_check_str

ast_check_str(
    expr: str,
    locals_for_eval: LocalsForEval,
    *,
    allow_sets: bool = False,
) -> None

Check the AST of the expression for security, whitelisting only certain nodes.

This prevents the user from executing arbitrary code through eval_expr.

convert_string_to_sympy

convert_string_to_sympy(
    expr: str,
    variables: Iterable[str] | None = None,
    *,
    allow_hidden: bool = False,
    allow_complex: bool = False,
    allow_sets: bool = False,
    allow_trig_functions: bool = True,
    simplify_expression: bool = True,
    custom_functions: Iterable[str] | None = None,
    assumptions: AssumptionsDictT | None = None,
) -> Expr

Convert a string to a SymPy expression, with optional restrictions on the variables and functions that can be used. If the string is invalid, raise an exception with a message that can be displayed to the user.

Parameters:

Name Type Description Default
expr str

The string to convert to a SymPy expression.

required
variables Iterable[str] | None

A list of variable names that are allowed in the expression.

None
allow_hidden bool

Whether to allow hidden variables (like pi and e).

False
allow_complex bool

Whether to allow complex numbers (like i).

False
allow_trig_functions bool

Whether to allow trigonometric functions.

True
simplify_expression bool

Whether to simplify the expression during conversion by evaluating it.

True
custom_functions Iterable[str] | None

A list of custom function names that are allowed in the expression.

None
assumptions AssumptionsDictT | None

A dictionary of assumptions for variables in the expression.

None

Examples:

>>> convert_string_to_sympy("n * sin(7*m) + m**2 * cos(6*n)", variables=["m", "n"])
n * sympy.sin(m * 7) + m * m * sympy.cos(n * 6)
>>> convert_string_to_sympy("-infty")
-sympy.oo
>>> convert_string_to_sympy("z**2 + y - x", variables=["x", "y", "z"], allow_complex=True, assumptions={"x": {"positive": False}, "z": {"complex": True}})

Returns:

Type Description
Expr

A sympy expression.

convert_string_to_sympy_with_source

convert_string_to_sympy_with_source(
    expr: str,
    variables: Iterable[str] | None = None,
    *,
    allow_hidden: bool = False,
    allow_complex: bool = False,
    allow_sets: bool = False,
    allow_trig_functions: bool = True,
    simplify_expression: bool = True,
    custom_functions: Iterable[str] | None = None,
    assumptions: AssumptionsDictT | None = None,
) -> tuple[Expr, str | CodeType]

Convert a string to a sympy expression, with optional restrictions on the variables and functions that can be used. If the string is invalid, raise an exception with a message that can be displayed to the user.

Returns:

Type Description
tuple[Expr, str | CodeType]

A tuple of the sympy expression and the source code that was used to generate it.

Raises:

Type Description
HasInvalidAssumptionError

If the assumptions are not valid.

HasConflictingVariableError

If the variable names conflict with existing names.

HasConflictingFunctionError

If the function names conflict with existing names.

evaluate

evaluate(
    expr: str,
    locals_for_eval: LocalsForEval,
    *,
    allow_complex: bool = False,
    allow_sets: bool = False,
) -> Expr

Evaluate a SymPy expression string with a given set of locals, and return only the result.

Returns:

Type Description
Expr

A SymPy expression.

evaluate_with_source

evaluate_with_source(
    expr: str,
    locals_for_eval: LocalsForEval,
    *,
    allow_complex: bool = False,
    allow_sets: bool = False,
    simplify_expression: bool = True,
) -> tuple[Expr, str | CodeType]

Evaluate a SymPy expression string with a given set of locals.

Returns:

Type Description
tuple[Expr, str | CodeType]

A tuple of the SymPy expression and the code that was used to generate it.

Raises:

Type Description
HasEscapeError

If the expression contains an escape character.

HasCommentError

If the expression contains a comment character.

HasSetNotationError

If the expression contains interval or set characters.

HasArgumentTypeError

If an expression is given the wrong types.

HasFunctionArityError

If a function is given the wrong number of args.

HasParseError

If the expression cannot be parsed.

BaseSympyError

If the expression cannot be evaluated.

find_symbol_offset

find_symbol_offset(expr: str, symbol: str) -> int

Return an approximate offset for symbol in expr for caret rendering.

get_builtin_constants

get_builtin_constants(
    *,
    allow_complex: bool = False,
    allow_hidden: bool = True,
) -> set[str]

Return the set of built-in constant names.

Parameters:

Name Type Description Default
allow_complex bool

Whether to include complex number constants (i, j).

False
allow_hidden bool

Whether to include sympy's longer name for constants.

True

Returns:

Type Description
set[str]

A set of built-in constant names.

get_builtin_functions

get_builtin_functions(
    *,
    allow_trig_functions: bool = True,
    allow_sets: bool = False,
) -> set[str]

Return the set of built-in function names.

Parameters:

Name Type Description Default
allow_trig_functions bool

Whether to include trigonometric functions.

True
allow_sets bool

Whether to include set operators and constructors.

False

Returns:

Type Description
set[str]

A set of built-in function names.

get_items_list

get_items_list(items_string: str | None) -> list[str]

Return a list of items from a comma-separated string.

greek_unicode_transform

greek_unicode_transform(input_str: str) -> str

Return input_str where all unicode greek letters are replaced by their spelled-out english names.

is_sympy_json

is_sympy_json(json: Any) -> TypeGuard[SympyJson]

Check if the input is a valid SymPy JSON dict.

Returns:

Type Description
TypeGuard[SympyJson]

True if the input is a valid SymPy JSON dict, False otherwise.

json_to_sympy

json_to_sympy(
    sympy_expr_dict: SympyJson,
    *,
    allow_sets: bool = False,
    allow_complex: bool = True,
    allow_trig_functions: bool = True,
    simplify_expression: bool = True,
) -> Expr

Convert a json-seralizable dictionary created by sympy_to_json to a SymPy expression.

Returns:

Type Description
Expr

A SymPy expression.

Raises:

Type Description
ValueError

If the input is not a valid SymPy JSON dict.

point_to_error

point_to_error(expr: str, ind: int, w: int = 5) -> str

Generate a string with a pointer to error in expr with index ind.

If ind is -1, returns the full expression without a caret pointer.

Returns:

Type Description
str

A string with the error location in the expression.

sympy_check

sympy_check(
    expr: Expr,
    locals_for_eval: LocalsForEval,
    *,
    allow_complex: bool,
    allow_sets: bool,
) -> None

Check the SymPy expression for complex numbers, invalid symbols, and floats.

sympy_to_json

sympy_to_json(
    a: Expr | Set,
    *,
    allow_complex: bool = True,
    allow_trig_functions: bool = True,
    allow_sets: bool = False,
) -> SympyJson

Convert a SymPy expression to a JSON-seralizable dictionary.

Returns:

Type Description
SympyJson

A JSON-serializable representation of the SymPy expression.

Raises:

Type Description
HasSetNotationError

If the expression is a set and allow_sets is False.

try_parse_string_as_sympy

try_parse_string_as_sympy(
    expr: str,
    variables: Iterable[str] | None,
    *,
    allow_complex: bool = False,
    allow_hidden: bool = False,
    allow_sets: bool = False,
    allow_trig_functions: bool = True,
    custom_functions: list[str] | None = None,
    imaginary_unit: str | None = None,
    simplify_expression: bool = True,
    assumptions: AssumptionsDictT | None = None,
) -> SympyParseResult

Try to parse expr as a SymPy expression.

Returns:

Type Description
SympyParseResult

A parsed SymPy expression on success, or a formatted error message on failure.

Example::

result = try_parse_string_as_sympy("x + 1", ["x"])
if isinstance(result, SympyParseFailure):
    print(result.error)
else:
    print(result.expr)

validate_names_for_conflicts

validate_names_for_conflicts(
    element_name: str,
    variables: list[str],
    custom_functions: list[str],
    *,
    allow_complex: bool = False,
    allow_hidden_variables: bool = True,
    allow_trig_functions: bool = True,
    allow_sets: bool = False,
) -> None

Validate that user-specified names don't conflict with built-in constants or functions.

Parameters:

Name Type Description Default
element_name str

Name of the element (for error messages).

required
variables list[str]

User-specified variable names.

required
custom_functions list[str]

User-specified custom function names.

required
allow_complex bool

Whether complex constants (i, j) are available.

False
allow_hidden_variables bool

Whether sympy's long-form names for constants are available.

True
allow_sets bool

Whether set operations are available.

False
allow_trig_functions bool

Whether trig functions are available.

True

Raises:

Type Description
ValueError

If any names conflict with built-ins.

validate_string_as_sympy

validate_string_as_sympy(
    expr: str,
    variables: Iterable[str] | None,
    *,
    allow_sets: bool = False,
    allow_hidden: bool = False,
    allow_complex: bool = False,
    allow_trig_functions: bool = True,
    custom_functions: list[str] | None = None,
    imaginary_unit: str | None = None,
    simplify_expression: bool = True,
    assumptions: AssumptionsDictT | None = None,
) -> str | None

Try to parse expr as a SymPy expression.

Returns:

Type Description
str | None

None if the expression is valid, and an error message otherwise.