Using FSCheck Generators

I have a function for generating doubles in the range:

let gen_doublein = fun mx mn -> Arb.generate<float> |> Gen.suchThat ( (>) mx ) |> Gen.suchThat ( (<) mn ) 

and then a function to create an array of 2 of them:

 let gen_params:Gen<double array> = gen { let! x = gen_doublein 0.0 20000.0 let! y = gen_doublein 0.0 2000.0 return [| x;y|] } 

I put:

 static member arb_params = Arb.fromGen gen_params 

in the Generator class and register it. Everything seems OK. To verify that everything is in order, I:

 let f2 (xs:double array) :double= exp (-2.0*xs.[0]) + xs.[1]*exp (-2.0*xs.[0]) let fcheck fn xs = fn xs > 0.0 

then using the array generator 'arrayOfLength':

 Check.Quick (Prop.forAll (arrayOfLength 2) (fcheck f2)) 

works as expected, however:

 Check.Quick (Prop.forAll (Generators.arb_params) (fcheck f2)) 

just starts doing some calculations and never returns. f # guru, please help.

+4
source share
3 answers

I have not tried this, but I think the problem is that the generator generates float values ​​randomly and then checks to see if they match the predicate (range) you specify. This means that it must generate a large number of floats before it (by chance) generates a value that matches.

It would be easier to generate values ​​in the specified range by generating float values ​​in the range [0 .. 1]
and then re-scale them to fit the range you need.

I am not familiar with FsCheck enough, so I don’t know if there is a generator for the floating point range [0 .. 1], but you can start by creating integers and converting them to float:

 let gen_doublein mx mn = gen { let! n = Arb.generate<int> let f = float n / float Int32.MaxValue return mx + (f * (mn - mx)) } 

EDIT I see that you have solved the problem already. I think that the solution I posted may still be relevant to smaller ranges (where the random generator does not give enough matching values ​​soon enough).

+3
source

The problem was that the parameters were incorrect. Tomas's suggestion is good, and there are some helper functions to implement it.

 // Another id function let fd (d:double) = d // Check that it is in bounds let mn=1.0 let mx=5.0 let fdcheck d = (fd d <= mx) && (fd d >= mn) // Run the check with the numbers generated within the bounds Check.Quick (Prop.forAll (Arb.fromGen (Gen.map (fun x-> match x with | _ when Double.IsNaN x -> (mn+mx)/2.0 | _ when x> 1e+17 ->mx | _ when x< -1e17 ->mn | _ -> mn + (mx-mn)*(sin x+1.0)/2.0 ) Arb.generate<double> ) ) fdcheck ) 

Here I have a function that passes the test if the parameter is generated correctly. I’m not sure that Thomas’s idea with integers works because I think that many small integers are generated and therefore the doubles do not explore the domain a lot - but maybe someone who knows FSCheck can enlighten us.

+1
source

Rewritten sample from @ b1g3ar5 in this way

 let mapRangeNormal (min : float<_>, max : float<_>) x = match x with | _ when Double.IsNaN x -> (min + max) / 2.0 | _ when Double.IsPositiveInfinity x -> max | _ when Double.IsNegativeInfinity x -> min | _ -> min + (max - min) * (sin x + 1.0) / 2.0 let mapRangeUniform (min : float<_>, max : float<_>) x = match x with | _ when Double.IsNaN x -> (min + max) / 2.0 | _ when Double.IsPositiveInfinity x -> max | _ when Double.IsNegativeInfinity x -> min | _ when x < 0.0 -> let newRange = max - min min - x * (newRange / Double.MaxValue) - newRange / 2.0 | _ -> let newRange = max - min min + x * (newRange / Double.MaxValue) + newRange / 2.0 
+1
source

Source: https://habr.com/ru/post/1402476/


All Articles