8

I'm using Octave and would like to vectorize a function that accepts as input a single real number and outputs a row vector of fixed length. I understand that arrayfun should be able to do this from its unclear documentation. From help arrayfun in Octave 3.2:

If the parameter VAL after a further string input argument "UniformOutput" is set 'true' (the default), then the named function FUNC must return a single element which then will be concatenated into the return value and is of type matrix. Otherwise, if that parameter is set to `false', then the outputs are concatenated in a cell array.

It seems however that Matlab's version is more forgiving:

[B1,...,Bm] = arrayfun(func,A1,...,An) calls the function specified by function handle func and passes elements from arrays A1,...,An, where n is the number of inputs to function func. Output arrays B1,...,Bm, where m is the number of outputs from function func, contain the combined outputs from the function calls. The ith iteration corresponds to the syntax [B1(i),...,Bm(i)] = func(A1{i},...,An{i}). The arrayfun function does not perform the calls to function func in a specific order.

It looks like this works in Matlab but not in Octave. Am I correct that this generalization cannot be performed using arrayfun in Octave? Is there some more clever way to achieve this without resorting to unvectorized loops?

For reference, here is my Octave result:

octave:5> nums
nums =

@(c) ([c, c + 2, c + 4])

octave:6> arrayfun(nums,[1,2,3])
error: cellfun: expecting all values to be scalars for UniformOutput = true
error: called from:
error: /opt/local/share/octave/3.2.4/m/general/arrayfun.m at line 168, column 21
octave:6>

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
djechlin
  • 59,258
  • 35
  • 162
  • 290
  • why do you insist on `arrayfun`? try simple `for` loop and be done with it. – Shai Dec 10 '13 at 17:09
  • @Shai http://en.wikipedia.org/wiki/Vectorization_(parallel_computing) – djechlin Dec 10 '13 at 17:53
  • @djechlin: http://stackoverflow.com/questions/12522888/arrayfun-can-be-significantly-slower-than-an-explicit-loop-in-matlab-why – Daniel Dec 10 '13 at 17:57
  • @DanielR that answer is for Matlab and the OP has explicitly stated he's using Octave. Advising him to use a for loop there is no no. – carandraug Dec 11 '13 at 11:57

2 Answers2

3

Use arrayfun to apply the function, nums to an array [1,2,3]

CellArray = arrayfun(nums, [1,2,3], "UniformOutput", false);

That gives you a cell array. If you want the answer in a matrix, use cell2mat

cell2mat(CellArray);

If your actual nums is more complicated, then we'll need a better example of it to suggest a solution.

Charity Leschinski
  • 2,886
  • 2
  • 23
  • 40
  • `arrayfun` with `"UniformOutput", false` returns a "row" cell array (i.e. along the 2nd dimension). And the outputs of `nums` are also row vectors (i.e. along the 2nd dimension too). The `cell2mat` concatenates them as a long row vector. To obtain a matrix, first reshape the cell array to be along the expected concatenation dimension: `cell2mat( reshape(CellArray, numel(CellArray), 1) )` – ederag Sep 28 '14 at 10:17
  • You can also simply transpose the vector `arrayfun(nums, [1,2,3].', "UniformOutput", false);` to have a column cell array. Note the transpose of [1,2,3]! – Jan Nov 16 '20 at 12:56
1

The error already suggests how to solve the problem:

arrayfun(nums,[1,2,3],'UniformOutput',false)

There is no difference between Matlab and Octave.

Matlab:

>> nums=@(c) ([c, c + 2, c + 4])

nums = 

    @(c)([c,c+2,c+4])

EDU>> arrayfun(nums,[1,2,3])
Error using arrayfun
Non-scalar in Uniform output, at
index 1, output 1.
Set 'UniformOutput' to false.

>> arrayfun(nums,[1,2,3],'UniformOutput',false)

ans = 

  Columns 1 through 2

    [1x3 double]    [1x3 double]

  Column 3

    [1x3 double]

Octave:

octave:1> nums=@(c) ([c, c + 2, c + 4])
nums =

@(c) ([c, c + 2, c + 4])

octave:2> arrayfun(nums,[1,2,3])
error: arrayfun: all values must be scalars when UniformOutput = true
octave:2> arrayfun(nums,[1,2,3],'UniformOutput',false)
ans = 
{
  [1,1] =

     1   3   5

  [1,2] =

     2   4   6

  [1,3] =

     3   5   7

}
octave:3> 

If your function is really that simple, I suggest to use:

nums([1,2,3]')
Daniel
  • 36,610
  • 3
  • 36
  • 69
  • Unfortunately my actual `nums` is a bit more complicated. It's more like `nums = @(c)(other_function(c, curried_argument))`. I think that's why your last solution breaks. Yes, I could disable UniformOutput and transform the output which is maybe the most elegant thing to do, but still researching. – djechlin Dec 11 '13 at 02:22
  • @djechlin then, if possible, you should consider writing the `other_function` as to accept vectors. – carandraug Dec 11 '13 at 11:58
  • @carandraug I don't own it. (It's `fmincg`.) – djechlin Dec 11 '13 at 13:44