Thread overview
Error while generate DNA with uniform()
Sep 03, 2022
Salih Dincer
Sep 03, 2022
Ali Çehreli
Sep 03, 2022
Salih Dincer
Sep 03, 2022
Salih Dincer
Sep 03, 2022
rassoc
Sep 03, 2022
Ali Çehreli
September 03, 2022

Hi All,

We discovered a bug yesterday and reported it:

https://forum.dlang.org/thread/mailman.1386.1662137084.31357.digitalmars-d-bugs@puremagic.com

You know, there is generate() depend to std.range. It created the error when we use it with the value of an enum. Which get their values from an enum DNA, we have 4 members that we want to generate 32 pieces randomly like this:

import std;
void main()
{
  enum DNA { timin = 84,
             sitozin = 67,
             guanin = 71,
             adenin = 65
           }
  char[] gene;
  enum n = 32;
  auto range = generate!(() => uniform(DNA.min, DNA.max)).take(n);/*
  auto preferred = generate!(() =>
                   uniform!"[]"(DNA.min,
                                DNA.max)).take(n);//*/
  // # Alternative Solution:
  foreach (_; 0..n)
  {
    gene ~= uniform!"[]"(DNA.min, DNA.max);
  }
  gene.writeln; // CGACGTGCTTCATCGATAGGAGCACGAGGAGC
  // If the ASCII table matches (capital group 64-95) there should be no problem...
}

If there was no alternative solution, we would generate random numbers between 65 and 84 that have no equivalent in DNA. We want to use "[]" ( closed to the left and right) but preferred version doesn't compile.

Can we solve this issue with our own generate() structure?

SDB@79

September 03, 2022

On 9/3/22 8:09 AM, Salih Dincer wrote:

>

Hi All,

We discovered a bug yesterday and reported it:

https://forum.dlang.org/thread/mailman.1386.1662137084.31357.digitalmars-d-bugs@puremagic.com

You know, there is generate() depend to std.range. It created the error when we use it with the value of an enum. Which get their values from an enum DNA, we have 4 members that we want to generate 32 pieces randomly like this:

import std;
void main()
{
   enum DNA { timin = 84,
              sitozin = 67,
              guanin = 71,
              adenin = 65
            }
   char[] gene;
   enum n = 32;
   auto range = generate!(() => uniform(DNA.min, DNA.max)).take(n);/*
   auto preferred = generate!(() =>
                    uniform!"[]"(DNA.min,
                                 DNA.max)).take(n);//*/

I'm not sure why this doesn't work. First, I would change your enum to this (for correctness and readability):

  enum DNA : char { timin = 'T',
             sitozin = 'C',
             guanin = 'G',
             adenin = 'A'
           }

There is probably a bug in generate when the element type is an enum which somehow makes it const. But what you need anyway is a char, so just return a char. For that, you need to specify the return type, which requires a different kind of function literal:

   auto preferred = generate!(function char() =>
                    uniform!"[]"(DNA.min,
                                 DNA.max)).take(n);

That works.

-Steve

September 03, 2022
On 9/3/22 07:25, Steven Schveighoffer wrote:

> There is probably a bug in generate when the element type is an `enum`
> which somehow makes it const.

Yes, Generator is missing an Unqual:

  https://issues.dlang.org/show_bug.cgi?id=23319

Salih had asked:

>> Can we solve this issue with our own `generate()` structure?

Yes, I did the following to determine that adding Unqual was a solution:

- Copy generate() functions to your source file,

- Copy the Generator struct to your source file,

- Edit the definition of Generator's elem_ member as I hinted in the bug.

Ali

September 03, 2022

On Saturday, 3 September 2022 at 14:25:48 UTC, Steven Schveighoffer wrote:

>

[...] what you need anyway is a char, so just return a char. [...]

Clean-cut, thank you!

It's very clear to me...

import std;
void main()
{
  alias fp = char function() @system;
  enum DNA : char
  {
      timin = 'T',
    sitozin = 'C',
     guanin = 'G',
     adenin = 'A'
  }
  fp getDNA = () => uniform!"[]"(DNA.min,
                                 DNA.max);
  enum n = 30;
  auto genes = generate!getDNA.take(n).array;
  auto unique = genes.uniq.array;
          // AAAACATCATGGTAGGCCTTTCATGCGCTA
  assert(unique.length < n, "no repeat");
  unique.writeln; // ACATCATGTAGCTCATGCGCTA
}

SDB@79

September 03, 2022
On 9/3/22 23:18, Salih Dincer via Digitalmars-d-learn wrote:
> Clean-cut, thank you!
> It's very clear to me...

Nothing major, but instead of `uniform!"[]"(DNA.min, DNA.max)`, you can simply use `uniform!DNA`.

`uniform` considers the whole enum: https://github.com/dlang/phobos/blob/v2.100.1/std/random.d#L2561

"Random variate drawn from the uniform distribution across all possible values of the [...] enum type T."
September 03, 2022

On Saturday, 3 September 2022 at 21:09:09 UTC, Ali Çehreli wrote:

>

Salih had asked:

> >

Can we solve this issue with our own generate() structure?

Yes, I did the following to determine that adding Unqual was a solution:

  • Copy generate() functions to your source file,

  • Copy the Generator struct to your source file,

  • Edit the definition of Generator's elem_ member as I hinted in the bug.

But the bug may be in the templates, not in the structure. Maybe template functionTypeOf(alias func)

For example:

auto gen(alias fun)() {
    auto gen = Gen!fun();
    gen.popFront();
    return gen;
}

struct Gen(alias fn)
{
  import std.traits:
  ReturnType!fn func;

  enum empty = false;
  auto front() { return func; }
  void popFront() { func = fn(); }
}

unittest
{
  import std.random : uniform;
  import std.range : takeExactly;

  enum E {
    O = '0', P, Q, R, S, T, U, V, W, X,
    A = 'A', B, C, D, E, F
  }
  auto range = gen!(function char()  =>
                   uniform!"[]"(E.min, E.max))
                   .takeExactly(15);
  assert(range.to!long(15) < long.max);
}

SDB@79

September 03, 2022
On 9/3/22 14:18, Salih Dincer wrote:

>    uniform!"[]"(DNA.min, DNA.max);

Even cleaner:

  uniform!DNA()

:)

Ali