# A macro for optimization problems

This question is an extension of Using an array environment inside an xparse command. I was advised to make a separate question for the extension. I would like to thank egreg very much for his help. I have two more features that I would like to implement, and I was wondering if I could get just a little more assistance from the community.

In particular, I would like to support box constraints and enumerated constraints. Before egreg gave his solution using LaTeX3, my constraint command was a macro with signature mmmggo. The first three arguments represented a standard constraint; the fourth and fifth arguments represented the additional information in a box constraint; and the sixth argument represented the enumeration for an enumerated constraint. My attempt to get these features working in shown below (the failed portions are commented out).

A couple of notes about my motivation for certain choices: I would like to make the constraints readable in usage, this is why I prefer formatting constraints using the constraint command given below; if none of the constraints are enumerated, I want to prevent the \qquad used to set off the enumerations from being printed to preserve the horizontal centering of the problem. I consider the box-constraint issue mostly resolved (I can always just include the additional information for the box constraint in the third argument as shown below). However, I would really like the abilities to

1. have the enumeration be an optional argument, and
2. suppress the \qquad used to set off the enumerations when no enumerated constraints are used (to preserve the horizontal centering of the optimization problem).

\documentclass{article}
\usepackage{amsmath}
\usepackage{array}
\usepackage{xparse}

\ExplSyntaxOn
% allocate the variables for an optimization problem
\tl_new:N \l_optprob_operator_tl
\tl_new:N \l_optprob_variable_tl
\tl_new:N \l_optprob_objective_tl
\tl_new:N \l_optprob_constraints_tl

% define the keys
\keys_define:nn{optprob}
{
operator   .tl_set:N = \l_optprob_operator_tl,
variable   .tl_set:N = \l_optprob_variable_tl,
objective  .tl_set:N = \l_optprob_objective_tl,
% constraint .code:n   = \constraint{#1}
}

\NewDocumentCommand{\optimizationproblem}{m}
{
% clear the variables
\tl_clear:N \l_optprob_operator_tl
\tl_clear:N \l_optprob_variable_tl
\tl_clear:N \l_optprob_objective_tl
\tl_clear:N \l_optprob_constraints_tl

% get the keys
\keys_set:nn{optprob}{#1}

% print the optimization problem
\tl_if_empty:NTF \l_optprob_objective_tl
{
% feasibility problem
\begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l}
\l_optprob_constraints_tl
\end{array}
}
{
% optimization problem
\begin{array}{c@{{}\mathrel{:}{}}l}
\displaystyle\operatorname*{\l_optprob_operator_tl}\sb{\l_optprob_variable_tl} &
\l_optprob_objective_tl \$2ex] \tl_if_empty:NF \l_optprob_constraints_tl { % constrained optimization problem \textnormal{subject~to} & \begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l} \l_optprob_constraints_tl \end{array} } \end{array} } } \NewDocumentCommand{\constraint}{mmmggo} { \IfValueTF{#4} { \IfValueTF{#6} { #1 & #2 & #3 #4 #5 & \qquad #6 \\ } { #1 & #2 & #3 #4 #5 \\ } } { \IfValue{#6} { #1 & #2 & #3 & \qquad #6 \\ } { #1 & #2 & #3 \\ } } } % helper function to process the constraints \cs_new_protected:Npn \optprob_add_constraint:nnnnnn #1 #2 #3 #4 { \tl_put_right:Nn \l_optprob_constraints_tl {#1 & #2 & #3 & \qquad #4 \\} } \ExplSyntaxOff \begin{document} \[ \optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}{i = 1 , \ldots , m}, % constraint = {A_{i} x}{=}{b_{i}}[i = 1 , \ldots , m], constraint = {F_{j} x}{\leq}{g_{j}}{j = 1 , \ldots , m}, % constraint = {F_{j} x}{\geq}{g_{j}}[j = 1 , \ldots , m], constraint = {0}{\leq}{x \leq 1}{}, % constraint = {0}{\leq}{x}{\leq}{1}, }$

\end{document}

-

My suggestion is to add two new keys; one could add some checks to ensure these follow an associated constraint key.

\documentclass{article}
\usepackage{amsmath}
\usepackage{array}
\usepackage{xparse}

\ExplSyntaxOn
% allocate the variables for an optimization problem
\tl_new:N \l_optprob_operator_tl
\tl_new:N \l_optprob_variable_tl
\tl_new:N \l_optprob_objective_tl
\tl_new:N \l_optprob_constraints_tl

% define the keys
\keys_define:nn{optprob}
{
operator   .tl_set:N = \l_optprob_operator_tl,
variable   .tl_set:N = \l_optprob_variable_tl,
objective  .tl_set:N = \l_optprob_objective_tl,
cinfo      .code:n   = \optprob_add_info:n { #1 },
cnumber    .code:n   = \optprob_add_number:n { #1 },
}

\NewDocumentCommand{\optimizationproblem}{m}
{
% clear the variables
\tl_clear:N \l_optprob_operator_tl
\tl_clear:N \l_optprob_variable_tl
\tl_clear:N \l_optprob_objective_tl
\tl_clear:N \l_optprob_constraints_tl

% get the keys
\keys_set:nn{optprob}{#1}

% print the optimization problem
\tl_if_empty:NTF \l_optprob_objective_tl
{
% feasibility problem
\begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l}
\l_optprob_constraints_tl
\end{array}
}
{
% optimization problem
\begin{array}{c@{{}\mathrel{:}{}}l}
\displaystyle\operatorname*{\l_optprob_operator_tl}\sb{\l_optprob_variable_tl} &
\l_optprob_objective_tl \$2ex] \tl_if_empty:NF \l_optprob_constraints_tl { % constrained optimization problem \textnormal{subject~to} & \begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l} \l_optprob_constraints_tl \end{array} } \end{array} } } % helper function to process the constraints \cs_new_protected:Npn \optprob_add_constraint:nnn #1 #2 #3 { \tl_if_empty:NF \l_optprob_constraints_tl { \tl_put_right:Nn \l_optprob_constraints_tl { \\ } } \tl_put_right:Nn \l_optprob_constraints_tl { #1 & #2 & #3 } } \cs_new_protected:Npn \optprob_add_info:n #1 { \tl_put_right:Nn \l_optprob_constraints_tl { \quad (#1) } } \cs_new_protected:Npn \optprob_add_number:n #1 { \tl_put_right:Nn \l_optprob_constraints_tl { & \qquad #1 } } \ExplSyntaxOff \begin{document} \[ \optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}, cinfo = {i = 1 , \ldots , m}, constraint = {F_{j} x}{\leq}{g_{j}}, cinfo = {j = 1 , \ldots , m}, }$

$\optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}, cinfo = {i = 1 , \ldots , m}, cnumber = 1, constraint = {F_{j} x}{\leq}{g_{j}}, cinfo = {j = 1 , \ldots , m}, cnumber = 2, }$

\end{document}


Don't forget the braces around the value for cinfo if it contains a comma.

-

@egreg Thank you so much for your help! I ended up making one small change to the answer you provided, and I thought I'd post it as the final answer to my original question. I was also hoping that you might be able to check my work. In your solution, the cinfo fields did not quite line up, so I wanted to make a separate column in my array to hold these fields. But then the function that processes the cnumber fields needs to know whether a cinfo field has been processed for the current constraint in order to place the cnumber field in the correct column. I accomplish this using the variable \l_optprob_cinfo_flag_tl. Please do let me know if there's anything wrong with the style or logic of my solution, and thanks again for all your help getting this macro set up the way I want.

\documentclass{article}
\usepackage{amsmath}
\usepackage{array}
\usepackage{xparse}

\ExplSyntaxOn
% allocate the variables for an optimization problem
\tl_new:N \l_optprob_operator_tl
\tl_new:N \l_optprob_variable_tl
\tl_new:N \l_optprob_objective_tl
\tl_new:N \l_optprob_constraints_tl
\tl_new:N \l_optprob_cinfo_flag_tl

% define the keys
\keys_define:nn{optprob}
{
operator   .tl_set:N = \l_optprob_operator_tl,
variable   .tl_set:N = \l_optprob_variable_tl,
objective  .tl_set:N = \l_optprob_objective_tl,
cinfo      .code:n   = \optprob_add_info:n { #1 },
cnumber    .code:n   = \optprob_add_number:n { #1 },
}

\NewDocumentCommand{\optimizationproblem}{m}
{
% clear the variables
\tl_clear:N \l_optprob_operator_tl
\tl_clear:N \l_optprob_variable_tl
\tl_clear:N \l_optprob_objective_tl
\tl_clear:N \l_optprob_constraints_tl
\tl_clear:N \l_optprob_cinfo_flag_tl

% get the keys
\keys_set:nn{optprob}{#1}

% print the optimization problem
\tl_if_empty:NTF \l_optprob_objective_tl
{
% feasibility problem
\begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l@{}l}
\l_optprob_constraints_tl
\end{array}
}
{
% optimization problem
\begin{array}{c@{{}\mathrel{:}{}}l}
\displaystyle\operatorname*{\l_optprob_operator_tl}\sb{\l_optprob_variable_tl} &
\l_optprob_objective_tl \$2ex] \tl_if_empty:NF \l_optprob_constraints_tl { % constrained optimization problem \textnormal{subject~to} & \begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}l@{}l} \l_optprob_constraints_tl \end{array} } \end{array} } } % helper function to process the constraints \cs_new_protected:Npn \optprob_add_constraint:nnn #1 #2 #3 { \tl_if_empty:NF \l_optprob_constraints_tl { \tl_put_right:Nn \l_optprob_constraints_tl { \\ } } \tl_put_right:Nn \l_optprob_constraints_tl { #1 & #2 & #3 } \tl_clear:N \l_optprob_cinfo_flag_tl } \cs_new_protected:Npn \optprob_add_info:n #1 { \tl_put_right:Nn \l_optprob_constraints_tl { & \quad (#1) } \tl_put_right:Nn \l_optprob_cinfo_flag_tl {set flag} } \cs_new_protected:Npn \optprob_add_number:n #1 { \tl_if_empty:NT \l_optprob_cinfo_flag_tl { \tl_put_right:Nn \l_optprob_constraints_tl { & } } \tl_put_right:Nn \l_optprob_constraints_tl { & \qquad #1 } } \ExplSyntaxOff \begin{document} \[ \optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}, cinfo = {i = 1 , \ldots , m}, constraint = {F_{j} x}{\leq}{g_{j}}, cinfo = {j = 1 , \ldots , m}, }$

$\optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}, cinfo = {i = 1 , \ldots , m}, cnumber = 1, constraint = {F_{j} x}{\leq}{g_{j}}, cinfo = {j = 1 , \ldots , m}, cnumber = 2, }$

$\optimizationproblem { operator = minimize, variable = x \in \mathbf{R}^{n}, objective = c^{T} x, constraint = {A_{i} x}{=}{b_{i}}, cinfo = {i = 1 , \ldots , m}, cnumber = 1, constraint = {F x}{\leq}{g}, cnumber = 2, }$

\end{document}

-