MATLAB Answers

Lukas
0

How do I parse two input arguments for matching dimensions with Matlab Input Parser?

Asked by Lukas
on 25 Jul 2014
Latest activity Commented on by Daniele Busi on 16 Sep 2018
I use the Matlab Inputparser Class to validate function input, this is a minimal example:
function C = multiplyMatrix(A, B)
p = inputParser;
addRequired(p, 'A', @isnumeric); % Line A
addRequired(p, 'B', @isnumeric);
parse(p, A, B);
if size(A, 2) ~= size(B, 1) % Line B
error('Size mismatch.');
end
C = A*B;
end
How do I integrate tests spanning more than one variable (i.e. the if-statement in Line B) in the concept of the Matlab Inputparser Class? I only found out how to create tests regarding one variable (see Line A).
I am also happy about comments about the usage of this Parser in total.
(I had asked this question on stackoverflow.com before, but I feel like this is the better place to ask.)

  0 Comments

Sign in to comment.

Products

1 Answer

Answer by Dima Lisin
on 26 Jul 2014
 Accepted Answer

Unfortunately, you cannot do that inside inputParser. For each parameter you can only supply a function that validates just that parameter. Any tests involving more than one parameter have to be done separately, after you call parse().

  4 Comments

Show 1 older comment
I beg to disagree. This is not a bug. It is a limitation of inputParser. It treats each parameter independently, and any validation that spans multiple parameters must be done separately. For example, the code you've posted does the job.
The reason inputParser treats each parameter separately is that it makes for a simple and clean interface. If you were to allow validators that take multiple parameters how would you do it?
Let's look at a more complex situation. Let's say we have required parameters A and B, and also a name-value pair 'C':
function C = multiplyMatrix(varargin)
p = inputParser;
addRequired(p, 'A', @isnumeric); % Line A
addRequired(p, 'B', @isnumeric);
addParameter(p, 'C', eye(3), @isnumeric);
...
end
Let's also say that A and C must have the same size. How would you tell that to the input parser? How would you specify a single validator function for A and C? Even if this can be done, it would certainly complicate the interface, and probably introduce additional limitations. In the example above you would probably have to require that A and C must be next to each other in the argument list, which would force you to re-order the arguments.
But what if you had more complicated validation involving multiple overlapping subsets of parameters? What if A and B must have the same number of columns, and A and C must have the same number of rows? It would seem much easier to let the input parser validate them in isolation, and then implement the complicated validation separately.
inputParser is very useful, because it saves you the trouble of parsing optional parameters and name-value pairs. But if you have complicated validation logic, you have to code that yourself. By the way it is always a good idea to wrap the parameter parsing including inputParser and any custom validation code into a local function.
The inputParser is indeed very useful. Since the errors are clear and the notation is widely accepted, I would like to remain within the inputParser framework.
This is a working improvisation:
function C = multiplyMatrix(A, B)
p = inputParser;
A_B = {A, B};
addRequired(p, 'A', @isnumeric);
addRequired(p, 'B', @isnumeric);
addRequired(p, 'A_B', @(x) (size(x{1}, 2) == size(x{2}, 1)));
parse(p, A, B, A_B);
C = A*B;
end
This is a possible way how it could look like with the additional function addCombination(). The additional function would have to be applied after evaluation of all other add*() functions. Thus, default values can be compared within combinations as well:
function C = multiplyMatrix(A, B)
p = inputParser;
addRequired(p, 'A', @isnumeric);
addRequired(p, 'B', @isnumeric);
addCombination(p, {'A', 'B'}, @(x, y) (size(x, 2) == size(y, 1)));
parse(p, A, B);
C = A*B;
end
Thank you for your detailed answer.
The suggested addCombination method can only be included inside a subclass. This subclass should do the job:
classdef inputParserEx < inputParser
properties
combofuncs
combovars
end
methods
function p = inputParserEx
p.combofuncs = cell(0);
p.combovars = cell(0);
end
function addCombination(p,vars,validateFunc)
assert(isa(p,'inputParserEx'))
assert(iscellstr(vars) && isvector(vars))
assert(isa(validateFunc,'function_handle'))
assert(nargin(validateFunc) == numel(vars));
p.combofuncs{end+1} = validateFunc;
p.combovars(end+1,1) = {numel(vars)};
p.combovars(end,2:numel(vars)+1) = {vars{:}};
end
function parse(p,varargin)
assert(isa(p,'inputParserEx'))
parse@inputParser(p,varargin{:});
for c = 1:numel(p.combofuncs)
args = cell(1,p.combovars{c,1});
for v = 1:p.combovars{c,1}
assert(isfield(p.Results,p.combovars{c,1+v}),['invalid combination number ' num2str(c)]);
args{v} = p.Results.(p.combovars{c,1+v});
end
assert(p.combofuncs{c}(args{:}),['condition number ' num2str(c) ' not fulfilled']);
end
end
end
end

Sign in to comment.