Reputation: 375
I have a question about the best practices for input validation in Matlab functions with multiple inputs. It's a bit philosophical. I've looked around in the forum and I have not seen a comprehensive discussion of this. I care about the case where the validation conditions involves two or more of the input variables. Here is an example.
Suppose that I write a function with two inputs, a and b. I know that the inputs must satisfy the conditions
a > 0
and b > 0
.
If I want to validate these inputs, I would normally write a function like this (just for illustration purposes, what the function does is not important):
% My function
function [result] = myLogSum(a,b)
% Create an input parser
p = inputParser;
% Add required inputs with validation
isPositive = @(x) all(x>0);
addRequired(p, 'a', isPositive);
addRequired(p, 'b', isPositive);
% Parse the inputs
parse(p,a,b);
% Calculate the log sum
result = log(a) + log(b);
end
But now suppose that a and b are arrays, and that I also need to check if they are the same size:
all(size(a) == size(b)) == true
.
Is there a way to deal with such a situation with the input parser? If not, what is the best way to deal with this?
I can think of four solutions, but I can't figure out which is the best.
1) Should I lump a and b in a single input cell array variable sumInput
of the form {a,b}
and write a custom validation function for the cell array? That's fine, but I don't know if it's always good practice to lump inputs together like that. Here it seems very natural to be able to write myLogSum(a,b)
instead of myLogSum(sumInput)
.
2) Or in such instances, should I write this part of the input validation in the function, i.e., modify the above code like this:
% My function
function [result] = myLogSum(a,b)
% Create an input parser
p = inputParser;
% Add required inputs with validation
isPositive = @(x) all(x>0);
addRequired(p, 'a', isPositive);
addRequired(p, 'b', isPositive);
% Parse the inputs
parse(p,a,b);
% Check that the input arrays have the same size
if ~all(size(a)==size(b))
message = 'The arrays a and b must have the same size.';
error(message);
end
% Calculate the log sum
result = log(a) + log(b);
end
That's fine too, but it makes the validation a bit inhomogeneous and inaesthetic because now I the inputs a and b are validated twice in different ways and for different reasons.
3) Should I just give up on the input parser and just write my own validation functions, as is suggested here:
Best practice when validating input in MATLAB
But I quite like the input parser because it's a neat way to manage options.
4) I could just let Matlab handle the error by itself when the program reaches the last line result = log(a) + log(b)
and the array sizes don't match. But somehow I feel like this is asking for trouble in the long run.
If you have experience in Matlab, just let me know what you think is the most robust and comprehensive validation strategy when the two inputs are related.
Ben
Upvotes: 4
Views: 603
Reputation: 375
As of Matlab 2019b, I would recommend the use of the arguments validation block:
https://de.mathworks.com/help/matlab/ref/arguments.html?searchHighlight=arguments&s_tid=srchtitle
It still has some limitations, but overall it is still quite versatile. It forces some uniformity in validation.
% My function
function result = myLogSum(a,b)
% Arguments validation block
arguments
a;
b {mustBeEqualSize(a,b)};
end
% Calculate the log sum
result = log(a) + log(b);
end
function mustBeEqualSize(a,b)
% Test for equal size
if ~isequal(size(a),size(b))
eid = 'mustBeEqualSize:sizesNotEqual';
msg = 'Size of first input must equal size of second input.';
throwAsCaller(MException(eid,msg))
end
end
Upvotes: 1
Reputation: 6284
There's nothing to stop you from calling parse
once, then adding new inputs and calling parse
again - at which point you can use the previously parsed value(s) in your validation function. The validateattributes
function is useful here for building validation functions with multiple conditions. For example:
% My function
function [result] = myLogSum(a,b)
% Create an input parser
p = inputParser;
% Add required inputs with validation
addRequired(p, 'a', @(x) validateattributes(x, {'numeric'}, {'>', 0}));
% Check the first input was valid
p.parse(a);
addRequired(p, 'b', @(x) validateattributes(x, {'numeric'}, {'>', 0, 'size', size(a)}));
% Parse the inputs
p.parse(a,b);
% Calculate the log sum
result = log(a) + log(b);
end
validateattributes
also generates reasonably explanatory error messages for you:
>> myLogSum(30, 40)
ans =
7.0901
>> myLogSum([30 20], 40)
Error using myLogSum (line 12)
The value of 'b' is invalid. Expected input to be of size 1x2 when it is
actually size 1x1.
>> myLogSum([30 20], [40 1])
ans =
7.0901 2.9957
>> myLogSum([30 20], [40 -1])
Error using myLogSum (line 12)
The value of 'b' is invalid. Expected input to be an array with all of the
values > 0.
Upvotes: 4