nlconstr.m#

function nlconstr()
% Copyright 2026, Gurobi Optimization, LLC
%
% This example formulates and solves the following simple nonlinear model
% using the nonlinear constraint API:
%
%  minimize
%      x - y
%  subject to
%      sin(x) * x + x^2 * y <= 1
%      y = cos(x)  (<=> cos(x) - y = 0)
%      -1 <= x <= 2
%       0 <= y <= 2
%
% The nonlinear constraints are set through the model.nlcon field, a struct
% array holding the expression tree of each constraint together with its
% sense and right-hand side.

model.varnames = {'x', 'y'};

% Set linear objective: x - y
model.obj = [1, -1];

% Bounds: -1 <= x <= 2, 0 <= y <= 2
model.lb = [-1; 0];
model.ub = [ 2; 2];

% There are no linear constraints, but the model still needs a constraint
% matrix with one column per variable
model.A = sparse(0, 2);

% Add nonlinear constraint: sin(x) * x + x^2 * y <= 1
%
% The left-hand side is given as an expression tree with 10 nodes. Every node
% has an opcode, a data value, and the index of its parent node. Nodes are
% 1-based, and the root node is marked by a parent of 0. The data value of a
% VARIABLE node is the 1-based index of that variable, the data value of a
% CONSTANT node is the constant itself, and all other nodes use -1.
%
%   node  1: PLUS        (root)
%   node  2: MULTIPLY    (child of 1)  ->  sin(x) * x
%   node  3: SIN         (child of 2)
%   node  4: VARIABLE x  (child of 3)
%   node  5: VARIABLE x  (child of 2)
%   node  6: MULTIPLY    (child of 1)  ->  x^2 * y
%   node  7: POW         (child of 6)
%   node  8: VARIABLE x  (child of 7)
%   node  9: CONSTANT 2  (child of 7)
%   node 10: VARIABLE y  (child of 6)
model.nlcon(1).opcode = [ ...
    GRB.OPCODE_PLUS, ...      %  1: root
    GRB.OPCODE_MULTIPLY, ...  %  2: sin(x) * x
    GRB.OPCODE_SIN, ...       %  3: sin(...)
    GRB.OPCODE_VARIABLE, ...  %  4: x
    GRB.OPCODE_VARIABLE, ...  %  5: x
    GRB.OPCODE_MULTIPLY, ...  %  6: x^2 * y
    GRB.OPCODE_POW, ...       %  7: x^2
    GRB.OPCODE_VARIABLE, ...  %  8: x
    GRB.OPCODE_CONSTANT, ...  %  9: 2
    GRB.OPCODE_VARIABLE, ...  % 10: y
];
model.nlcon(1).data   = [-1, -1, -1, 1, 1, -1, -1, 1, 2, 2];
model.nlcon(1).parent = [ 0,  1,  2, 3, 2,  1,  6, 7, 7, 6];
model.nlcon(1).sense  = '<';
model.nlcon(1).rhs    = 1;
model.nlcon(1).name   = 'NonlinearConstraint1';

% Add nonlinear constraint: cos(x) - y = 0
%
% Only a constant right-hand side is allowed, so y = cos(x) is stated by
% moving y to the left-hand side. The expression tree has 4 nodes:
%
%   node 1: MINUS        (root)
%   node 2: COS          (child of 1)
%   node 3: VARIABLE x   (child of 2)
%   node 4: VARIABLE y   (child of 1)
model.nlcon(2).opcode = [ ...
    GRB.OPCODE_MINUS, ...     % 1: root
    GRB.OPCODE_COS, ...       % 2: cos(...)
    GRB.OPCODE_VARIABLE, ...  % 3: x
    GRB.OPCODE_VARIABLE, ...  % 4: y
];
model.nlcon(2).data   = [-1, -1, 1, 2];
model.nlcon(2).parent = [ 0,  1, 2, 1];
model.nlcon(2).sense  = '=';
model.nlcon(2).rhs    = 0;
model.nlcon(2).name   = 'NonlinearConstraint2';

% Optimize
params.logfile = 'nlconstr.log';
result = gurobi(model, params);

if strcmp(result.status, 'OPTIMAL')
    for j = 1:2
        fprintf('%s %e\n', model.varnames{j}, result.x(j));
    end
    fprintf('Obj: %e\n', result.objval);
else
    fprintf('Optimization did not produce an optimal solution\n');
end
end