It's really comfortable to write optimizations in terms of `sympy.codegen.rewrite.RewriteOptim`

:

```
# Evaluates all constant terms
evaluate_constant_terms = ReplaceOptim(
lambda e: hasattr(e, 'is_constant') and e.is_constant,
lambda p: p.evalf()
)
```

This PR adds a parameter `sympy_optimizations`

to the `create_*_kernel`

functions that applies the list of optimizations to the assignments before creating the AST.

`sympy.codegen.rewrite`

already has some optimizations. Some similar to the optimizations of pystencils.
For example `create_expand_pow_optimization(limit)`

is really similar to the logic in `CustomSympyPrinter._print_Pow`

.

See #13 (closed)

Problem: old versions of sympy (e.g. from ubuntu CI) don't have `sympy.codegen.rewrite`

. The optimizations are skipped in that case. `test_and_coverage`

applies all optimizations.

We could also try to implement a fma-optimization (fused-multipy add) with that and `sympy.Wild`

.