Использование генераторов FSCheck

У меня есть функция для генерации двойников в диапазоне:

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

а затем функция для создания массива из 2 из них:

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|] }

Я кладу:

static member arb_params = Arb.fromGen  gen_params

в классе Generator и зарегистрируйте его. Все кажется в порядке. Чтобы проверить, что все в порядке, у меня есть:

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

затем с помощью генератора массива 'arrayOfLength':

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

однако работает так, как ожидалось:

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

просто начинает делать какие-то вычисления и никогда не возвращается. гуру f# помогите.


person b1g3ar5    schedule 20.03.2012    source источник
comment
Решено - минимум и максимум где то наоборот - поэтому условия не могут быть выполнены. Извините за потраченное время!   -  person b1g3ar5    schedule 20.03.2012
comment
Не стесняйтесь опубликовать это как ответ и отметить его. fun mx mn обязательно вызовет сюрпризы в будущем :)   -  person Brian    schedule 20.03.2012
comment
Совет: если вы используете Gen.suchThat, рекомендуется сначала поэкспериментировать с использованием оператора импликации ==› в вашем свойстве. Gen.suchThat будет бесконечно пытаться фильтровать результаты — оператор ==› подсчитывает, сколько значений не прошло фильтр, и терпит неудачу, когда достигает порога. Так гораздо проще понять, что происходит.   -  person Kurt Schelfthout    schedule 10.08.2012


Ответы (3)


Я этого не пробовал, но думаю, проблема в том, что генератор случайным образом создает float значений, а затем проверяет, соответствуют ли они указанному вами предикату (диапазон). Это означает, что он должен сгенерировать большое количество чисел с плавающей запятой, прежде чем он (случайно) сгенерирует подходящий.

Было бы проще сгенерировать значения в указанном диапазоне, сгенерировав float значений в диапазоне [0 .. 1]
, а затем повторно масштабировав их, чтобы они соответствовали нужному диапазону.

Я недостаточно знаком с FsCheck, поэтому не знаю, есть ли генератор для диапазона [0 .. 1] с плавающей запятой, но вы можете начать с генерации целых чисел и преобразования их в числа с плавающей запятой:

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

ИЗМЕНИТЬ Я вижу, что вы уже решили проблему. Я думаю, что решение, которое я опубликовал, все еще может быть актуальным для меньших диапазонов (где генератор случайных чисел не дает достаточного количества совпадающих значений достаточно быстро).

person Tomas Petricek    schedule 20.03.2012
comment
Да, я все равно буду использовать это, для эффективности. У меня будет больше параметров и некоторые диапазоны будут меньше. - person b1g3ar5; 20.03.2012
comment
Из любопытства, что делает! в пусть! п = ... в смысле? - person Scott Nimrod; 01.12.2015

Проблема была в том, что параметры были неверными. Предложение Томаса хорошее, и для его реализации есть несколько вспомогательных функций.

// 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
         )

Здесь у меня есть функция, которая проходит тест, если параметр сгенерирован правильно. Я не уверен, что идея Томаса с целыми числами работает, потому что я думаю, что генерируется много маленьких целых чисел, и поэтому двойники не сильно исследуют предметную область, но, возможно, кто-то, кто знает FSCheck, может просветить нас.

person b1g3ar5    schedule 21.03.2012
comment
Зависит от того, каким вы хотите видеть дистрибутив — хотите ли вы, чтобы он больше фокусировался на некоторых конкретных пограничных случаях? Равномерное распределение кажется идеальным, но в случае с FsCheck я думаю, что это редко бывает необходимо или даже желательно. Я обычно выбираю что-то, что эмпирически охватывает пространство ввода и больше фокусируется на некоторых крайних случаях. Я часто использую Prop.collect и друзей для настройки генераторов. С механизмом размера и всеми преобразованиями добиться единообразия на самом деле очень сложно. - person Kurt Schelfthout; 10.08.2012

Переписал образец из @b1g3ar5 таким образом

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
person Andrii    schedule 14.09.2016