Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

NMinimize problem: fct minimized uses FindRoot

392 views
Skip to first unread message

Doug Tinkham

unread,
Dec 10, 2011, 7:30:14 AM12/10/11
to
Hello

I'm using NMinimize and FindMinimum to minimize a function that uses
FindRoot when calculating it's value. The problem is that the equation
that FindRoot is used on uses the variable that is being optimized, and
Mathematica appears to be forcing the variable that is being optimized
to remain symbolic in the FindRoot call, and this leads to recursion and
a recursion limit error.

Rather than post my actual functions that are quite long, I've reduced
my problem to the code below that shows my issue. As you will see,
FindRoot keeps optvar in symbolic form when executing FindRoot. Is there
a way to force Mathematica to use all numerical calculations using
NMinimize or FindMinimum? Is the issue with calculation of the
gradient, which Mathematica wants to do symbolically?

Many thanks.



MyNumFct[optvar_] := Module[{inteq, n},
inteq[x_] := (Sin[x] + 1/2*Cos[x])/optvar;
n = n /. FindRoot[inteq[n], {n, 0.1}];
Return[n + Sin[optvar]];
]

NMinimize[{MyNumFct[var], 0 <= var <= 6}, {var, 4.1}]


DrMajorBob

unread,
Dec 11, 2011, 3:48:46 AM12/11/11
to
1) Never, ever, EVER use Return. In this case, there wasn't even a flimsy
excuse for it.

2) To prevent computing a function with symbolic arguments, use a pattern
on the LHS such as _?NumericQ.

3) You used invalid syntax in the second argument of NMinimize.

4) Use Set, not SetDelayed, whenever possible.

Clear[numFct]
numFct[optvar_?NumericQ] :=
Module[{inteq, n, x}, inteq[x_] = (Sin[x] + 1/2*Cos[x])/optvar;
Sin[optvar] + n /. FindRoot[inteq[n], {n, 0.1}]]

NMinimize[numFct[var], {var, 0, 6}]

{-1.46365, {var -> 4.71239}}

5) Finding a root for (Sin[x] + 1/2*Cos[x])/optvar is the same as finding
a root for Sin[x] + 1/2*Cos[x]:

Clear[numFct]
numFct[optvar_?NumericQ] :=
Module[{inteq, n, x}, inteq[x_] = Sin[x] + 1/2*Cos[x];
Sin[optvar] + n /. FindRoot[inteq[n], {n, 0.1}]]

NMinimize[numFct[var], {var, 0, 6}]

{-1.46365, {var -> 4.71239}}

6) Don't define functions you don't need:

Clear[numFct]
numFct[optvar_?NumericQ] := Module[{x},
Sin[optvar] + x /. FindRoot[Sin[x] + Cos[x]/2, {x, 0.1}]]
NMinimize[numFct[var], {var, 0, 6}]

{-1.46365, {var -> 4.71239}}

7) Optimization and root-finding are uncoupled in this case, so:

Clear[numFct]
numFct[optvar_?NumericQ] := Sin[optvar] + Module[{x},
x /. FindRoot[Sin[x] + Cos[x]/2, {x, 0.1}]]
NMinimize[numFct[var], {var, 0, 6}]

{-1.46365, {var -> 4.71239}}

or

Clear[numFct, x, y]
numFct[x_?NumericQ] = Sin[x] +
y /. FindRoot[Sin[y] + Cos[y]/2, {y, 0.1}];
NMinimize[numFct[x], {x, 0, 6}]

{-1.46365, {x -> 4.71239}}

or even simpler:

Clear[x]
NMinimize[Sin[x], {x, 0, 6}]
First@% + x /. FindRoot[Sin[x] + Cos[x]/2, {x, 0.1}]

{-1., {x -> 4.71239}}

-1.46365

Bobby
--
DrMaj...@yahoo.com

Bob Hanlon

unread,
Dec 11, 2011, 3:51:49 AM12/11/11
to
Since myNumFct uses numeric techniques, restrict it to numeric arguments.

Return is unnecessary here.

Syntax for NMinimize does not include initial estimate.

MyNumFct[optvar_?NumericQ] :=
Module[
{inteq, n},
inteq[x_] := (Sin[x] + Cos[x]/2)/optvar;
n = n /. FindRoot[inteq[n], {n, 0.1}];
n + Sin[optvar]]

NMinimize[{MyNumFct[var], 0 <= var <= 6}, var]

{-1.46365, {var -> 4.71239}}


Bob Hanlon

Doug Tinkham

unread,
Dec 11, 2011, 3:53:51 AM12/11/11
to
Thanks Bobby.

The function I showed with Sin and Cos is meaningless; it was just a
function that reproduced the recursion limit problem I was having. The
actual function would be several pages long, even after simplification.

Why never use Return? As a C++ programmer, it is a habit. I can't find
information saying why one should not. Is there a performance penalty?

Thanks.

--------------------------------------------------
From: "DrMajorBob"
> On Sat, 10 Dec 2011 06:27:05 -0600, Doug Tinkham <dtin...@live.ca> wrote:
>
>> Hello
>>
>> I'm using NMinimize and FindMinimum to minimize a function that uses
>> FindRoot when calculating it's value. The problem is that the equation
>> that FindRoot is used on uses the variable that is being optimized, and
>> Mathematica appears to be forcing the variable that is being optimized
>> to remain symbolic in the FindRoot call, and this leads to recursion and
>> a recursion limit error.
>>
>> Rather than post my actual functions that are quite long, I've reduced
>> my problem to the code below that shows my issue. As you will see,
>> FindRoot keeps optvar in symbolic form when executing FindRoot. Is there
>> a way to force Mathematica to use all numerical calculations using
>> NMinimize or FindMinimum? Is the issue with calculation of the
>> gradient, which Mathematica wants to do symbolically?
>>
>> Many thanks.
>>
>>
>>
>> MyNumFct[optvar_] := Module[{inteq, n},
>> inteq[x_] := (Sin[x] + 1/2*Cos[x])/optvar;
>> n = n /. FindRoot[inteq[n], {n, 0.1}];
>> Return[n + Sin[optvar]];
>> ]
>> NMinimize[{MyNumFct[var], 0 <= var <= 6}, {var, 4.1}]
>>
>>
>
>
> --
> DrMaj...@yahoo.com
>

Oleksandr Rasputinov

unread,
Dec 11, 2011, 4:01:31 AM12/11/11
to
An easy fix: just don't allow MyNumFct to evaluate for symbolic arguments.
Define it with a condition that the argument has to be numeric:

MyNumFct[optvar_?NumericQ] := Module[{inteq, n},
inteq[x_] := (Sin[x] + 1/2*Cos[x])/optvar;
n = n /. FindRoot[inteq[n], {n, 0.1}];
Return[n + Sin[optvar]];
]

Your syntax for NMinimize is also erroneous; if you want to specify
initial values, you have to specify a starting region (with upper and
lower bounds), not just a starting point:

NMinimize[{MyNumFct[var], 0 <= var <= 6}, {var, 4, 5}]

giving:

{-1.46365, {var -> 4.71239}}

Heike Gramberg

unread,
Dec 11, 2011, 3:46:11 AM12/11/11
to
To prevent NMinimize and FindMinimum from evaluating your function
symbolically, you could define MyNumFct according to

Clear[MyNumFct];
MyNumFct[optvar_?NumericQ] :=
Module[{inteq, n}, inteq[x_] := (Sin[x] + 1/2*Cos[x])/optvar;
n = n /. FindRoot[inteq[n], {n, 0.1}];
Return[n + Sin[optvar]];]



Heike.

DrMajorBob

unread,
Dec 12, 2011, 6:45:53 AM12/12/11
to
Andrzej answered far better than I would have, but in general...

I avoid Return, GoTo, Label, Continue, and Break because they encourage
Fortran-like "spaghetti code". I suspect they ALL have scoping issues,
too, as Andrzej pointed out for Return.

In tutorial/LoopsAndControlStructures, Help says, "In general, use of Goto
reduces the degree of structure that can readily be perceived in a
program, and therefore makes the operation of the program more difficult
to understand."

That's putting it mildly, think.

Bobby

On Sun, 11 Dec 2011 05:21:24 -0600, Andrzej Kozlowski <ak...@mimuw.edu.pl>
wrote:

> It does not do in Mathematica what you think it does. In cases like
> yours it is harmless enough for it does nothing at all! At the end of a
> function call Return[x] is exactly the same as x, so you are only
> deluding yourself by using it.
>
> The only real use Of Return is to exit control structures and loops but,
> if not used properly it will cause problems. For example, this will not
> work:
>
> memberQ[x_List, y_] := (Do[If[i == y, Return[True]], {i, x}]; False)
>
> memberQ[{a, b, c}, b]
>
> False
>
> What you actually need to do is:
>
> Clear[memberQ]
>
> memberQ[x_List, y_] := (Do[If[i == y, Return[Return[True]]], {i, x}];
> False)
>
> memberQ[{a, b, c}, b]
>
> True
>
> With a multiple Do loop (but not with, for example, a While loop) you
> will need multiple returns to exit the entire looping structure. The
> only use of Return I can imagine is precisely when you wish to exit a
> single loop inside multiple looping structure (without exiting the
> enclosing ones). In all other cases Throw and Catch are much preferable.
> Using Return as one does in C and Fortran in Mathematica is like using
> French when speaking English - you may well be understood but you are
> deluding yourself if you think you are speaking English.
>
> Andrzej Kozlowski
>>> wrote:
>>>
>>>> Hello
>>>>
>>>> I'm using NMinimize and FindMinimum to minimize a function that uses
>>>> FindRoot when calculating it's value. The problem is that the equation
>>>> that FindRoot is used on uses the variable that is being optimized,
>>>> and
>>>> Mathematica appears to be forcing the variable that is being optimized
>>>> to remain symbolic in the FindRoot call, and this leads to recursion
>>>> and
>>>> a recursion limit error.
>>>>
>>>> Rather than post my actual functions that are quite long, I've reduced
>>>> my problem to the code below that shows my issue. As you will see,
>>>> FindRoot keeps optvar in symbolic form when executing FindRoot. Is
>>>> there
>>>> a way to force Mathematica to use all numerical calculations using
>>>> NMinimize or FindMinimum? Is the issue with calculation of the
>>>> gradient, which Mathematica wants to do symbolically?
>>>>
>>>> Many thanks.
>>>>
>>>>
>>>>
>>>> MyNumFct[optvar_] := Module[{inteq, n},
>>>> inteq[x_] := (Sin[x] + 1/2*Cos[x])/optvar;
>>>> n = n /. FindRoot[inteq[n], {n, 0.1}];
>>>> Return[n + Sin[optvar]];
>>>> ]
>>>> NMinimize[{MyNumFct[var], 0 <= var <= 6}, {var, 4.1}]
>>>>
>>>>
>>>
>>>
>>> --
>>> DrMaj...@yahoo.com
>>>
>>
>


--
DrMaj...@yahoo.com

Oleksandr Rasputinov

unread,
Dec 12, 2011, 6:46:24 AM12/12/11
to
On Sun, 11 Dec 2011 08:53:51 -0000, Doug Tinkham <dtin...@live.ca> wrote:

> Why never use Return? As a C++ programmer, it is a habit. I can't find
> information saying why one should not. Is there a performance penalty?

Return is not meant to be used in this way, although I wouldn't go as far
as to say never to use it.

The purpose of Return in Mathematica is to exit, returning a value, from
an enclosing control structure, but specifically prior to the normal
termination of that control flow or with a different value than would be
yielded otherwise. For example, Do usually returns Null, but inserting
Return into a Do loop can be used to obtain a non-Null return value.

The problems with using Return in other contexts are that (a) if no
enclosing control structure is found then the enclosing Return is not
removed from the returned value; it will appear as Return[val], which is
probably not what you want and (b) in deeply nested evaluations of the
kind that are common in Mathematica, Return used incorrectly can incur a
considerable performance penalty while walking back up the evaluation
stack and trying to find a suitable enclosing control structure (which
ultimately might be misidentified, possibly leading to erroneous results,
or not exist at all, giving Return[val] where val was required).

For example, the erroneous usage:

Block[{a = 1, b = 2}, Return[a]; Return[b]]

gives:

Return[1]

but a correct usage:

Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]

gives (because of the enclosing Do):

1

Andrzej Kozlowski

unread,
Dec 12, 2011, 6:48:57 AM12/12/11
to
> Why never use Return? As a C++ programmer, it is a habit. I can't find
> information saying why one should not. Is there a performance penalty?
>

Andrzej Kozlowski

unread,
Dec 13, 2011, 5:41:11 AM12/13/11
to

On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:

> For example, the erroneous usage:
>
> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>
> gives:
>
> Return[1]
>
> but a correct usage:
>
> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>
> gives (because of the enclosing Do):
>
> 1

This is not quite the whole story. For example, while the above use of Return in Block does not work, this one (in a function call) does:

f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]

f[]

1

Moreover, if you replace the Do loop in your example with a While loop, you will get:

n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]

Return[1]

All of this is purposeful design and can be explained and justified but I don't think it's worth the bother since Mathematica has far superior means of flow control than this clumsy and arcane construct.

Andrzej


Oliver Ruebenkoenig

unread,
Dec 13, 2011, 5:54:13 AM12/13/11
to


On Mon, 12 Dec 2011, Oleksandr Rasputinov wrote:

> On Sun, 11 Dec 2011 08:53:51 -0000, Doug Tinkham <dtin...@live.ca> wrote:
>
>> Why never use Return? As a C++ programmer, it is a habit. I can't find
>> information saying why one should not. Is there a performance penalty?
>
> Return is not meant to be used in this way, although I wouldn't go as far
> as to say never to use it.
>
> The purpose of Return in Mathematica is to exit, returning a value, from
> an enclosing control structure, but specifically prior to the normal
> termination of that control flow or with a different value than would be
> yielded otherwise. For example, Do usually returns Null, but inserting
> Return into a Do loop can be used to obtain a non-Null return value.
>
> The problems with using Return in other contexts are that (a) if no
> enclosing control structure is found then the enclosing Return is not
> removed from the returned value; it will appear as Return[val], which is
> probably not what you want and (b) in deeply nested evaluations of the
> kind that are common in Mathematica, Return used incorrectly can incur a
> considerable performance penalty while walking back up the evaluation
> stack and trying to find a suitable enclosing control structure (which
> ultimately might be misidentified, possibly leading to erroneous results,
> or not exist at all, giving Return[val] where val was required).
>
> For example, the erroneous usage:
>
> Block[{a = 1, b = 2}, Return[a]; Return[b]]

You can "fix" this by using

Block[{a = 1, b = 2}, Return[a, Block]; Return[b]]

but Throw/Catch is probably a better bet.

Oliver

DrMajorBob

unread,
Dec 14, 2011, 6:02:18 AM12/14/11
to
There's a large gap between "explained and justified" versus "worth the
trouble" or even "sensible".

Do not use Return. It isn't worth the spit you'll need to polish it.

Bobby

On Tue, 13 Dec 2011 04:39:04 -0600, Andrzej Kozlowski <ak...@mimuw.edu.pl>
wrote:

>
> On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:
>
>> For example, the erroneous usage:
>>
>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>
>> gives:
>>
>> Return[1]
>>
>> but a correct usage:
>>
>> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>>
>> gives (because of the enclosing Do):
>>
>> 1
>
> This is not quite the whole story. For example, while the above use of
> Return in Block does not work, this one (in a function call) does:
>
> f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]
>
> f[]
>
> 1
>
> Moreover, if you replace the Do loop in your example with a While loop,
> you will get:
>
> n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]
>
> Return[1]
>
> All of this is purposeful design and can be explained and justified but
> I don't think it's worth the bother since Mathematica has far superior
> means of flow control than this clumsy and arcane construct.
>
> Andrzej
>
>


--
DrMaj...@yahoo.com

Oleksandr Rasputinov

unread,
Dec 14, 2011, 6:05:58 AM12/14/11
to
Interesting; this two-argument form of Return seems like it could be quite
useful. However, as far as I can see, this possibility has never been
listed on the main page for Return, nor on those for Break or Continue
(with which a second argument can also be used), even though it has been
allowed since version 4 at the latest. The only documentation that I could
find suggesting that it exists at all is for the message (Return::nofunc)
that one sees after using it incorrectly. Is this likely to change in
future, or has it been left deliberately undocumented to discourage its
use?

Oleksandr Rasputinov

unread,
Dec 14, 2011, 6:09:35 AM12/14/11
to
On Tue, 13 Dec 2011 10:41:11 -0000, Andrzej Kozlowski <ak...@mimuw.edu.pl>
wrote:

>
> On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:
>
>> For example, the erroneous usage:
>>
>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>
>> gives:
>>
>> Return[1]
>>
>> but a correct usage:
>>
>> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>>
>> gives (because of the enclosing Do):
>>
>> 1
>
> This is not quite the whole story. For example, while the above use of
> Return in Block does not work, this one (in a function call) does:
>
> f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]
>
> f[]
>
> 1
>
> Moreover, if you replace the Do loop in your example with a While loop,
> you will get:
>
> n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]
>
> Return[1]
>
> All of this is purposeful design and can be explained and justified but
> I don't think it's worth the bother since Mathematica has far superior
> means of flow control than this clumsy and arcane construct.
>
> Andrzej
>

Thanks for the additional examples. Yes, there are limitations to what
Return (or Break, or Continue) will accept as a control flow construct; I
imagine most people would have suspected that Block does not count but it
is a little surprising to find that While doesn't either.

The documentation for Return also states that "Return[expr] exits control
structures within the definition of a function, and gives the value expr
for the whole function", by which it apparently means that delayed
definitions do count as enclosing control flow, although in apparent
contradiction to the documented behaviour Function does not. (This seems
to be another example of a virtually meaningless statement about functions
in the documentation made on the assumption that the reader is stuck in
the mindset of another language and prefers a loose analogy to the actual
behaviour over a factually accurate account.)

Oliver Ruebenkoenig's reply demonstrates the almost undocumented
two-argument form of Return, the existence of which (I think) suggests
that the list of constructs accepted by Return were not considered
completely indisputable by the designers of Mathematica. However, the
two-argument form still cannot be used inside Compile, where it seems it
would be most useful.

Andrzej Kozlowski

unread,
Dec 15, 2011, 4:52:41 AM12/15/11
to
My view of this is a little different. I think there is a good reason why the While example returns Return[1] rather than just 1. To see my point, look at the example on memberQ I posted earlier. With a Do loop this does not work

memberQ[x_List, y_] := (Do[If[i === y, Return[True]], {i, x}]; False)

Now use the While loop:

Clear[memberQ]

memberQ[x_List, y_] := (i = 1;
While[i <= Length[x], If[x[[i]] === y, Return[True], i++]]; False)

memberQ[{a, b, c}, b]

True

The While loop works fine. Why is that? Well, I think it is exactly for the same reason why my earlier While example (when While was not in the body of a function) "did not work" - i.e. when we got Return[1] instead of 1. This returning Return[something] rather than just something is not the result of some sort of confusion in the design (although it is, no doubt, confusing) but it actually the means by which Return in a While loop gets through all the control flow structures while in a Do loop it only exits the innermost control structure. As I pointed out, to achieve the same result in a Do loop, you will need a double Return:

Clear[memberQ]

memberQ[x_List,
y_] := (Do[If[i == y, Return[Return[True]]], {i, x}]; False)

memberQ[{a, b, c}, b]

True


Andrzej Kozlowski


Oliver Ruebenkoenig

unread,
Dec 15, 2011, 4:57:51 AM12/15/11
to


On Wed, 14 Dec 2011, Oleksandr Rasputinov wrote:

> On Tue, 13 Dec 2011 10:54:13 -0000, Oliver Ruebenkoenig
> <rueb...@wolfram.com> wrote:
>
>>
>> On Mon, 12 Dec 2011, Oleksandr Rasputinov wrote:
>>
>>> For example, the erroneous usage:
>>>
>>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>
>> You can "fix" this by using
>>
>> Block[{a = 1, b = 2}, Return[a, Block]; Return[b]]
>>
>> but Throw/Catch is probably a better bet.
>>
>> Oliver
>>
>
> Interesting; this two-argument form of Return seems like it could be quite
> useful. However, as far as I can see, this possibility has never been
> listed on the main page for Return, nor on those for Break or Continue
> (with which a second argument can also be used), even though it has been
> allowed since version 4 at the latest. The only documentation that I could
> find suggesting that it exists at all is for the message (Return::nofunc)
> that one sees after using it incorrectly. Is this likely to change in
> future, or has it been left deliberately undocumented to discourage its
> use?
>
>

I am not exactly sure, but I think Return has been left as it is since a
change might break too much legacy code. As for why the two argument
Return is not a full-fledged citizen I don't know.

As has been suggested on this thread throw/catch is a better and some say
more modern approach to this.


Oliver Ruebenkoenig

unread,
Dec 15, 2011, 5:06:01 AM12/15/11
to


On Wed, 14 Dec 2011, Oleksandr Rasputinov wrote:

> On Tue, 13 Dec 2011 10:41:11 -0000, Andrzej Kozlowski <ak...@mimuw.edu.pl>
> wrote:
>
>>
>> On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:
>>
>>> For example, the erroneous usage:
>>>
>>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>>
>>> gives:
>>>
>>> Return[1]
>>>
>>> but a correct usage:
>>>
>>> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>>>
>>> gives (because of the enclosing Do):
>>>
>>> 1
>>
>> This is not quite the whole story. For example, while the above use of
>> Return in Block does not work, this one (in a function call) does:
>>
>> f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>
>> f[]
>>
>> 1
>>
>> Moreover, if you replace the Do loop in your example with a While loop,
>> you will get:
>>
>> n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]
>>
>> Return[1]
>>
>> All of this is purposeful design and can be explained and justified but
>> I don't think it's worth the bother since Mathematica has far superior
>> means of flow control than this clumsy and arcane construct.
>>
>> Andrzej
>>
>
> Thanks for the additional examples. Yes, there are limitations to what
> Return (or Break, or Continue) will accept as a control flow construct; I
> imagine most people would have suspected that Block does not count but it
> is a little surprising to find that While doesn't either.
>
> The documentation for Return also states that "Return[expr] exits control
> structures within the definition of a function, and gives the value expr
> for the whole function", by which it apparently means that delayed
> definitions do count as enclosing control flow, although in apparent
> contradiction to the documented behaviour Function does not. (This seems
> to be another example of a virtually meaningless statement about functions
> in the documentation made on the assumption that the reader is stuck in
> the mindset of another language and prefers a loose analogy to the actual
> behaviour over a factually accurate account.)
>
> Oliver Ruebenkoenig's reply demonstrates the almost undocumented
> two-argument form of Return, the existence of which (I think) suggests
> that the list of constructs accepted by Return were not considered
> completely indisputable by the designers of Mathematica. However, the
> two-argument form still cannot be used inside Compile, where it seems it
> would be most useful.
>
>

Just for comepletness sake:

this does work

cf = Compile[{{n, _Integer}},
Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
res]]

Catch[cf[111], cfe]


Related:
http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565

Oliver

W Craig Carter

unread,
Dec 16, 2011, 5:56:00 AM12/16/11
to
Hello,
I didn't understand the rationale for the "do not use Return" maxim, so
I constructed a little experiment:

(****** snip start *******)
(*list of lists of random lengths*)
temp = RandomInteger[{0, 100}, #] & /@ RandomInteger[{1, 100},
100000];

(*artificial examples of using Throw or Return*)

returnExample[x_] := Module[
{max, min},
If[Total[x] < 100, Return[0]];
{max, min} = {Max[x], Min[x]};
max^min
]

throwExample[x_] := Module[
{max, min},
If[Total[x] < 100, Throw[0]];
{max, min} = {Max[x], Min[x]};
max^min
]

(*timings*)
Timing[returnExample /@ temp;]
(*I get: {1.08535, Null}*)

Timing[Catch[throwExample[#]] & /@ temp;]

(*I get: {1.20134, Null}*)

(*******snip end********)

OK, this is artificial, but how do I understand why Return is eschewed
when it appears to be faster and its construction is simpler?

Thanks, Craig


W Craig Carter
Professor of Materials Science, MIT



On Dec 14, 2011, at Wed, Dec 14, 11 ---6:00 AM, DrMajorBob wrote:

> There's a large gap between "explained and justified" versus "worth
the
> trouble" or even "sensible".
>
> Do not use Return. It isn't worth the spit you'll need to polish it.
>
> Bobby
>
> On Tue, 13 Dec 2011 04:39:04 -0600, Andrzej Kozlowski
> --
> DrMaj...@yahoo.com
>


DrMajorBob

unread,
Dec 16, 2011, 5:56:31 AM12/16/11
to
> This returning Return[something] rather than just something is not the
> result of some sort of confusion in the design

Eh. A sow's ear might turn out to be a purse in disguise, but I wouldn't
count on it.

Bobby

On Thu, 15 Dec 2011 03:51:26 -0600, Andrzej Kozlowski <ak...@mimuw.edu.pl>
wrote:

>
> On 14 Dec 2011, at 12:02, Oleksandr Rasputinov wrote:
>
>> On Tue, 13 Dec 2011 10:41:11 -0000, Andrzej Kozlowski
>> <ak...@mimuw.edu.pl>
>> wrote:
>>
>>>
>>> On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:
>>>
>>>> For example, the erroneous usage:
>>>>
>>>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>>>
>>>> gives:
>>>>
>>>> Return[1]
>>>>
>>>> but a correct usage:
>>>>
>>>> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>>>>
>>>> gives (because of the enclosing Do):
>>>>
>>>> 1
>>>
>>> This is not quite the whole story. For example, while the above use of
>>> Return in Block does not work, this one (in a function call) does:
>>>
>>> f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>>
>>> f[]
>>>
>>> 1
>>>
>>> Moreover, if you replace the Do loop in your example with a While loop,
>>> you will get:
>>>
>>> n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]
>>>
>>> Return[1]
>>>
>>> All of this is purposeful design and can be explained and justified but
>>> I don't think it's worth the bother since Mathematica has far superior
>>> means of flow control than this clumsy and arcane construct.
>>>
>>> Andrzej
>>>
>>
>> Thanks for the additional examples. Yes, there are limitations to what
>> Return (or Break, or Continue) will accept as a control flow construct;
>> I
>> imagine most people would have suspected that Block does not count but
>> it
>> is a little surprising to find that While doesn't either.
>
--
DrMaj...@yahoo.com

Oleksandr Rasputinov

unread,
Dec 16, 2011, 5:59:47 AM12/16/11
to
On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
<rueb...@wolfram.com> wrote:

>
> Just for comepletness sake:
>
> this does work
>
> cf = Compile[{{n, _Integer}},
> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
> res]]
>
> Catch[cf[111], cfe]
>
>
> Related:
> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>
> Oliver
>

Well, sort of.

cf // InputForm

reveals the MainEvaluate call (opcode 46):

CompiledFunction[{8, 8., 5468}, {_Integer}, {{2, 0, 0}, {2, 0, 2}}, {{0,
{2, 0, 1}}, {10, {2, 0, 5}}}, {1, 7, 0, 0, 0},
{{6, 1, 2}, {6, 0, 3}, {6, 1, 4}, {3, 7}, {12, 2, 4, 6}, {6, 6, 2}, {25,
5, 2, 0}, {2, 0, 3},
{46, Function[{n}, Throw[0, cfe]], {i, 2, 0, 4, Block}, 2, 0, 0, 6, 0,
17}, {3, 1}, {4, 4, 3, -6}, {1}},
Function[{n}, Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]],
{i, n}]; res]], Evaluate]

so the advantage of the two-argument Throw in this case appears limited
versus, say,

cf = Compile[{{n, _Integer}},
Catch@Module[{res = 0},
Do[res += i; If[res > 10, Throw[0]], {i, n}];
res
]
]

which does the same thing without requiring any MainEvaluate calls.

Also:

cf2 = Compile[{{n, _Integer}},
Catch[
Module[{res = 0},
Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
res
], cfe
]
]

Catch::argx: Catch called with 2 arguments; 1 argument is expected.

cf2 // InputForm

CompiledFunction[{8, 8., 5468}, {_Integer}, {{2, 0, 0}, {3, 0, 0}}, {},
{0, 1, 1, 0, 0},
{{46, Function[{n}, Catch[Module[{res = 0}, Do[res += i; If[res > 10,
Throw[0, cfe]], {i, n}]; res], cfe]], 2, 0, 0, 3, 0, 0}, {1}},
Function[{n}, Catch[Module[{res = 0}, Do[res += i; If[res > 10, Throw[0,
cfe]], {i, n}]; res], cfe]], Evaluate]

which is not compiled at all. So neither Throw/Catch nor Return can accept
two arguments when used in compiled code proper (i.e. not wrapped in
MainEvaluate). The advantage of the latter, of course, is that the
location it returns to is indicated by context without requiring a second
argument to describe it, which is just as well since the locations Return
will or will not return to appear to be somewhat arbitrary choices as you
point out in your StackOverflow posting. I suppose one can use Label/Goto
to replicate the behaviour of two-argument forms in compiled code if
absolutely necessary, although this seems fairly anachronistic.

DrMajorBob

unread,
Dec 16, 2011, 5:59:11 AM12/16/11
to
The third code below is far superior... and not just as a matter of speed:

temp = RandomInteger[{0, 100}, #] & /@ RandomInteger[{1, 100}, 100000];
returnExample[x_] :=
Module[{max, min}, If[Total[x] < 100, Return[0]];
{max, min} = {Max[x], Min[x]};
max^min]
throwExample[x_] := Module[{max, min}, If[Total[x] < 100, Throw[0]];
{max, min} = {Max[x], Min[x]};
max^min]
simple[x_] := If[Total@x < 100, 0, {Max@x, Min@x}]

Timing[returnExample /@ temp;]
Timing[Catch[throwExample[#]] & /@ temp;]
Timing[simple /@ temp;]

{1.61005, Null}

{1.73297, Null}

{0.458204, Null}

Others have suggested Throw and Catch... but I almost never use THOSE,
either.

Bobby
>> On Tue, 13 Dec 2011 04:39:04 -0600, Andrzej Kozlowski
>> <ak...@mimuw.edu.pl>
>> wrote:
>>
>>>
>>> On 12 Dec 2011, at 12:43, Oleksandr Rasputinov wrote:
>>>
>>>> For example, the erroneous usage:
>>>>
>>>> Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>>>
>>>> gives:
>>>>
>>>> Return[1]
>>>>
>>>> but a correct usage:
>>>>
>>>> Do[Block[{a = 1, b = 2}, Return[a]; Return[b]], {1}]
>>>>
>>>> gives (because of the enclosing Do):
>>>>
>>>> 1
>>>
>>> This is not quite the whole story. For example, while the above use of
>>> Return in Block does not work, this one (in a function call) does:
>>>
>>> f[] := Block[{a = 1, b = 2}, Return[a]; Return[b]]
>>>
>>> f[]
>>>
>>> 1
>>>
>>> Moreover, if you replace the Do loop in your example with a While loop,
>>> you will get:
>>>
>>> n = 1; While[n < 10, Block[{a = 1, b = 2}, Return[a]; Return[b]]]
>>>
>>> Return[1]
>>>
>>> All of this is purposeful design and can be explained and justified but
>>> I don't think it's worth the bother since Mathematica has far superior
>>> means of flow control than this clumsy and arcane construct.
>>>
>>> Andrzej
>>>
>>>
>>
>>
>> --
>> DrMaj...@yahoo.com
>>
>


--
DrMaj...@yahoo.com

Oliver Ruebenkoenig

unread,
Dec 17, 2011, 2:46:03 AM12/17/11
to


On Fri, 16 Dec 2011, Oleksandr Rasputinov wrote:

> On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
> <rueb...@wolfram.com> wrote:
>
>>
>> Just for comepletness sake:
>>
>> this does work
>>
>> cf = Compile[{{n, _Integer}},
>> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>> res]]
>>
>> Catch[cf[111], cfe]
>>
>>
>> Related:
>> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>>
>> Oliver
>>
>
> Well, sort of.
>
> cf // InputForm
>
> reveals the MainEvaluate call (opcode 46):

That does not matter - this is evaluated only once; and you want to go
back to the evaluator anyways.

Calls to MainEvaluate in compiled code are evil if they appear in a loop.

Needs["CompiledFunctionTools`"]
CompilePrint[cf]


>
> CompiledFunction[{8, 8., 5468}, {_Integer}, {{2, 0, 0}, {2, 0, 2}}, {{0,
> {2, 0, 1}}, {10, {2, 0, 5}}}, {1, 7, 0, 0, 0},
> {{6, 1, 2}, {6, 0, 3}, {6, 1, 4}, {3, 7}, {12, 2, 4, 6}, {6, 6, 2}, {25,
> 5, 2, 0}, {2, 0, 3},
> {46, Function[{n}, Throw[0, cfe]], {i, 2, 0, 4, Block}, 2, 0, 0, 6, 0,
> 17}, {3, 1}, {4, 4, 3, -6}, {1}},
> Function[{n}, Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]],
> {i, n}]; res]], Evaluate]
>
> so the advantage of the two-argument Throw in this case appears limited
> versus, say,
>
> cf = Compile[{{n, _Integer}},
> Catch@Module[{res = 0},
> Do[res += i; If[res > 10, Throw[0]], {i, n}];
> res
> ]
> ]
>
> which does the same thing without requiring any MainEvaluate calls.

But is less flexible concerning that possibilities where to Catch.

>
> Also:
>
> cf2 = Compile[{{n, _Integer}},
> Catch[
> Module[{res = 0},
> Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
> res
> ], cfe
> ]
> ]
>
> Catch::argx: Catch called with 2 arguments; 1 argument is expected.
>
> cf2 // InputForm

Yes, this seems to me much more of an issue.

I'd cautiously say, that this might be improvable. But I am not sure. I
think the issue is that the second arg is a expr, which the compiler does
not know how to deal with. But perhaps if a "string" type were added then
something like this might work

cf2 = Compile[{{n, _Integer}},
Catch[
Module[{res = 0},
Do[res += i; If[res > 10, Throw[0, "cfe"]], {i, n}];
res
], "cfe"
]
]

But this is speculation.


>
> CompiledFunction[{8, 8., 5468}, {_Integer}, {{2, 0, 0}, {3, 0, 0}}, {},
> {0, 1, 1, 0, 0},
> {{46, Function[{n}, Catch[Module[{res = 0}, Do[res += i; If[res > 10,
> Throw[0, cfe]], {i, n}]; res], cfe]], 2, 0, 0, 3, 0, 0}, {1}},
> Function[{n}, Catch[Module[{res = 0}, Do[res += i; If[res > 10, Throw[0,
> cfe]], {i, n}]; res], cfe]], Evaluate]
>
> which is not compiled at all. So neither Throw/Catch nor Return can accept
> two arguments when used in compiled code proper (i.e. not wrapped in
> MainEvaluate). The advantage of the latter, of course, is that the

But this is only a problem if you want to Catch inside compile. So I think
the way to go is to re-write slow parts of code with compile and then the
control structure in top level. Perhaps some else has another idea?

> location it returns to is indicated by context without requiring a second
> argument to describe it, which is just as well since the locations Return
> will or will not return to appear to be somewhat arbitrary choices as you
> point out in your StackOverflow posting. I suppose one can use Label/Goto
> to replicate the behaviour of two-argument forms in compiled code if
> absolutely necessary, although this seems fairly anachronistic.

Yes, that might be possible - but hm... ;-)

DrMajorBob

unread,
Dec 17, 2011, 2:47:04 AM12/17/11
to
If posters put forth examples where Return was, say, only 1% pointless...
we could have a different discussion. Until that happens, I'll stick to my
current opinion.

(And no, I don't plan to search thousands of archived posts to prove
myself wrong.)

Bobby

On Fri, 16 Dec 2011 06:07:47 -0600, Andrzej Kozlowski <ak...@mimuw.edu.pl>
wrote:

> You are absolutely right, but that is mostly due to the fact that the
> use of Return and Throw in the posted examples was 100% pointless.
>
> It's a very different matter in cases when you really do need to
> interrupt a control structure. If you look in the list archives you will
> find many examples of compiled procedural code posted by Daniel
> Lichtblau where Throw is used and where is going to be a lot harder to
> produce equivalent code that matches the performance and avoids the use
> of Throw, Return or Break.
>
> Andrzej
--
DrMaj...@yahoo.com

Oliver Ruebenkoenig

unread,
Dec 17, 2011, 2:51:40 AM12/17/11
to


On Fri, 16 Dec 2011, Oliver Ruebenkoenig wrote:

>
>
> On Fri, 16 Dec 2011, Oleksandr Rasputinov wrote:
>
>> On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
>> <rueb...@wolfram.com> wrote:
>>
>>>
>>> Just for comepletness sake:
>>>
>>> this does work
>>>
>>> cf = Compile[{{n, _Integer}},
>>> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>>> res]]
>>>
>>> Catch[cf[111], cfe]
>>>
>>>
>>> Related:
>>> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>>>
>>> Oliver
>>>
>>
>> Well, sort of.
>> cf // InputForm
>>
>> reveals the MainEvaluate call (opcode 46):
>
> That does not matter - this is evaluated only once; and you want to go back
> to the evaluator anyways.
>
> Calls to MainEvaluate in compiled code are evil if they appear in a loop.
>
> Needs["CompiledFunctionTools`"]
> CompilePrint[cf]


Ah, and I forgot you can avoid that (insignificant) MainEval call with

cf2 = Compile[{{n, _Integer}},
Module[{res = 0},
Do[res += i; If[res > 10, Internal`CompileError[]], {i, n}];
res],
"RuntimeOptions" -> {"WarningMessages" -> False,
"RuntimeErrorHandler" -> Function[Throw[$Failed, cfe]]}
]


Oliver

Andrzej Kozlowski

unread,
Dec 17, 2011, 2:55:44 AM12/17/11
to

Oleksandr Rasputinov

unread,
Dec 18, 2011, 4:37:59 AM12/18/11
to
On Sat, 17 Dec 2011 07:46:03 -0000, Oliver Ruebenkoenig
<rueb...@wolfram.com> wrote:

>
> On Fri, 16 Dec 2011, Oleksandr Rasputinov wrote:
>
>> On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
>> <rueb...@wolfram.com> wrote:
>>
>>>
>>> Just for comepletness sake:
>>>
>>> this does work
>>>
>>> cf = Compile[{{n, _Integer}},
>>> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>>> res]]
>>>
>>> Catch[cf[111], cfe]
>>>
>>>
>>> Related:
>>> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>>>
>>> Oliver
>>>
>>
>> Well, sort of.
>>
>> cf // InputForm
>>
>> reveals the MainEvaluate call (opcode 46):
>
> That does not matter - this is evaluated only once; and you want to go
> back to the evaluator anyways.

No, I really don't. :) The reason I suggested that a compiled form of the
two-argument Return, Continue, Break etc. might be useful is that these
could be used for more complex (Fortran 90-like, say, versus Fortran
77-like) structured flow control in procedural code. (While Throw is
usually a better choice than Return, I still tend to prefer Break and
Continue in a procedural context over co-opting Throw for these purposes
as well.) But once you Throw using a MainEvaluate call, there is no way to
get back into compiled code except by starting again from the beginning,
which is rather contrary to the purpose of flow control in general. As a
result, Label/Goto is left as the only possible solution, anachronistic as
it may be.

>>
>> Also:
>>
>> cf2 = Compile[{{n, _Integer}},
>> Catch[
>> Module[{res = 0},
>> Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>> res
>> ], cfe
>> ]
>> ]
>>
>> Catch::argx: Catch called with 2 arguments; 1 argument is expected.
>>
>> cf2 // InputForm
>
> Yes, this seems to me much more of an issue.
>
> I'd cautiously say, that this might be improvable. But I am not sure. I
> think the issue is that the second arg is a expr, which the compiler does
> not know how to deal with. But perhaps if a "string" type were added then
> something like this might work
>
> cf2 = Compile[{{n, _Integer}},
> Catch[
> Module[{res = 0},
> Do[res += i; If[res > 10, Throw[0, "cfe"]], {i, n}];
> res
> ], "cfe"
> ]
> ]
>
> But this is speculation.

At the risk of speculating on top of speculation, I don't think this is
correct. The compiler can handle exprs, at least in certain contexts, as
this example demonstrates (where loop can be an arbitrary expr):

cf = Compile[{{x, _Integer, 0}},
Block[{i = x, n = 0},
Label[loop];
n += x;
If[--i > 0, Goto[loop]];
n
]
]

I think the main reason why the compiler cannot deal with the second
argument to Throw/Catch is that it is not handled as an expr but as a
pattern. If this requirement can be relaxed, it's not inconceivable that
the compiler could in future handle a two-argument Throw/Catch block by
expanding it as a macro. But first, it has to get smarter about handling
this situation:

Block[{x},
Block[{},
x = 1;
Goto[exit];
];
x = 2;
Label[exit];
x
]

This works as expected under normal evaluation, but the presence of the
inner Block confuses the compiler into generating an incorrect
MainEvaluate call that makes it fail when compiled.

>
> But this is only a problem if you want to Catch inside compile. So I
> think
> the way to go is to re-write slow parts of code with compile and then the
> control structure in top level. Perhaps some else has another idea?

I agree, but this may not always be possible while retaining reasonable
performance. Thus, as mentioned above, one might have to use Label/Goto
(compiler bugs notwithstanding) or perhaps link in code written in a
procedural language with more versatile flow control, such as Fortran 90.

DrMajorBob

unread,
Dec 19, 2011, 7:17:22 AM12/19/11
to
I see no reason not to write it THIS way:

cf3 = Compile[{{n, _Integer}},
Module[{sum = 0, i = 0}, While[i < n && sum <= 10, i++; sum += i];
If[sum > 10, 0, sum]]]

It's not that I don't know how to write spaghetti code! I probably wrote
more Fortran 5 in 1977-1982 than I've written in all other languages
combined. (Demon, IBM Assembler, PL/1, Simscript, QGERT, SAS, MathCAD,
Mathematica, and others I probably forget.)

But, if there's no reason for a troublesome construct, I plan to avoid it.

Bobby

On Sun, 18 Dec 2011 03:36:23 -0600, Oleksandr Rasputinov
<oleksandr_...@hmamail.com> wrote:

> On Sat, 17 Dec 2011 07:46:03 -0000, Oliver Ruebenkoenig
> <rueb...@wolfram.com> wrote:
>
>>
>> On Fri, 16 Dec 2011, Oleksandr Rasputinov wrote:
>>
>>> On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
>>> <rueb...@wolfram.com> wrote:
>>>
>>>>
>>>> Just for comepletness sake:
>>>>
>>>> this does work
>>>>
>>>> cf = Compile[{{n, _Integer}},
>>>> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i,
>>>> n}];
>>>> res]]
>>>>
>>>> Catch[cf[111], cfe]
>>>>
>>>>
>>>> Related:
>>>> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>>>>
>>>> Oliver
>>>>
>>>
>>> Well, sort of.
>>>
>>> cf // InputForm
>>>
>>> reveals the MainEvaluate call (opcode 46):
>>
>> That does not matter - this is evaluated only once; and you want to go
>> back to the evaluator anyways.
>
> No, I really don't. :) The reason I suggested that a compiled form of the
> two-argument Return, Continue, Break etc. might be useful is that these
> could be used for more complex (Fortran 90-like, say, versus Fortran
> 77-like) structured flow control in procedural code. (While Throw is
> usually a better choice than Return, I still tend to prefer Break and
> Continue in a procedural context over co-opting Throw for these purposes
> as well.) But once you Throw using a MainEvaluate call, there is no way
> to
> get back into compiled code except by starting again from the beginning,
> which is rather contrary to the purpose of flow control in general. As a
> result, Label/Goto is left as the only possible solution, anachronistic
> as
> it may be.
>
>>>
>>> Also:
>>>
>>> cf2 = Compile[{{n, _Integer}},
>>> Catch[
>>> Module[{res = 0},
>>> Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>>> res
>>> ], cfe
>>> ]
>>> ]
>>>
>>> Catch::argx: Catch called with 2 arguments; 1 argument is expected.
>>>
>>> cf2 // InputForm
>>
>> Yes, this seems to me much more of an issue.
>>
>> I'd cautiously say, that this might be improvable. But I am not sure. I
>> think the issue is that the second arg is a expr, which the compiler
>> does
>> not know how to deal with. But perhaps if a "string" type were added
>> then
>> something like this might work
>>
>> cf2 = Compile[{{n, _Integer}},
>> Catch[
>> Module[{res = 0},
>> Do[res += i; If[res > 10, Throw[0, "cfe"]], {i, n}];
>> res
>> ], "cfe"
>> ]
>> ]
>>
>> But this is speculation.
>
>> But this is only a problem if you want to Catch inside compile. So I
>> think
>> the way to go is to re-write slow parts of code with compile and then
>> the
>> control structure in top level. Perhaps some else has another idea?
>
> I agree, but this may not always be possible while retaining reasonable
> performance. Thus, as mentioned above, one might have to use Label/Goto
> (compiler bugs notwithstanding) or perhaps link in code written in a
> procedural language with more versatile flow control, such as Fortran 90.
>


--
DrMaj...@yahoo.com

Oliver Ruebenkoenig

unread,
Dec 19, 2011, 7:22:59 AM12/19/11
to


On Sun, 18 Dec 2011, Oleksandr Rasputinov wrote:

> On Sat, 17 Dec 2011 07:46:03 -0000, Oliver Ruebenkoenig
> <rueb...@wolfram.com> wrote:
>
>>
>> On Fri, 16 Dec 2011, Oleksandr Rasputinov wrote:
>>
>>> On Thu, 15 Dec 2011 10:06:01 -0000, Oliver Ruebenkoenig
>>> <rueb...@wolfram.com> wrote:
>>>
>>>>
>>>> Just for comepletness sake:
>>>>
>>>> this does work
>>>>
>>>> cf = Compile[{{n, _Integer}},
>>>> Module[{res = 0}, Do[res += i; If[res > 10, Throw[0, cfe]], {i, n}];
>>>> res]]
>>>>
>>>> Catch[cf[111], cfe]
>>>>
>>>>
>>>> Related:
>>>> http://stackoverflow.com/questions/8184548/do-return-behave-differently-in-compile-why/8194565#8194565
>>>>
>>>> Oliver
>>>>
>>>
>>> Well, sort of.
>>>
>>> cf // InputForm
>>>
>>> reveals the MainEvaluate call (opcode 46):
>>
>> That does not matter - this is evaluated only once; and you want to go
>> back to the evaluator anyways.
>
> No, I really don't. :) The reason I suggested that a compiled form of the

Hm, I think we have a different opinion here: If the Catch is not withing
the compile, then I think you want to return to the interpreter, if the
catch is within the compile then you do not.

> two-argument Return, Continue, Break etc. might be useful is that these
> could be used for more complex (Fortran 90-like, say, versus Fortran
> 77-like) structured flow control in procedural code. (While Throw is
> usually a better choice than Return, I still tend to prefer Break and
> Continue in a procedural context over co-opting Throw for these purposes
> as well.) But once you Throw using a MainEvaluate call, there is no way to
> get back into compiled code except by starting again from the beginning,
> which is rather contrary to the purpose of flow control in general. As a

I think that depends on what you want to do.
The compiler can not handle exprs; the compiler can handle a sub set of
exprs.

> this example demonstrates (where loop can be an arbitrary expr):
>
> cf = Compile[{{x, _Integer, 0}},
> Block[{i = x, n = 0},
> Label[loop];
> n += x;
> If[--i > 0, Goto[loop]];
> n
> ]
> ]

But in this case the label is transformed into something that the
compiler can handle - an integer.

Another issue is that say Catch could deal with an integer label, how
would that be transformed if CompilationTarget ->C is given. Maybe there
is an easy solution, I am not an expert here.

>
> I think the main reason why the compiler cannot deal with the second
> argument to Throw/Catch is that it is not handled as an expr but as a
> pattern. If this requirement can be relaxed, it's not inconceivable that

That is exactly what I was trying to say, either by a "string" or by an
integer - which would be easier since that is something the compiler
already understands.

> the compiler could in future handle a two-argument Throw/Catch block by
> expanding it as a macro. But first, it has to get smarter about handling
> this situation:
>
> Block[{x},
> Block[{},
> x = 1;
> Goto[exit];
> ];
> x = 2;
> Label[exit];
> x
> ]
>
> This works as expected under normal evaluation, but the presence of the
> inner Block confuses the compiler into generating an incorrect
> MainEvaluate call that makes it fail when compiled.
>

Without having thought about this issue too much, it strikes me as
tricky. (Well it is not a rainy afternoon project) The interpreter needs
to propagate it's state to the compiler.

>>
>> But this is only a problem if you want to Catch inside compile. So I
>> think
>> the way to go is to re-write slow parts of code with compile and then the
>> control structure in top level. Perhaps some else has another idea?
>
> I agree, but this may not always be possible while retaining reasonable
> performance. Thus, as mentioned above, one might have to use Label/Goto
> (compiler bugs notwithstanding) or perhaps link in code written in a
> procedural language with more versatile flow control, such as Fortran 90.
>

You certainly have the option to link in other code, but I think
depending on the problem at hand it perhaps could be reformulated such
that such a control flow would be avoidable. Really, I in my work have
never needed such a control structure.

Oliver

Oleksandr Rasputinov

unread,
Dec 20, 2011, 3:07:36 AM12/20/11
to
On Mon, 19 Dec 2011 12:17:22 -0000, DrMajorBob <btr...@austin.rr.com>
wrote:

> I see no reason not to write it THIS way:
>
> cf3 = Compile[{{n, _Integer}},
> Module[{sum = 0, i = 0}, While[i < n && sum <= 10, i++; sum += i];
> If[sum > 10, 0, sum]]]

The point of the example is to demonstrate that the compiler can handle
arbitrary exprs when they appear as labels and thus that there is no
reason in principle the two-argument form of Throw/Catch could not be
compiled as long as the tag was not to be treated as a pattern. I thought
it would be helpful to demonstrate this with a program that actually does
something, albeit something trivial (i.e. squaring a number) accomplished
in a way that I sincerely hope nobody would use in practice. Perhaps this
makes the original intent clearer:

With[{loop = Plot[Sin[x], {x, -Pi, Pi}]},
cf = Compile[{{x, _Integer, 0}},
Block[{i = x, n = 0}, Label[loop];
n += x;
If[--i > 0, Goto[loop]];
n
]
]
]

> It's not that I don't know how to write spaghetti code! I probably wrote
> more Fortran 5 in 1977-1982 than I've written in all other languages
> combined. (Demon, IBM Assembler, PL/1, Simscript, QGERT, SAS, MathCAD,
> Mathematica, and others I probably forget.)
>
> But, if there's no reason for a troublesome construct, I plan to avoid
> it.

So have you now changed your mind about Throw/Catch to consider it (or at
least its two-argument form) troublesome and/or leading to spaghetti code?
When the only viable alternative (as things stand) is Label/Goto, isn't it
the lesser of two evils?

DrMajorBob

unread,
Dec 21, 2011, 6:54:13 AM12/21/11
to
> So have you now changed your mind about Throw/Catch to consider it (or at
> least its two-argument form) troublesome and/or leading to spaghetti
> code?

I didn't change my mind at all. I rarely do, as some may have noticed.

Throw/Catch didn't Compile, did it? So the combination of Throw/Catch and
Compile is troublesome. GoTo and Label encourage spaghetti code. My
alternative avoids both.

(Compile's success or failure seems unpredictable in general, so I don't
use THAT very much, either. I rarely need anything to run in nanoseconds,
when microseconds will do.)

Bobby
--
DrMaj...@yahoo.com

Oliver Ruebenkoenig

unread,
Dec 22, 2011, 4:30:55 AM12/22/11
to


On Wed, 21 Dec 2011, DrMajorBob wrote:

>> So have you now changed your mind about Throw/Catch to consider it (or at
>> least its two-argument form) troublesome and/or leading to spaghetti
>> code?
>
> I didn't change my mind at all. I rarely do, as some may have noticed.
>
> Throw/Catch didn't Compile, did it? So the combination of Throw/Catch and
> Compile is troublesome. GoTo and Label encourage spaghetti code. My
> alternative avoids both.
>
> (Compile's success or failure seems unpredictable in general, so I don't
> use THAT very much, either. I rarely need anything to run in nanoseconds,
> when microseconds will do.)
>

That is not true in V8 since you can either inspect the output with
CompilePrint or use the options that report on various issues that may
come up during compile.

Oliver
0 new messages