SQP Optimizer
SQPoptimizer is a general-purpose nonlinear programming (NLP) solver that uses
Sequential Quadratic Programming to minimize an objective function subject to
equality constraints, inequality constraints, and variable bounds.
When to Use SQP
| Need | Recommended Optimizer |
|---|---|
| Single variable adjustment | Adjuster |
| Multi-variable simultaneous specs | MultiVariableAdjuster |
| Minimize/maximize an objective (no constraints) | ProductionOptimizer or SQP |
| Minimize/maximize with equality & inequality constraints | SQP |
| External optimizer integration | ProcessSimulationEvaluator + SciPy |
| Multi-objective Pareto | MultiObjectiveOptimizer |
SQP is the right choice when you have a constrained optimization problem: minimise cost, maximise throughput, or optimise operating conditions subject to equipment limits, product specs, or safety constraints.
Algorithm
The solver implements a reduced SQP method for smooth local optimization:
-
Quadratic sub-problem (QP): At each iteration, build a quadratic objective model and linearise constraints. The reduced active-set solve releases inequalities with negative multipliers; variable bounds use projection.
-
BFGS Hessian update: Objective-gradient differences update the Hessian approximation with damped BFGS (Powell’s modification). Nonlinear constraint curvature is not included in this update.
-
L1 merit function: An exact penalty merit function combines the objective and constraint violations to determine step acceptance:
\[\phi(x) = f(x) + \sum_i \mu_i |c_i(x)| + \sum_j \mu_j \max(0, -h_j(x))\] -
Armijo backtracking: The step length is reduced until sufficient decrease in the merit function is achieved.
-
KKT convergence: The solver checks Karush-Kuhn-Tucker optimality conditions and stops when the KKT error falls below the tolerance.
Check isConverged() and replay the selected process state before using the result.
The reported KKT error checks stationarity and primal feasibility; independently
check constraint values and solution quality for the intended application.
Basic Usage
Each example is a Java method body: put imports above your class and executable
statements inside public static void main(String[] args). Run the algebraic and
process examples separately because they reuse local names.
Java output uses Log4j2. Declare this field inside your example class:
private static final org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger("OptimizationExample");.
import neqsim.process.util.optimizer.SQPoptimizer;
import neqsim.process.util.optimizer.SQPoptimizer.OptimizationResult;
SQPoptimizer sqp = new SQPoptimizer();
// Objective: minimise f(x) = (x[0]-2)^2 + (x[1]-3)^2
sqp.setObjectiveFunction(x -> (x[0] - 2.0) * (x[0] - 2.0)
+ (x[1] - 3.0) * (x[1] - 3.0));
// Equality constraint: x[0] + x[1] = 4
sqp.addEqualityConstraint(x -> x[0] + x[1] - 4.0);
// Inequality constraint (h(x) >= 0): x[0] >= 1
sqp.addInequalityConstraint(x -> x[0] - 1.0);
// Variable bounds
sqp.setVariableBounds(
new double[] {0.0, 0.0}, // lower bounds
new double[] {10.0, 10.0} // upper bounds
);
// Solver settings
sqp.setMaxIterations(100);
sqp.setTolerance(1e-8);
// Start from a point satisfying the equality, inequality, and bounds.
sqp.setInitialPoint(new double[] {1.0, 3.0});
OptimizationResult result = sqp.solve();
logger.info("Converged={}, KKT error={}", result.isConverged(), result.getKktError());
if (result.isConverged()) {
double[] xOpt = result.getOptimalPoint();
logger.info(String.format("x* = [%.4f, %.4f]%n", xOpt[0], xOpt[1]));
logger.info(String.format("f* = %.6f%n", result.getOptimalValue()));
logger.info(String.format("Iterations: %d%n", result.getIterations()));
logger.info(String.format("KKT error: %.2e%n", result.getKktError()));
}
Process Optimization Example
Optimise compressor interstage pressures to minimise total power:
import neqsim.thermo.system.SystemSrkEos;
import neqsim.process.equipment.stream.Stream;
import neqsim.process.equipment.compressor.Compressor;
import neqsim.process.equipment.heatexchanger.Cooler;
import neqsim.process.processmodel.ProcessSystem;
import neqsim.process.util.optimizer.SQPoptimizer;
import neqsim.process.util.optimizer.SQPoptimizer.OptimizationResult;
// Build process: 2-stage compression with intercooling
SystemSrkEos gas = new SystemSrkEos(288.15, 5.0);
gas.addComponent("methane", 0.9);
gas.addComponent("ethane", 0.07);
gas.addComponent("propane", 0.03);
gas.setMixingRule("classic");
Stream feed = new Stream("feed", gas);
feed.setFlowRate(50000.0, "kg/hr");
Compressor comp1 = new Compressor("LP Comp", feed);
comp1.setOutletPressure(20.0);
comp1.setIsentropicEfficiency(0.75);
Cooler cooler1 = new Cooler("Intercooler", comp1.getOutletStream());
cooler1.setOutTemperature(303.15);
Compressor comp2 = new Compressor("HP Comp", cooler1.getOutletStream());
comp2.setOutletPressure(80.0);
comp2.setIsentropicEfficiency(0.75);
ProcessSystem process = new ProcessSystem();
process.add(feed);
process.add(comp1);
process.add(cooler1);
process.add(comp2);
process.run();
// Optimise: find interstage pressure that minimises total power
SQPoptimizer sqp = new SQPoptimizer();
sqp.setObjectiveFunction(x -> {
comp1.setOutletPressure(x[0]);
process.run();
return comp1.getPower("kW") + comp2.getPower("kW");
});
// Interstage pressure between feed and discharge
sqp.setVariableBounds(
new double[] {8.0}, // above feed pressure
new double[] {60.0} // below final discharge
);
sqp.setMaxIterations(30);
sqp.setTolerance(1e-4);
sqp.setFiniteDifferenceStep(1e-4); // Relative step, multiplied by max(1, |x[i]|)
sqp.setInitialPoint(new double[] {20.0});
OptimizationResult result = sqp.solve();
logger.info("Converged={}, KKT error={}", result.isConverged(), result.getKktError());
if (result.isConverged()) {
double pOpt = result.getOptimalPoint()[0];
comp1.setOutletPressure(pOpt);
process.run(); // Restore the optimum after finite-difference trial evaluations
logger.info(String.format("Optimal interstage P: %.1f bara%n", pOpt));
logger.info(String.format("Min total power: %.0f kW%n", result.getOptimalValue()));
}
API Reference
Constructor
SQPoptimizer sqp = new SQPoptimizer();
Problem Definition
| Method | Description |
|---|---|
setObjectiveFunction(ObjectiveFunc) |
Function to minimise: f(double[] x) -> double |
addEqualityConstraint(ConstraintFunc) |
Constraint c(x) = 0 |
addInequalityConstraint(ConstraintFunc) |
Constraint h(x) >= 0 |
setVariableBounds(double[], double[]) |
Lower and upper bounds on all variables |
Solver Settings
| Method | Default | Description |
|---|---|---|
setMaxIterations(int) |
100 | Maximum iterations |
setTolerance(double) |
1e-6 | KKT error convergence tolerance |
setFiniteDifferenceStep(double) |
1e-6 | Relative step for central-difference gradients |
Result
OptimizationResult is returned from solve():
| Method | Returns | Description |
|---|---|---|
isConverged() |
boolean |
Whether KKT conditions are satisfied |
getOptimalPoint() |
double[] |
Optimal variable values |
getOptimalValue() |
double |
Objective value at optimum |
getIterations() |
int |
Number of SQP iterations |
getKktError() |
double |
Final KKT error measure |
Functional Interfaces
// Objective function: x -> scalar
SQPoptimizer.ObjectiveFunc f = x -> x[0]*x[0] + x[1]*x[1];
// Constraint function: x -> scalar (equality: =0, inequality: >=0)
SQPoptimizer.ConstraintFunc c = x -> x[0] + x[1] - 1.0;
Integration with ProcessOptimizationEngine
The SEQUENTIAL_QUADRATIC_PROGRAMMING algorithm is also registered as a
SearchAlgorithm in ProcessOptimizationEngine:
import neqsim.process.util.optimizer.ProcessOptimizationEngine;
ProcessOptimizationEngine engine = new ProcessOptimizationEngine(process);
engine.setSearchAlgorithm(
ProcessOptimizationEngine.SearchAlgorithm.SEQUENTIAL_QUADRATIC_PROGRAMMING);
// ... configure and run
Related Documentation
- Optimization Overview - All optimization capabilities
- Adjusters - Single and multi-variable adjusters
- External Optimizer Integration - SciPy/Python integration
- Production Optimization Guide - ProductionOptimizer examples