# Using an array environment inside an xparse command

I would like to define a macro for optimization problems. My attempt is given below. I am wondering why \optimizationproblema works, but \optimizationproblemb does not (I get a "misplaced alignment tab" error when I try to use the latter). Apparently my constraints aren't viewed as belonging within the array in \optimizationproblemb? If there's a better way to approach the problem of defining my macro, that'd be nice to know, too. I'd like to keep the usage more or less the same (that is,

\optimizationproblem{minimize}{x}{f(x)}{g(x) <= 0}


should represent minimize f(x) over x subject to g(x) <= 0; I also need some way to suppress the "subject to" parts for unconstrained problems [I use an \IfValueTF test in my actual code]), but I don't really care about the implementation.

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

\NewDocumentCommand{\optimizationproblema}{mmmm}
{
\begin{array}{c@{\ }c@{\ }l}
\displaystyle\operatorname*{#1}_{#2} & :
& #3 \\
\mathrm{subject \ to} & : &
#4
\end{array}
\end{array}
}

\NewDocumentCommand{\optimizationproblemb}{momo}
{
\begin{array}{c@{\ }c@{\ }l}
\displaystyle\operatorname*{#1}_{#2} & :
& #3 \\
\mathrm{subject \ to} & : &
#4
\end{array}
\end{array}
}

\NewDocumentCommand{\constraint}{mmm}
{
#1 & #2 & #3 \\
}

\begin{document}

$\optimizationproblema { minimize } { x \in \mathbf{R}^{n} } { c^{T} x } { \constraint{A x}{=}{b} \constraint{x}{\geq}{0} }$

%$% \optimizationproblemb % { % minimize % } % [ % x \in \mathbf{R}^{n} % ] % { % c^{T} x % } % [ % \constraint{A x}{=}{b} % \constraint{x}{\geq}{0} % ] %$

\end{document}


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, (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}

-
Thanks. It works if I put the [ of the last argument on the same line as the } of the penultimate argument. I guess I can live with this, even though it doesn't quite satisfy my formatting desires. I'd still be open to other solutions if people know a better way. – Stirling Feb 12 '13 at 22:17
Put a % in the line before the optional argument. But I'd prefer a different syntax: this surely calls for key-value. – egreg Feb 12 '13 at 22:44

The implementation of optional arguments in xparse doesn't want spaces before the last one in order to avoid problems if the call is followed by a bracket that's to be printed.

So if you call

$\optimizationproblemb {minimize} [x \in \mathbf{R}^{n}] {c^{T} x}% <-------------- Don't forget this!!! [\constraint{A x}{=}{b} \constraint{x}{\geq}{0}]$


you'll have no problem. However I don't see why the constraint should be optional.

In this case I would recommend a key-value syntax:

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

\ExplSyntaxOn
% Allocate some variables
\tl_new:N \l_stirling_operator_tl
\tl_new:N \l_stirling_domain_tl
\tl_new:N \l_stirling_function_tl
\tl_new:N \l_stirling_constraints_tl
\tl_new:N \l_stirling_sidecondition_tl

% Define the keys
\keys_define:nn { stirlingopt }
{
operator   .tl_set:N = \l_stirling_operator_tl,
domain     .tl_set:N = \l_stirling_domain_tl,
function   .tl_set:N = \l_stirling_function_tl,
}

\NewDocumentCommand{\optimizationproblem}{m}
{
% Clear the variables
\tl_clear:N \l_stirling_operator_tl
\tl_clear:N \l_stirling_domain_tl
\tl_clear:N \l_stirling_function_tl
\tl_clear:N \l_stirling_constraints_tl
% Set the keys from the argument
\keys_set:nn { stirlingopt } { #1 }
% Print
\begin{array}{c@{{}\mathrel{:}{}}l}
\displaystyle
\operatorname*{\l_stirling_operator_tl}\sb{\l_stirling_domain_tl} &
\l_stirling_function_tl
\tl_if_empty:NF \l_stirling_constraints_tl
{ % this will be executed only if constraints are specified
\$2ex] \textnormal{subject~to} & \begin{array}[t]{@{}r@{}>{{}}c<{{}}@{}l@{}} \l_stirling_constraints_tl \end{array} } \end{array} } % A helper macro for the constraints \cs_new_protected:Npn \stirling_add_constraint:nnn #1 #2 #3 { \tl_put_right:Nn \l_stirling_constraints_tl { #1 & #2 & #3 \\ } } \ExplSyntaxOff \begin{document} \[ \optimizationproblem{ operator=minimize, domain=x\in\mathbf{R}^{n}, function=c^{T}x, constraint={Ax}{=}{b}, constraint={x}{\geq}{0}, }$
\end{document}


Note that the order of the keys is irrelevant; of course constraints will be added in the order they are expressed.

One can have no constraints, for instance with

$\optimizationproblem{ operator=minimize, domain=x\in\mathbf{R}^{n}, function=c^{T}x, }$


the result would be

I made some changes in how the arrays are typeset, for better spacing.

-
Nice to see LaTeX3's all-in-one capability: adding a key-value interface without problem. – Werner Feb 13 '13 at 0:18
Ah, that is beautiful! Thank you! Incidentally, I want the constraint to be optional because some optimization problems are unconstrained. (I have a separate macro for feasibility problems.) – Stirling Feb 13 '13 at 1:41
So, how would I suppress the "subject to:" when no constraints are provided? – Stirling Feb 13 '13 at 1:49
@Stirling That's quite easy! I'll add the check. – egreg Feb 13 '13 at 10:14
@egreg Thanks again! This is a great example, and has been very helpful. I even figured out how to support feasibility problems by myself! – Stirling Feb 13 '13 at 20:36