Strange behaviour of Check
The behaviour we see is due to an optimization that is being applied by the evaluator. Once the evaluator sees that an expression is inert, it assumes that for the remainder of the evaluation the same expression need not be evaluated again. In the case at hand, Inverse[{{1, 2}, {1, 2}}]
is inert since the definitions of Inverse
have determined that the matrix is singular and have declined to transform the expression.
We can use Update to force the evaluator to avoid the optimization:
Do[Print@Check[Inverse[({{1,2},{1,2}})]; "wrong", "right"], {i,2}];
(*
Inverse::sing: Matrix {{1,2},{1,2}} is singular.
right
wrong
*)
Do[Print@Check[Update[]; Inverse[({{1,2},{1,2}})]; "wrong", "right"], {i,2}];
(*
Inverse::sing: Matrix {{1,2},{1,2}} is singular.
right
Inverse::sing: Matrix {{1,2},{1,2}} is singular.
right
*)
Analysis (current as of version 11.3.0)
An expression is called "inert" when there are no applicable definitions which transform it. The key word here is applicable. Definitions can exist, but as long as they are not applicable the expression is treated as inert. In this case of a singular matrix, there does exist at least one definition of Inverse
that is evaluated -- but that definition has a condition that detects the singularity, issues an error message, and then indicates that the definition is not applicable. The optimization is insensitive to whether or not the definition condition has any side-effects (in this case, the issuance of a message).
We can emulate this behaviour of Inverse
ourselves with a simpler example:
f[x_] := 10 x /; If[x >= 0, True, Message[f::ltzero]; False]
f::ltzero = "less than zero!";
We have defined a function which takes a value and multiplies it by ten, but only under the condition that the value is not less than zero. Otherwise, the condition issues an error message and returns False
indicating that the definition does not apply. So:
f[1]
(* 10 *)
f[-1]
(*
>>f::ltzero: less than zero!
f[-1]
*)
With this in place, we can see the action of the optimization:
Table[f[-1], {2}]
(*
>>f::ltzero: less than zero!
{f[-1], f[-1]}
*)
Note how the message is issued only once even though we tried to evaluate f[-1]
twice.
We can use Update
tell the evaluator to make no optimizing assumptions about inert expressions:
Table[Update[]; f[-1], {2}]
Table[f[-1], {2}]
(*
>>f::ltzero: less than zero!
>>f::ltzero: less than zero!
{f[-1], f[-1]}
*)
Now, the expression is evaluated twice and the condition message appears twice.
What About Assert
?
The question observes that wrapping Assert
around the call to Inverse
causes it to behave "correctly". Actually, the call to Assert
is failing because the argument is expected to be True
or False
-- not Inverse[...]
. Assert
issues a message in that case and it is that message that causes Check
to return its second argument. All of this can be seen if we remove Quiet
from the expression:
Reap @ Do[Sow@Check[Assert[Inverse[({{1,2},{1,2}})]];2+2,3+3],{i,2}]
(*
>>Inverse::sing: Matrix {{1,2},{1,2}} is singular.
>>Assert::asrttf: Assertion test Inverse[{{1,2},{1,2}}] evaluated to Inverse[{{1,2},{1,2}}] that is neither True nor False.
>>Assert::asrttf: Assertion test Inverse[{{1,2},{1,2}}] evaluated to Inverse[{{1,2},{1,2}}] that is neither True nor False.
{Null,{{6,6}}}
*)
Notice how the Inverse::sing
message was still only issued once whereas the Assert
-related messages were shown multiple times.
The hard-coded singular matrix only causes Inverse
to generate a message (triggering Check
) in the first iteration of table (or Do
). This doesn't happen with all message generating expressions, e.g. Check[0/0, 3 + 3]
generates a message every iteration.
On[Inverse::sing]
f := Quiet[Check[Inverse[{{1, 2}, {1, 2}}], 3 + 3]]
Table[f, 2]
{6, Inverse[{{1, 2}, {1, 2}}]}
With the matrix as a variable a singular matrix message is generated with every evaluation.
m = {{1, 2}, {1, 2}};
f := Quiet[Check[Inverse[m], 3 + 3]]
Table[f, 2]
{6, 6}