rational_choice 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c062495abe33854b1fdda0b37fe663d2f53f1036b2d79c6491577fe06b645b90
4
- data.tar.gz: 5155200d59d02441f02c2500adab6676cfe45a294c1319adea2670eedf7cb8cc
3
+ metadata.gz: 7ae25e7a180ad02a6837d0e085639330ad54577f880211ea5ed9b8bdcbfd24ad
4
+ data.tar.gz: 9415891d43f63156400471ab1ff71dc388cc76fa30943e5c2dfaf4337cb07d81
5
5
  SHA512:
6
- metadata.gz: 3de02bef19b681e9467383fd86e9653813201c4a0af91bc9f9efabb60b1c5fd4265ddc93d004aa9bf90cfe66fdae356a02481d084ce014182ce53e3baf2381ea
7
- data.tar.gz: b76b864d7bda067426bcd555def66fecd57a5652dfd12426fb544e6d6ae955e24899def8cb74710e596797f0de25043eb56887336f19d44037f53e9b2053a3cf
6
+ metadata.gz: 598b15197987bf19787f9affafde489cf2fbb8b8b0260b36b16f93d9d23eee53d10548170dc719c4ef49e37d12568a151e2854c187708013bb99a77678b7f0a1
7
+ data.tar.gz: d6ffe67d2879780a6418fc722bc19a9bfd70b9def2fbd83826dccc077b4ebef54f47531400a6c93e35a9ea8565e2bd03edb24e8f4175d413d5568189f6ac9a1d
@@ -1,7 +1,7 @@
1
1
  # Tiny fuzzy-logic gate for making choices based on a continuum of permitted values
2
2
  # as opposed to a hard condition.
3
3
  module RationalChoice
4
- VERSION = '2.0.1'
4
+ VERSION = '2.1.0'
5
5
 
6
6
  # Gets raised when a multidimensional choice has to be made with a wrong number
7
7
  # of values versus the number of dimensions
@@ -16,9 +16,11 @@ module RationalChoice
16
16
  #
17
17
  # @param false_at_or_below[#to_f, #<=>] the lower bound, at or below which the value will be considered false
18
18
  # @param true_at_or_above[#to_f, #<=>] the upper bound, at or above which the value will be considered true
19
- def initialize(false_at_or_below:, true_at_or_above:)
19
+ # @param random[Random] the RNG, defaults to a new Random
20
+ def initialize(false_at_or_below:, true_at_or_above:, random: Random.new)
20
21
  raise DomainError, "Bounds were the same at #{false_at_or_below}" if false_at_or_below == true_at_or_above
21
22
 
23
+ @random = random
22
24
  @lower, @upper = [false_at_or_below, true_at_or_above].sort
23
25
  @flip_sign = [@lower, @upper].sort != [false_at_or_below, true_at_or_above]
24
26
  end
@@ -55,9 +57,7 @@ module RationalChoice
55
57
  delta = @upper.to_f - @lower.to_f
56
58
  v = (value - @lower).to_f
57
59
  t = (v / delta)
58
-
59
- r = Random.new # To override in tests if needed
60
- r.rand < t
60
+ @random.rand < t
61
61
  else
62
62
  # just seen where it is (below or above)
63
63
  value >= @upper
@@ -79,8 +79,13 @@ module RationalChoice
79
79
  # will be first coerced into one (number of truthy evaluations vs. number of falsey evaluations)
80
80
  # and then a true/false value will be deduced from that.
81
81
  class ManyDimensions
82
- def initialize(*dimensions)
82
+ # Initializes a new Dimension to evaluate values
83
+ #
84
+ # @param dimensions[Array<Dimension>] the dimensions to make a choice over
85
+ # @param random[Random] the RNG, defaults to a new Random
86
+ def initialize(*dimensions, random: Random.new)
83
87
  @dimensions = dimensions
88
+ @random = random
84
89
  raise CardinalityError, '%s has no dimensions to evaluate' % inspect if @dimensions.empty?
85
90
  end
86
91
 
@@ -106,7 +111,7 @@ module RationalChoice
106
111
  evaluations = values.zip(@dimensions).map { |(v, d)| d.choose(v) }
107
112
  num_truthy_choices = evaluations.select { |e| e }.length
108
113
 
109
- Dimension.new(false_at_or_below: 0, true_at_or_above: evaluations.length).choose(num_truthy_choices)
114
+ Dimension.new(false_at_or_below: 0, true_at_or_above: evaluations.length, random: @random).choose(num_truthy_choices)
110
115
  end
111
116
  end
112
117
  end
@@ -39,6 +39,14 @@ describe 'RationalChoice::Dimension' do
39
39
  end
40
40
  end
41
41
 
42
+ describe 'with a given Random' do
43
+ it 'creates a predictable sequence of choices' do
44
+ d = RationalChoice::Dimension.new(false_at_or_below: 0, true_at_or_above: 1, random: Random.new(42))
45
+ choices = (1..10).map { d.choose(0.5) }
46
+ expect(choices).to eq([true, false, false, false, true, true, true, false, false, false])
47
+ end
48
+ end
49
+
42
50
  describe 'with values between thresholds creates a sensible choice distribution' do
43
51
  it 'for 0.5 on a continuum from 0 to 1' do
44
52
  d = RationalChoice::Dimension.new(false_at_or_below: 0, true_at_or_above: 1)
@@ -22,6 +22,16 @@ describe 'RationalChoice::ManyDimensions' do
22
22
  RationalChoice::ManyDimensions.new(one_to_zero, one_to_zero, one_to_zero)
23
23
  }
24
24
 
25
+ it 'accepts a custom seed and uses it to generate predictable choices' do
26
+ r = Random.new(42)
27
+ d1 = RationalChoice::Dimension.new(false_at_or_below: 0, true_at_or_above: 1, random: r)
28
+ d2 = RationalChoice::Dimension.new(false_at_or_below: 0, true_at_or_above: 1, random: r)
29
+ d3 = RationalChoice::Dimension.new(false_at_or_below: 0, true_at_or_above: 1, random: r)
30
+ multi_d = RationalChoice::ManyDimensions.new(d1, d2, d3, random: r)
31
+ choices = (1..10).map { multi_d.choose(0.5, 0.2, 0.6) }
32
+ expect(choices).to eq([false, true, false, true, true, false, true, true, true, false])
33
+ end
34
+
25
35
  it 'returns "true" when all dimensions are at or above upper bound' do
26
36
  10_000.times { expect(md.choose(1, 1, 1)).to eq(true) }
27
37
  10_000.times { expect(md.choose(2, 2, 2)).to eq(true) }
@@ -35,7 +45,7 @@ describe 'RationalChoice::ManyDimensions' do
35
45
  it 'returns "true" in approximately 50% of the cases when all the values are at 0.5' do
36
46
  truthy = 0
37
47
  10_000.times { truthy += 1 if md.choose(0.5, 0.5, 0.5) }
38
- expect(truthy).to be_within(100).of(10_000 / 2)
48
+ expect(truthy).to be_within(200).of(10_000 / 2)
39
49
  end
40
50
 
41
51
  it 'returns "true" in approximately 10% of the cases when all the values are at 0.1' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rational_choice
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov