2
$\begingroup$

I want to create a function directly from the output of Maximize. Specifically, I have the following function $f(l,h,d,a)$ that I want to maximize with respect to $a$ subject to some constraints:

f[l_, h_, d_, a_] := (1 - d)*a*(h - a)/(h - l) + (d^2(a + l))/(2(1 + d))

Calling Maximize[{f[l, h, d, a], 0 < l <= a <= h && 0 < d < 1}, {a}] yields a set of conditional expressions for different parameter constellations.

I would like to combine this output quickly and effectively into a new function $f_m(l,h,d)$. To me, the most obvious possibility is to do this:

fm1[l_, h_, d_] := First@Maximize[{f[l, h, d, a], 0 < l <= a <= h  && 0 < d < 1}, {a}]

This works but is slow, presumably because Maximize is evaluated every time the function is called. I have worked around this issue the following way:

m = Maximize[{f[l, h, d, a], 0 < l <= a <= h  && 0 < d < 1}, {a}];
mm = m /. {l -> ll, h -> hh, d -> dd};
fm2[l_, h_, d_] :=
  If[mm[[1, 1, 1, 2]] /. {ll -> l, hh -> h, dd -> d}, 
     mm[[1, 1, 1, 1]] /. {ll -> l, hh -> h, dd -> d},
  If[mm[[1, 1, 2, 2]] /. {ll -> l, hh -> h, dd -> d}, 
     mm[[1, 1, 2, 1]] /. {ll -> l, hh -> h, dd -> d},
  If[mm[[1, 1, 3, 2]] /. {ll -> l, hh -> h, dd -> d}, 
     mm[[1, 1, 3, 1]] /. {ll -> l, hh -> h, dd -> d}, -infinity]]]

This works as well but is pretty inconvenient to do for larger output and most likely not the best way to do this. However, it evaluates much faster:

fm1[1, 6, .8] // AbsoluteTiming
{0.109200, 1.26864}

fm2[1, 6, .8] // AbsoluteTiming
{0., 1.26864}

My question therefore is: Is there a convenient way to transform the output of Maximize into an efficient new function $f_m$ without having to copy and paste expressions and conditions by hand?

Any help is highly appreciated!

$\endgroup$
  • 2
    $\begingroup$ Try using Set instead of SetDelayed for defining fm1 (fm1[l_, h_, d_] = First@Maximize[{f[l, h, d, a],...) $\endgroup$ – Sascha Jan 25 '16 at 18:38
  • 2
    $\begingroup$ Sascha's comment hit the nail on the head: using Set will execute Maximize once only and save the result for future use. Furthermore, since you only seem to care about the maximum value, and not the value of the parameter at the maximum, you may want to use MaxValue instead of Maximise, so you will not need to apply First to the results, and improve the legibility of your code as well. $\endgroup$ – MarcoB Jan 25 '16 at 18:45
3
$\begingroup$

As already stated in my comment, the solution is to use Set (=) for the "function definition" instead of SetDelayed(:=). The reason is that Set evaluates the r.h.s of l.h.s. = r.h.s. (abbreviations for left- and right-hand-side) once and whenever Mathematica encounters l.h.s it gets replaced with the evaluated form of r.h.s. SetDelayed on the other hand reevaluates r.h.s. whenever it encounters l.h.s.

In your example using SetDelayed introduces a performance penalty since the evaluation of Maximizeis costly and it gets evaluated every time you call your "function" - the returned expression (your "function") in this case is the same for both Set and SetDelayed however since your function always evaluates to the same expression given the same input parameters.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Very nice. I would also recommend to look at the InputForm of the First part of the solution. I needed that in order to understand that it was a Piecewise function. $\endgroup$ – Jack LaVigne Jan 25 '16 at 23:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.