15

I admit that I have been reluctant to use Reap and Sow for years and that I used it only when I had no choice with EvaluationMonitor.

That is partly because the documentation was difficult for me and partly because I could not think of any scenario where that would be useful. My impression is that I am not the only one that avoids Reap/Sow constructs.

However, I am noticing that it can be useful. Here are some examples:

Sub expressions

Obtaining a list of sub-expressions :

Reap[expression /. 
  x : pattern :> Sow[x]]

Depending on the pattern that might differ from

Cases[expression,pattern,All]

Example:

Cases[{x, x^2, m[x]*m[y]*e}, m[a_]*m[b_], All]

{}

Reap[{x, x^2, m[x]*m[y]*e} /. x : m[a_]*m[b_] :> Sow[x]][[2, 1]]

{m[x] m[y]}

Substitution for AppendTo.

Compare :

l = {};
Do[AppendTo[l, RandomReal[]], 10^5] // AbsoluteTiming

10.8 seconds

with

l = {};
Reap[Do[Sow[RandomReal[]], 10^5]][[2, 1]]; // AbsoluteTiming

0.06 seconds

AppendTo is slow because it copies the list each time. See the following links for further details on AppendTo:

https://mathematica.stackexchange.com/a/72625/86543

and point 3 and 5 in this answer:

https://mathematica.stackexchange.com/a/29351/86543

See also the following for a way to reproduce and enhance Reap/Sow:

https://mathematica.stackexchange.com/a/244033/86543

EvaluationMonitor or StepMonitor

Nothing much to say other than check out the documentation but I am guilty of not using this to check why FindRoot or Plot is not converging or is slow.

Debugging

I use Echo usually but if one needs to extract a lot of intermediate parts in a code and check how they relate to each other using another code, then copy pasting Echo outputs would be overly complicated.

Also, Reap and Sow could be helpful to check what Map, MapAt, MapAll, SubsetMap is working on especially if one included a level specification. That said, I prefer taking a small example and using Framed

Finding specific terms using functions that take a function as an argument

For example one may have wrote a code that acts only on specific terms using a function argument. A quick way to extract those specific terms is to use Reap and Sow.

Arbitrary depth and position categorization

One can use Reap/Sow with tags in combination with MapAt, MapAll,MapBlock, MapIndexed, ArrayReduce etc to generalize Select and Gather to different depths and positions.

Example 1:

Gather sub-expressions of an expression by LeafCount:

Reap[MapAll[Sow[#, LeafCount[#]] &, 
    Integrate[1/(1 + Tan[x]^20), x]]][[2]] // Map[DeleteDuplicates]

Example 2:

Gather and select columns of a matrix depending on whether the sum of elements of the column is prime:

(
Reap[
    ArrayReduce[
                Sow[{#,PrimeQ@Total@#}
                    ,
                    PrimeQ[Total@#]
                   ]&
        ,
        EchoFunction[MatrixForm]@
        Array[RandomInteger[40]&,{3,4}]
        ,
        1
    ]
][[2]]
//Map[MatrixForm,#,{2,Infinity}]&
)

The matrix:

$$ \left( \begin{array}{cccc} 13 & 13 & 25 & 28 \\ 21 & 26 & 7 & 28 \\ 9 & 35 & 21 & 4 \\ \end{array} \right) $$

The gathered columns:

$$\left( \begin{array}{cc} \{\{13,21,9\},\text{True}\} & \{\{25,7,21\},\text{True}\} \\ \{\{13,26,35\},\text{False}\} & \{\{28,28,4\},\text{False}\} \\ \end{array} \right)$$

If we want to select only the column where the sum of elements is prime than we can add an extra argument True at the end in the Reap command.

I should also mention SelectEquivalents from this answer:

https://stackoverflow.com/a/6245166

but I am not sure how to use it in practice.


What else ?

userrandrand
  • 5,847
  • 6
  • 33
  • 5
    Although I asked on Meta what the reasons for closing are I still do not see why these types of questions should be closed (and in my opinion closing is a bit excessively frequent recently). Yes it is broad and no it is not made to be specific. What are the alternatives and what is the gain/loss ratio for closing ? Reap/Sow gets little attention, asking users to just scroll through all of the posts on stack exchange is not a good alternative in my opinion and will just lead to more people fearful to use Reap/Sow. I fail to see how that is better for the community. – userrandrand Dec 14 '22 at 17:30
  • 4
    I'm no expert but it seems within scope, or at least I've seen similar questions/threads in past that were retained. – Daniel Lichtblau Dec 14 '22 at 17:33
  • 2
    Usually, material like this is presented as a question/answer pair. – bbgodfrey Dec 14 '22 at 17:43
  • 7
    I find such open-ended discussions useful. I often return to and refer my students to https://mathematica.stackexchange.com/questions/18/where-can-i-find-examples-of-good-mathematica-programming-practice and https://mathematica.stackexchange.com/questions/18393/what-are-the-most-common-pitfalls-awaiting-new-users. In general, I respect the opinions of frequent contributors. – Craig Carter Dec 14 '22 at 18:03
  • @bbgodfrey I should move the list of uses/examples for Reap and Sow to an answer and leave only the question ? I have two personal issues with that, 1 : that would double my points and I rather not get my reputation bloated by a single question/answer for which I placed little effort. I am actually already bothered by getting 6 points for a simple question. I can separate the questions and examples more clearly in my question. 2: I feel it more prudent on my behalf to not attempt such an authoritative general answer for such a general question. I will appreciate any useful answer though. – userrandrand Dec 14 '22 at 18:29
  • 1
    If you feel more comfortable with your question/answer as it, stay with it. However structured, it is useful. (+!) – bbgodfrey Dec 14 '22 at 22:54
  • 1
    I just wanted to throw in my two cents on it as an improvement over AppendTo, which seems horrendously slow once you get a list of any size (I'm guessing Mathematica just re-copies the list every time by default?) So if you're going to be AppendTo-ing more than a few hundred times, I would Reap/Sow every time instead. – Trev Dec 15 '22 at 06:29
  • 1
    @Trev Indeed AppendTo copies the lists as lists are immutable in Mathematica here are some references: https://mathematica.stackexchange.com/a/72625/86543, and point 3 and 5 in this answer https://mathematica.stackexchange.com/a/29351/86543 – userrandrand Dec 15 '22 at 06:38
  • @Trev I added those links to the answer and a link that shows how to reproduce and enhance Reap/Sow (by being able to access the data stored at any time) – userrandrand Dec 15 '22 at 06:55
  • @JasonB. Thank you for mentioning why you vote to close. For me, finding sub-expressions with ReplaceAll and Sow and using Sow with tags as a generalized form of Gather and Select can be quite useful to know. There is another usage of Reap and Sow that someone could have included and that I did not which is: – userrandrand Dec 28 '22 at 00:17
  • Reap[FullSimplify[expression,ComplexityFunction->(Sow[#];LeafCount[#] &)] which I saw in another answer and which allows finding some of the steps of FullSimplify. But I did not find a case where it was actually useful to me. – userrandrand Dec 28 '22 at 00:17
  • From a pedagogical standpoint I find it valuable as it shows examples of why one would use Reap/Sow when the documentation is difficult and people do not see the point of these functions : https://mathematica.stackexchange.com/q/181597/86543. Basically I think I would have been rather happy to find a post like this in the past when I avoided Reap/Sow. – userrandrand Dec 28 '22 at 00:20

1 Answers1

4

Ok, I'll bite. Here is one example where I found Reap/Sow useful. It's also an excuse to document my Backtrack function that I find useful but hasn't seen many example of Wolfram Language code implementing it in a most generic form.


Backtracking is a very useful technique for solving combinatorial problems, and Donald Knuth dedicated a substantial part of TAOCP 4A to it. Let's define a simple backtrack solver:

Backtrack[start_, next_, visitQ_, visit_] := Module[{
   level = 1, (* current search level *)
   stack = {{start}}, (* list of states to try at each level *)
   current = {1}, (* 
   list of indexes of states that need to be tried at each level*)
   state (* current state *)},
  While[level > 0,
   If[current[[level]] > Length[stack[[level]]], (* 
    We must backtrack *)
    level--,
    state = stack[[level, current[[level]]]];
    current[[level]]++;
    If[visitQ@state, visit@state];
    With[{nextStates = next@state},
     If[
      Length@nextStates > 0, (* We must go deeper one level *)
      level++;
      If[Length@stack < level, stack = Append[stack, nextStates], 
       stack[[level]] = nextStates];
      If[Length@current < level, current = Append[current, 1], 
       current[[level]] = 1]
      ]]]]]

The function Backtrack uses the following parameters:

  • start - the initial state
  • next - a function that, given the current state, produces a list of next states
  • visitQ - a function that checks whether the current state should be visited
  • visit - a function that visits the current state

It's the most generic version, and we can define more specialized versions with more convenient interface by modifying the visit function. Notice the use of Reap/Sow in BacktrackCollect - I find it idiomatic to use it for data collection, esp. given very non-linear nature of backtracking search.

BacktrackCollect[start_, next_, visitQ_, n_ : -1] := 
 Module[{count = 0, r},
  r = Reap@
    Catch@Backtrack[start, next, 
      visitQ, (Sow[#]; count++; If[count == n, Throw["Enough"]]) &];
  If[Length@r[[2]] > 0, r[[2, 1]], {}]]

BacktrackCount[start_, next_, visitQ_] := Module[{count = 0}, Backtrack[start, next, visitQ, count++ &]; count]


Now let's use our freshly minted BacktrackCollect to solve n-queens problem:

queenProblem[n_] := Module[{next, visitQ},
  next[queens_] := Map[
    Append[queens, #] &,
    Select[
     Complement[Range[n], queens],
     q |-> Module[{m = Length@queens, i},
       AllTrue[Range[m], Abs[queens[[#]] - q] != m + 1 - # &]
       ]]];
  visitQ[queens_] := Length@queens == n;
  {next, visitQ}]

{next, visitQ} = queenProblem[5];

BacktrackCount[{}, next, visitQ] // Timing (* {0.003299, 10} *)

displayQueens[queens_] := Module[{n = Length[queens], board, i}, board = ConstantArray[" ", {n, n}]; For[i = 1, i <= n, i++, board[[queens[[i]], i]] = [BlackQueen]]; Grid[board, Frame -> All, Background -> {Automatic, Automatic, Flatten[Table[{i, j} -> If[EvenQ[i + j], Darker[White], White], {i, n}, {j, n}]]}]]

Multicolumn[displayQueens /@ BacktrackCollect[{}, next, visitQ], 5]

enter image description here

Victor K.
  • 5,146
  • 3
  • 21
  • 34