sandalwood Tutorial: Advanced Functionality

This notebook explores advanced topics in sandalwood, including substitution, composition, accessing coefficients, and the use of other elementary functions.

1. Initialization

As always, we begin by initializing the global settings for sandalwood.

[1]:
import numpy as np
import sympy as sp
from IPython.display import display

from sandalwood import ComplexMultivariateTaylorFunction, mtf

if not mtf.get_mtf_initialized_status():
    mtf.initialize_mtf(max_order=8, max_dimension=3)
else:
    print("MTF globals are already initialized.")

# Define some variables for our examples
x = mtf.var(1)
y = mtf.var(2)
z = mtf.var(3)

2. Substitution and Composition

A powerful feature of sandalwood is the ability to substitute variables with constants or even other Taylor functions.

Substitution with a Constant

Replace a variable in a Taylor function with a constant value.

[2]:
f_xyz = x + y + z**4
print(f"Original function f(x, y, z):\n{f_xyz}\n")

# Substitute x with 0.1
g_yz = f_xyz.substitute_variable(1, 0.1)
print(f"After substituting x=0.1, we get g(y, z):\n{g_yz}")
Original function f(x, y, z):
         Coefficient  Order  Exponents
0 1.000000000000e+00      1  (1, 0, 0)
1 1.000000000000e+00      1  (0, 1, 0)
2 1.000000000000e+00      4  (0, 0, 4)


After substituting x=0.1, we get g(y, z):
         Coefficient  Order  Exponents
0 1.000000000000e-01      0  (0, 0, 0)
1 1.000000000000e+00      1  (0, 1, 0)
2 1.000000000000e+00      4  (0, 0, 4)

Composition with other Taylor Functions

Substitute variables with other Taylor functions to compose them.

[3]:
# Outer function: f(x, y) = x^2 + y^3
f_xy = x**2 + y**3

# Substituting functions:
# g(y) = 1 + y
# h(y, z) = 1 + y*z
g_y = 1 + y
h_yz = 1 + y * z

# Perform composition: f(g(y), h(y,z))
composed_f = f_xy.compose({1: g_y, 2: h_yz})
print(f"Composed function f(g(y), h(y,z)):\n{composed_f}")
Composed function f(g(y), h(y,z)):
         Coefficient  Order  Exponents
0 2.000000000000e+00      0  (0, 0, 0)
1 2.000000000000e+00      1  (0, 1, 0)
2 1.000000000000e+00      2  (0, 2, 0)
3 3.000000000000e+00      2  (0, 1, 1)
4 3.000000000000e+00      4  (0, 2, 2)
5 1.000000000000e+00      6  (0, 3, 3)

3. Accessing Coefficients

You can directly access the coefficients and exponents of a Taylor function.

[4]:
sin_x = mtf.sin(x)
coefficients = {tuple(exp): coeff for exp, coeff in zip(sin_x.exponents, sin_x.coeffs)}

print("Coefficients of sin(x):")
for exp, coeff in coefficients.items():
    print(f"  Exponent {exp}: {coeff}")
Coefficients of sin(x):
  Exponent (np.int32(1), np.int32(0), np.int32(0)): (1+0j)
  Exponent (np.int32(3), np.int32(0), np.int32(0)): (-0.16666666666666666+0j)
  Exponent (np.int32(5), np.int32(0), np.int32(0)): (0.008333333333333333+0j)
  Exponent (np.int32(7), np.int32(0), np.int32(0)): (-0.0001984126984126984+0j)

4. Other Elementary Functions

sandalwood supports a variety of other elementary functions.

[5]:
print("--- Example: mtf.exp(x+2*y) ---")
x = mtf.var(1)
y = mtf.var(2)
mtf_sin = mtf.sin(x + 2 * y)
sympy_sin_expr = mtf_sin.symprint()
print("The following is a SymPy object, which will render beautifully in a notebook.")
display(sympy_sin_expr)

# --- Example with ComplexMultivariateTaylorFunction ---
print("--- Example: mtf.exp(i*x) ---")
i = 1j

x_complex = ComplexMultivariateTaylorFunction.from_variable(var_index=1, dimension=1)
mtf_complex_exp = mtf.exp(i * x_complex)
sympy_complex_expr = mtf_complex_exp.symprint(symbols=["x"])
print("The following is a SymPy object for a complex function.")
display(sympy_complex_expr)

# --- Example with custom coefficient formatting ---
print("--- Example: Custom coefficient formatting ---")


def custom_formatter(c, p):
    # A simple rational formatter
    if np.iscomplexobj(c):
        return sp.Rational(c.real).limit_denominator(10**p) + sp.I * sp.Rational(
            c.imag
        ).limit_denominator(10**p)
    else:
        return sp.Rational(c).limit_denominator(10**p)


# Re-initialize for the 2D mtf_sin
x = mtf.var(1)
y = mtf.var(2)
mtf_sin = mtf.sin(x + 2 * y)

sympy_custom_format_expr = mtf_sin.symprint(
    precision=3, coeff_formatter=custom_formatter
)
print("The following is a SymPy object with custom rational formatting.")
display(sympy_custom_format_expr)
--- Example: mtf.exp(x+2*y) ---
The following is a SymPy object, which will render beautifully in a notebook.
$\displaystyle - 0.000198413 x^{7} - 0.00277778 x^{6} y - 0.0166667 x^{5} y^{2} + 0.00833333 x^{5} - 0.0555556 x^{4} y^{3} + 0.0833333 x^{4} y - 0.111111 x^{3} y^{4} + 0.333333 x^{3} y^{2} - 0.166667 x^{3} - 0.133333 x^{2} y^{5} + 0.666667 x^{2} y^{3} - 1.0 x^{2} y - 0.0888889 x y^{6} + 0.666667 x y^{4} - 2.0 x y^{2} + 1.0 x - 0.0253968 y^{7} + 0.266667 y^{5} - 1.33333 y^{3} + 2.0 y$
--- Example: mtf.exp(i*x) ---
The following is a SymPy object for a complex function.
$\displaystyle 2.48016 \cdot 10^{-5} x^{8} - 0.000198413 i x^{7} - 0.00138889 x^{6} + 0.00833333 i x^{5} + 0.0416667 x^{4} - 0.166667 i x^{3} - 0.5 x^{2} + 1.0 i x + 1.0$
--- Example: Custom coefficient formatting ---
The following is a SymPy object with custom rational formatting.
$\displaystyle - \frac{x^{6} y}{360} - \frac{x^{5} y^{2}}{60} + \frac{x^{5}}{120} - \frac{x^{4} y^{3}}{18} + \frac{x^{4} y}{12} - \frac{x^{3} y^{4}}{9} + \frac{x^{3} y^{2}}{3} - \frac{x^{3}}{6} - \frac{2 x^{2} y^{5}}{15} + \frac{2 x^{2} y^{3}}{3} - x^{2} y - \frac{4 x y^{6}}{45} + \frac{2 x y^{4}}{3} - 2 x y^{2} + x - \frac{8 y^{7}}{315} + \frac{4 y^{5}}{15} - \frac{4 y^{3}}{3} + 2 y$