7

I have this loop as part of a larger program (it's open source, and it can be downloaded and run within comma, the configuration is there):

 my $promise = start react whenever $channel-one -> @crew {
            Algorithm::Evolutionary::LogTimelineSchema::Evolutionary.log: {
                my %fitness-of;
                Algorithm::Evolutionary::LogTimelineSchema::Frequencies
                        .log( :@crew );
                my @unpacked-pop = generate-by-frequencies( $population-size, @crew );
                my $population = evaluate( population => @unpacked-pop,
                                            :%fitness-of,
                                            evaluator => &leading-ones);
                Algorithm::Evolutionary::LogTimelineSchema::GenerationsStart
                        .log( :population-size(@unpacked-pop.elems),
                              :distinct-elements( %fitness-of.keys.elems) );
                my atomicint $count = 0;
                while ($count⚛++ < $generations) &&
                        (best-fitness($population) < $max-fitness) {
                    LAST {
                        if best-fitness($population) >= $max-fitness {
                            Algorithm::Evolutionary::LogTimelineSchema::SolutionFound
                                    .log(
                                    {
                                        id => $*THREAD.id,
                                        best => best-fitness($population),
                                        found => True,
                                        finishing-at => DateTime.now.Str
                                    }
                                    );

                            say "Solution found" => $evaluations;
                            Algorithm::Evolutionary::LogTimelineSchema::SolutionFound
                                    .log( :$evaluations );
                            $channel-one.close;
                        } else {
                            say "Emitting after $count generations in thread ",
                                    $*THREAD.id, " Best fitness ",best-fitness($population);
                            if  $count < $generations {
                                Algorithm::Evolutionary::LogTimelineSchema::Weird
                                        .log(  id => $*THREAD.id,
                                                best => best-fitness($population),
                                                :$count,
                                                :population-size(@unpacked-pop.elems),
                                                :distinct-elements( %fitness-of.keys.elems)
                                        );
                            } else {
                                Algorithm::Evolutionary::LogTimelineSchema::Events.log(
                                        id => $*THREAD.id,
                                        best => best-fitness($population),
                                        :$count
                                        );
                            }
                            $to-mix.send( frequencies-best($population, 8) );
                        }
                    };
                    $population = generation( :$population, :%fitness-of,
                            evaluator => &leading-ones,
                            :$population-size
                            );
                    $evaluations += $population.elems;
                }
                Algorithm::Evolutionary::LogTimelineSchema::Generations
                        .log( :generations($count),
                              individuals => %fitness-of.keys.elems);
                $evaluations;
            };
        };

Check out the loop conditions:

                while ($count⚛++ < $generations) &&
                        (best-fitness($population) < $max-fitness) {

$count is a local variable that, besides, is atomic, and exceeding that count or achieving the best fitness seem to be the only way out.

Only it's not. The loop ends on its own accord, without meeting any of the conditions there. I have set out two different log events, "Weird", and "EndRun" (which you can see above), "Weird" is called when the loop ends without reaching either the best fitness or the number of generations; that one is almost always called, as seen in this Comma visualization enter image description here With the (sparse) triangles on the top showing when the loop ends when it should, the purple triangles below marking the "weird" endings. The printed log also shows the same thing:


Emitting after 8 generations in thread 10 Best fitness 48
Mixing in 12
Emitting after 5 generations in thread 7 Best fitness 47
Mixing in 12
Emitting after 2 generations in thread 8 Best fitness 48
Mixing in 12
Emitting after 1 generations in thread 10 Best fitness 48
Mixing in 8

It almost never reaches 16 generations, which is the value of $generations. I'm using Raku 2019.11, but this kind of thing happened before too. I'm not sure I've bumped into a bug, or I'm simply triggering some mechanism I don't really know about. Any idea?

Update: It also crashes from time to time...

/home/jmerelo/.rakudobrew/moar-2020.01/install/bin/rakudo /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6
Use of Nil in numeric context
  in block  at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 68
An operation first awaited:
  in sub MAIN at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 139
  in block <unit> at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 21

Died with the exception:
    A react block:
      in code  at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 55

    Died because of the exception:
        Type check failed in binding to parameter '@chromosome1'; expected Positional but got Any (Any)
          in sub crossover at /home/jmerelo/.rakudobrew/moar-2020.01/install/share/perl6/site/sources/C405508803395DA88D77FC9B113DB20EFC0C1C4C (Algorithm::Evolutionary::Simple) line 96
          in sub produce-offspring at /home/jmerelo/.rakudobrew/moar-2020.01/install/share/perl6/site/sources/C405508803395DA88D77FC9B113DB20EFC0C1C4C (Algorithm::Evolutionary::Simple) line 115
          in sub generation at /home/jmerelo/.rakudobrew/moar-2020.01/install/share/perl6/site/sources/C405508803395DA88D77FC9B113DB20EFC0C1C4C (Algorithm::Evolutionary::Simple) line 149
          in block  at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 107
          in block  at /home/jmerelo/.rakudobrew/moar-2020.01/install/share/perl6/site/sources/0F5CDA46B783643CA4E3D746E2329D0C256CB46B (Log::Timeline) line 95
          in method log at /home/jmerelo/.rakudobrew/moar-2020.01/install/share/perl6/site/sources/0F5CDA46B783643CA4E3D746E2329D0C256CB46B (Log::Timeline) line 90
          in block  at /home/jmerelo/Papers/2020/2020-evostar-concurrent-eas/code/concurrent-ea-leading-ones.p6 line 56

Update 2 Trying to golf it down as requested, I've changed the while to a for loop and it works like a charm. So at least that works for me, but still I'd like to know what's the deal with the tunneling while

Community
  • 1
  • 1
jjmerelo
  • 22,578
  • 8
  • 40
  • 86
  • 2
    Question upvoted though I can't help you at the moment, hopefully just because my coffee hasn't yet kicked in -- though I concurrently suspect it's because I'm a mere mortal, not Jonathan. :) .oO ( If a voluntary documenter of a tech you care about, especially a warm hearted, generous peer and friend, posts an SO question about it **without a [MRE]**, is a smile, upvote and/or effort to produce an MRE in order on the basis it helps the doc and/or friend, or a frown, downvote, and/or grumble? If both responses seem reasonable, how do you convey the latter while you get on with the former? ) – raiph Feb 14 '20 at 11:48
  • 1
    @raiph It's as minimal as it gets... I could have the loop doing something else, but if that's solved for that else, it might not be solved for me. I just realized I put no link to the original source, it's all open source and it can be downloaded and run, including Comma configuration. – jjmerelo Feb 14 '20 at 11:58
  • 1
    I'll give the coffee another few minutes and then take another look but in the meantime, as you struggled to produce an MRE for this question, which of the two strategies documented on the MRE page did you find more fruitful? .oO ( And if you didn't struggle to produce an MRE, I'm forced to wonder if perhaps you've missed the point that it's that very struggle that is the heart of both efficiently understanding/debugging problems such as this and asking questions about them on SO in a manner that's helpful to answerers... ) – raiph Feb 14 '20 at 12:11
  • 1
    @jjmerelo The "larger program" link seems to be to the wrong repository; I don't see any Raku code in that one. – Jonathan Worthington Feb 14 '20 at 12:45
  • @JonathanWorthington Sorry, done. – jjmerelo Feb 14 '20 at 16:30
  • @raiph the problem with concurrency is that the size of the program might be a factor. There are 5 concurrent threads, and the program itself takes a certain amount of time. I can have a loop not do anything for the same time, maybe. But if the solution is not having the program do the same thing, well, it does not solve the problem for me... – jjmerelo Feb 14 '20 at 16:32
  • 1
    `$evaluations` isn't atomic (or incremented atomically) despite the fact its declared outside the critical section and used inside of it. – ugexe Feb 14 '20 at 16:50
  • Thanks, @ugexe, that helps. – jjmerelo Feb 14 '20 at 18:27
  • 1
    slightly off topic - but this was the cool things nag about occam (an early CSP language) - ie. if a parallel program COULD fail, then it ALWAYS would regardless of size, space, etc https://en.m.wikipedia.org/wiki/Occam_(programming_language) – librasteve Feb 15 '20 at 17:14

0 Answers0