CSDL_alpha

Define a problem in CSDL_alpha

This example does not intend to cover all the features of CSDL_alpha. For more details and tutorials on CSDL_alpha, please refer to CSDL_alpha documentation. In this example, we solve a constrained problem given by

\[ \begin{align}\begin{aligned} \underset{x_1, x_2 \in \mathbb{R}}{\text{minimize}} \quad x_1^2 + x_2^2\\\newline \text{subject to} \quad x_1 \geq 0 \newline \quad \quad \quad \quad x_1 + x_2 = 1 \newline \quad \quad \quad \quad x_1 - x_2 \geq 1 \end{aligned}\end{align} \]

We know the solution of this problem is \(x_1=1\), and \(x_2=0\). However, we start from an initial guess of \(x_1=500.0\), and \(x_2=5.0\) for the purposes of this tutorial.

The problem model is written in CSDL_alpha as follows:

import csdl_alpha as csdl

# minimize x^2 + y^2 subject to x>=0, x+y=1, x-y>=1.

rec = csdl.Recorder()
rec.start()

# add design variables
x = csdl.Variable(name = 'x', value=500.)
y = csdl.Variable(name = 'y', value=5.)
x.set_as_design_variable(lower = 0.0)
y.set_as_design_variable()

# add objective
z = x**2 + y**2
z.add_name('z')
z.set_as_objective()

# add constraints
constraint_1 = x + y
constraint_2 = x - y
constraint_1.add_name('constraint_1')
constraint_2.add_name('constraint_2')
constraint_1.set_as_constraint(lower=1., upper=1.)
constraint_2.set_as_constraint(lower=1.)

rec.stop()

Once your model is defined within CSDL_alpha’s Recorder object, create a Simulator object from it. Lastly, translate the Simulator object to a CSDLAlphaProblem object for use in modOpt.

from csdl_alpha.experimental import PySimulator, JaxSimulator

# Create a Simulator object from the Recorder object
sim = PySimulator(rec)

# Import CSDLAlphaProblem from modopt
from modopt import CSDLAlphaProblem

# Instantiate your problem using the csdl Simulator object and name your problem
prob = CSDLAlphaProblem(
    problem_name='quadratic',
    simulator=sim,
)

Solve your problem using an optimizer

Once your problem model is wrapped for modOpt, import your preferred optimizer from modOpt and solve it, following the standard procedure. Here we will use the SLSQP optimizer from the SciPy library.

from modopt import SLSQP

# Setup your preferred optimizer (SLSQP) with the Problem object 
# Pass in the options for your chosen optimizer
optimizer = SLSQP(prob, solver_options={'maxiter':20})

# Check first derivatives at the initial guess, if needed
optimizer.check_first_derivatives(prob.x0)

# Solve your optimization problem
optimizer.solve()

# Print results of optimization
optimizer.print_results()
----------------------------------------------------------------------------
Derivative type | Calc norm  | FD norm    | Abs error norm | Rel error norm 
----------------------------------------------------------------------------

Gradient        | 1.0000e+03 | 1.0000e+03 | 1.5473e-05     | 1.5472e-08    
Jacobian        | 2.0000e+00 | 2.0000e+00 | 5.0495e-09     | 2.5248e-09    
----------------------------------------------------------------------------


	Solution from Scipy SLSQP:
	----------------------------------------------------------------------------------------------------
	Problem                  : quadratic
	Solver                   : scipy-slsqp
	Success                  : True
	Message                  : Optimization terminated successfully
	Status                   : 0
	Total time               : 0.006938934326171875
	Objective                : 1.0000000068019972
	Gradient norm            : 2.000000006801997
	Total function evals     : 2
	Total gradient evals     : 2
	Major iterations         : 2
	Total callbacks          : 17
	Reused callbacks         : 0
	obj callbacks            : 5
	grad callbacks           : 3
	hess callbacks           : 0
	con callbacks            : 6
	jac callbacks            : 3
	----------------------------------------------------------------------------------------------------

Scaling API

Please refer to the code snippet below as a guide for scaling the design variables, objective, and constraints independent of their definitions.

Warning

The results provided by the optimizer will always be scaled, while the values from the models will remain unscaled.

# add design variables
x.set_as_design_variable(lower = 0.0, scaler=2.)
y.set_as_design_variable(scaler=2.)

# add objective
z.set_as_objective(scaler=5.)

# add constraints
constraint_1.set_as_constraint(lower=1., upper=1., scaler=10.)
constraint_2.set_as_constraint(lower=1., scaler=100.)