<jupyter_start><jupyter_text>Introduction: RosenbrockWelcome to the first AeroSandbox tutorial!AeroSandbox is a tool for solving design optimization problems for large, multidisciplinary engineered systems. The most important part of AeroSandbox is its `Opti()` stack, which allows you formulate and solve an optimization problem in natural mathematical syntax.The `Opti` class extends the `Opti` class of CasADi (the library AeroSandbox uses for automatic differentiation), adding many new features tailored specifically for engineering design. We'll explore more of these advanced features later!For now, let's solve the "Hello World!" of optimization problems: [the Rosenbrock problem](https://en.wikipedia.org/wiki/Rosenbrock_function). Mathematically, it is stated as:$$ \underset{x, y}{\text{minimize }}(1-x)^2 + 100(y-x^2)^2 $$In code:<jupyter_code>def rosenbrock(x, y):
    return (1 - x) ** 2 + 100 * (y - x ** 2) ** 2<jupyter_output><empty_output><jupyter_text>It's a good test case, because the minimum lies at the bottom of a shallow, curving valley:<jupyter_code>### Don't worry about this code block; this is just here to visualize the Rosenbrock function.
from matplotlib import pyplot as plt, colors; import numpy as np
fig, ax = plt.subplots(figsize=(4, 4), dpi=200, subplot_kw={"projection": "3d"})
X, Y = np.meshgrid(np.linspace(-2, 2, 150), np.linspace(-1, 3, 150))
ax.plot_surface(X, Y, rosenbrock(X, Y), cmap=plt.cm.rainbow, linewidth=0, norm=colors.LogNorm(vmin=0.1, vmax=2500), rstride=1, cstride=1)
ax.view_init(35, 255); plt.xlabel("$x$"); plt.ylabel("$y$"); plt.title("The Rosenbrock Function"); plt.tight_layout();
plt.show()<jupyter_output><empty_output><jupyter_text>As it turns out, a good number of engineering design optimization problems are pretty mathematically similar to the Rosenbrock problem (of course, there are plenty of exceptions). Specifically, many engineering design problems are:* Continuous: all design variables are continuous inputs (as opposed to being discrete).* [Nonlinear](https://en.wikipedia.org/wiki/Nonlinear_system): the sensitivity of performance with respect to inputs changes throughout the design space.* Nonconvex: doesn't satisfy the [convex inequality](https://en.wikipedia.org/wiki/Convex_functionDefinition). Speaking loosely, convexity means the objective function is always "curving up" and that the boundary of the feasible design space doesn't have any concave regions.* Poorly-scaled, i.e. Hessian has a large condition number (e.g. for Rosenbrock, $\text{cond}(H)\approx 2500$ at the optimum)* [Constrained](https://en.wikipedia.org/wiki/Constrained_optimization): most engineering problems are constrained. The Rosenbrock problem is unconstrained out-of-the-box, but we'll add a constraint in the next tutorial.For now, let's optimize and find the minimum of the Rosenbrock function! First, we set up the problem:<jupyter_code>import aerosandbox as asb  # This is the standard AeroSandbox import convention

opti = asb.Opti()  # Initialize a new optimization environment; convention is to name it `opti`.

### Define your optimization variables
x = opti.variable(init_guess=0)  # You must provide initial guesses.
y = opti.variable(init_guess=0)

### Define your objective
f = (1 - x) ** 2 + 100 * (y - x ** 2) ** 2  # You can construct nonlinear functions of variables...
opti.minimize(f)  # ...and then optimize them.<jupyter_output><empty_output><jupyter_text>Then, we solve the problem. The solver will spit out lots of helpful info as it solves; we can ignore this for now (and later we'll learn how to suppress it if desired).<jupyter_code>### Optimize
sol = opti.solve()  # This is the conventional syntax to solve the optimization problem.<jupyter_output>This is Ipopt version 3.12.3, running with linear solver mumps.
NOTE: Other linear solvers might be more efficient (see Ipopt documentation).

Number of nonzeros in equality constraint Jacobian...:        0
Number of nonzeros in inequality constraint Jacobian.:        0
Number of nonzeros in Lagrangian Hessian.............:        3

Total number of variables............................:        2
                     variables with only lower bounds:        0
                variables with lower and upper bounds:        0
                     variables with only upper bounds:        0
Total number of equality constraints.................:        0
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0 1[...]<jupyter_text>Finally, we can look at the optimal values of our optimization variables:<jupyter_code>### Extract values at the optimum
x_opt = sol.value(x)  # Evaluates x at the point where the solver converged.
y_opt = sol.value(y)

### Print values
print(f"x = {x_opt}")
print(f"y = {y_opt}")<jupyter_output>x = 0.9999999999999899
y = 0.9999999999999792
{'fraction_non_alphanumeric': 0.10527347578906097, 'fraction_numerical': 0.021368359145265633, 'mean_word_length': 4.143145161290323, 'pattern_counts': {'":': 1, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '493081', 'license': 'MIT', 'n_tokens_mistral': 1542, 'n_tokens_neox': 1413, 'n_words': 584, 'path': 'tutorial/01 - Optimization and Math/01 - 2D Rosenbrock.ipynb', 'repo_name': 'tackoo/AeroSandbox', 'size': '5101'}