4

In Matlab, I have a function handle defined as a vector like this

F = @(x) [... 
coeff1*x(1)*x(4); ...
coeff2*x(3); ... 
coeff3*x(7)*x(3) ...
];

In reality it has 150 rows. I need to extract different subsets the functions in the rows. For example make a new handle from the rows 3:17 of those in F. But I can't just go indexing a handle. Is there a solution?

Edit: I need the subset as a new handle, in other words I can not evaluate the entire F and just select the solution rows.

Thanks in advance.

milez
  • 2,201
  • 12
  • 31
  • There are ways to return only the rows you want but in the background all the rows will be evaluated. In this form, your question is a duplicate of [How can I index a MATLAB array returned by a function without first assigning it to a local variable?](http://stackoverflow.com/questions/3627107/how-can-i-index-a-matlab-array-returned-by-a-function-without-first-assigning-it). All these solutions will only avoid the temporary variable. If you want to evaluate **only** the selected rows, then each row must be a function handle, as shown in @Dan 's answer below. – Hoki Mar 29 '16 at 09:00
  • Yes, I have to do this without evaluating the entire F first. – milez Mar 29 '16 at 09:03
  • Then the answer is you **cannot** (as far as I know). You have to reformulate your `F`. Dan's answer below is where I would start. – Hoki Mar 29 '16 at 09:07

3 Answers3

3

It's a bit messier to create but it might make more sense to have a vector of function handles rather than a function handle that creates a vector:

F = {... 
     @(x)coeff1*x(1)*x(4); ...
     @(x)coeff2*x(3); ... 
     @(x)coeff3*x(7)*x(3) ...
    };

And now you can call

cellfun(@(x)x(y),F(3:17))

or even

F2 = @(y)cellfun(@(x)x(y),F(3:17))

And now you can call

y = rand(10,1)
F2(y)

And only get back rows 3 to 17 of your original F. This is basically just wrapping up loops in shorthand. You need to make sure your input y has the right size or you will get an error (i.e. if y is [1,2] and your line three tries to call y(7) you will get an error)

Mohsen Nosratinia
  • 9,844
  • 1
  • 27
  • 52
Dan
  • 45,079
  • 17
  • 88
  • 157
  • In the first version, will calling F(x) still return the same as in my original version? I have a lot of references to it. Otherwise this looks promising, I will test it. – milez Mar 29 '16 at 09:07
  • @milez no it won't. Test it. You need to use that `F2` syntax for that. I suggest you redeclare your `F` as a cell array like mine called something else (let's say `G`) and then to get `F(x)` to still work do `F = @(y)cellfun(@(x)x(y),G)`. By the way, do all the lines of your `F` return results of the same size? – Dan Mar 29 '16 at 09:14
  • That makes sense. All the lines in my F return a scalar. – milez Mar 29 '16 at 09:17
  • I'm getting an error that my coefficients are undefined, wonder why is that? – milez Mar 29 '16 at 11:05
  • @milez that is impossible for anyone to say without seeing your code. Have you defined your coefficients? – Dan Mar 29 '16 at 11:17
  • Alright it worked fine! I guess the error was introduced from Mohsen's method although I can't see how. Thanks all. – milez Mar 29 '16 at 11:51
2

You can convert your original function to the format used in Dan's answer:

>> G=regexp(func2str(F), ';|\[|\]', 'split')
G = 
    '@(x)'    'coeff1*x(1)*x(4)'    'coeff2*x(3)'    'coeff3*x(7)*x(3)'    ''
>> H=cellfun(@str2func, strcat(G{1}, G(2:end-1)), 'uni', 0)
H = 
    @(x)coeff1*x(1)*x(4)    @(x)coeff2*x(3)    @(x)coeff3*x(7)*x(3)

Now H is a cell array containing the function handles and you can index into that.

Mohsen Nosratinia
  • 9,844
  • 1
  • 27
  • 52
  • This seems very handy, but if I use it, I get an error that coeff1 and others are undefined. Same problem I have with JCKaz's script. Do you know what's wrong? – milez Mar 29 '16 at 15:39
  • @milez, you mean when running these or when evaluating elements of `H`? I have tested in R2016a and R2013b and it works fine. If the problem arises when you evaluate them then `coeff1` should be defined in the workspace. – Mohsen Nosratinia Mar 29 '16 at 16:53
  • The error rises when I proceed with Dan's way after doing your script first. Here is a demo http://stackoverflow.com/questions/36289476/undefined-function-or-variable-after-strcat-in-matlab. In the comments they say this is not supposed to work. – milez Mar 29 '16 at 16:58
1

How about this:

F = @(x)[
     5*x.^2*x*4;
     6*x; 
     12*x.^2*x*3
    ];

newF = getFunHandles(F,2:3);

where getFunHandles works for any arbitrary range, e.g. 3:17

function f = getFunHandles(F, range)
funStr = textscan(func2str(F),'%s','delimiter',';');
funStr = regexprep(funStr{1}, {'@(x)[', ']'}, {'', ''});
newFunStr = strcat('@(x)[',strjoin(funStr(range),';'),']');
f = str2func(newFunStr);
jkazan
  • 1,149
  • 12
  • 29
  • I'm getting a warning that the input to str2func is not a valid function, I also get an index out of bounds error when i try to query for all the functions (1:150) :( – milez Mar 29 '16 at 12:11
  • Try it now (I made a minor change in the function) – jkazan Mar 29 '16 at 12:41
  • Very clever! I modified it to newFunStr = strcat('@(x)[',newFunStr,']'); so the result is a vector like I used to have, which seem to perform better than the cell-versions. Thank you. – milez Mar 29 '16 at 12:50
  • Good to hear! I'm happy to help. If this is your preferred answer, please consider to make this the accepted one. (I will update to include newFunStr = strcat('@(x)[',newFunStr,']'); instead of cell) – jkazan Mar 29 '16 at 12:52
  • It works for the toy example. For my full function with 150 variables I still get the "Index exceeds matrix dimensions" error from line 8 in your function (newFunStr = funStr(range);). I have some zero entries in my F, are they a problem? – milez Mar 29 '16 at 12:56
  • Hmm, could you send your full function? I'd love to solve it. – jkazan Mar 29 '16 at 12:57
  • I only find 43 functions in F – jkazan Mar 29 '16 at 13:24
  • I cannot share the larger one, but this has the same problem. The error I new regularly get is that coefficients are undefined – milez Mar 29 '16 at 13:29
  • Ok, so what I should solve now is so that it works to use 1:44 as range? – jkazan Mar 29 '16 at 13:31
  • The function itself gives no error from 1:43. But what needs to be solved is that the coefficients are not recognized. For example with my code, the first error I get is 'kLeakf' is undefined, although it seems to not be. – milez Mar 29 '16 at 13:34
  • I see, I'll take a look. – jkazan Mar 29 '16 at 13:35
  • I get "Undefined function or variable 'Capost_sine'." Is that a user defined function? – jkazan Mar 29 '16 at 13:35
  • Yes, its in the repository. – milez Mar 29 '16 at 13:36
  • Oh? How strange. I have been debugging and sometimes your function removes the last entry if it is zero. I'll try a reboot and see whats up here. Thanks again. – milez Mar 29 '16 at 13:40
  • No problem. Check my latest update of the function above. I saw that mistake and fixed it before. Also, at line 337, change `original = toc` to `disp('original = ', num2str(toc))`, remove brackets at line 107 and remove `clear all; close all;`in the befinning. Performance went from 0.2763 seconds to 0.078392 on my computer. – jkazan Mar 29 '16 at 13:43
  • That's strange... Where did you implement my code? I can't seem to find it in your code. – jkazan Mar 29 '16 at 13:51
  • I am uploading it as we speak. Check back soon. – milez Mar 29 '16 at 13:53
  • I'll be leaving soon, but I don't make it today, I'll have another look tomorrow ;) – jkazan Mar 29 '16 at 13:54
  • Its in the repo now. I'll be working on it all evening and will check back here, cheers. – milez Mar 29 '16 at 14:00
  • `function f = getFunHandles(F, range) funStr_temp = func2str(F); funStr = textscan(funStr_temp,'%s','delimiter',';'); funStr = funStr{1}; funStr = strrep(funStr,'@(x)[',''); funStr = strrep(funStr,']',''); newFunStr = funStr(range); newFunStr = strjoin(newFunStr,';'); newFunStr = strcat('@(x,kLeakf,kprodAGf,kDAG3f,kGPLC2f,kDAG1f,kDAGPKCf)[',newFunStr,']'); f = str2func(newFunStr); end` And then change line 355 to `sol = ode15s(@(t,x) (A0+A1*Capost(t)+A2*Capost(t).^2)*x+F(x,kLeakf,kprodAGf,kDAG3f,kGPLC2f,kDAG1f,kDAGPKCf)+B*Gluext(t),tspan,x0);` – jkazan Mar 29 '16 at 14:11
  • That seems more like a hack than a solution, I need this to be rather general :s – milez Mar 29 '16 at 15:19
  • I got it working. What I did was change the coefficient parameters in my function to their actual values. Not as maintainable as the other solutions, but the output of your solution is over three times faster than the others, and I happen to need speed here :) Also see this for why it wont work at its current form http://se.mathworks.com/help/matlab/ref/str2func.html#buyd7x5-1 – milez Mar 30 '16 at 13:24
  • Good to hear! Would you like me to update the answer to be correct? – jkazan Mar 30 '16 at 16:14
  • If you do I will accept it :) Could you also remove the references to my specific files and variables? – milez Mar 30 '16 at 16:33
  • Great, thanks! I made it entirely generic now, and removed all references to your material. – jkazan Mar 31 '16 at 05:28