nondeterminism 0.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.
data/README ADDED
@@ -0,0 +1,86 @@
1
+ Copyright 2008 James Rosen
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+
16
+
17
+
18
+ Some example use cases:
19
+
20
+
21
+ # accept only half of emails
22
+ 50.percent_of_the_time do
23
+ email.accept!
24
+ end
25
+
26
+
27
+ # accept only half of emails and send nasty
28
+ # responses to the rest
29
+ 50.percent_of_the_time do
30
+ email.accept!
31
+ end.else do
32
+ email.send_nasty_response!
33
+ end
34
+
35
+
36
+ # name should be
37
+ # 'jerome' 30% of the time
38
+ # 'kevin' 40% of the time
39
+ # 'leslie' the rest of the time
40
+
41
+ with_probability(0.30) do
42
+ name = 'jerome'
43
+ end.else_with_probability(0.40) do
44
+ name = 'kevin'
45
+ end.else
46
+ name = 'leslie'
47
+ end
48
+
49
+
50
+ # select a random friend from an Array:
51
+ friend = person.friends.random_element
52
+
53
+
54
+ # call foo between 5 and 8 times (inclusive):
55
+ (5..8).times do
56
+ foo
57
+ end
58
+
59
+
60
+
61
+ Using a custom random probability generator:
62
+ if you want to change the random-probability-generation mechanism, do
63
+ something like:
64
+
65
+ Nondeterminism::probability_generator = Proc.new { ... }
66
+
67
+ or
68
+
69
+ Nondeterminism::probability_generator = my_obj
70
+
71
+ just make sure my_obj responds to #call or #to_proc and only returns
72
+ numbers in [0.0, 1.0]
73
+
74
+
75
+ Using a custom random integer generator:
76
+ if you want to change the random-integer-generation mechanism, do
77
+ something like:
78
+
79
+ Nondeterminism::integer_generator = Proc.new { |low, high| ... }
80
+
81
+ or
82
+
83
+ Nondeterminism::integer_generator = Proc.new { |low, high| ... }
84
+
85
+ just make sure my_obj responds to #to_proc, the proc has #arity = 2
86
+ (or < -2), and only returns numbers in [low, high]
@@ -0,0 +1,52 @@
1
+ require 'nondeterminism/probability/kernel'
2
+ require 'nondeterminism/probability/numeric'
3
+ require 'nondeterminism/array'
4
+ require 'nondeterminism/range'
5
+
6
+ module Nondeterminism
7
+
8
+ DEFAULT_PROBABILITY_GENERATOR = Proc.new { Kernel.rand }
9
+
10
+ @@probability_generator = DEFAULT_PROBABILITY_GENERATOR
11
+
12
+ def self.probability_generator
13
+ @@probability_generator
14
+ end
15
+
16
+ def self.probability_generator=(generator)
17
+ raise ArgumentError.new('generator must respond to #to_proc') unless generator.respond_to?(:to_proc)
18
+ @@probability_generator = generator.to_proc
19
+ end
20
+
21
+ def self.random_probability
22
+ result = probability_generator.call
23
+ raise "generator (#{probability_generator}) generated invalid probability: #{result}" unless result >= 0.0 && result <= 1.0
24
+ result
25
+ end
26
+
27
+
28
+ DEFAULT_INTEGER_GENERATOR = Proc.new { |low, high| Kernel.rand * high + low }
29
+
30
+ @@integer_generator = DEFAULT_PROBABILITY_GENERATOR
31
+
32
+ def self.integer_generator
33
+ @@integer_generator
34
+ end
35
+
36
+ def self.integer_generator=(generator)
37
+ raise ArgumentError.new('generator must respond to #to_proc') unless generator.respond_to?(:to_proc)
38
+ proc = generator.to_proc
39
+ raise ArgumentError.new('generator must have arity == 2 or < -2') unless proc.arity == 2 || proc.arity < -2
40
+ @@integer_generator = proc
41
+ end
42
+
43
+ def self.random_integer(range)
44
+ self.random_integer(range.first, range.last)
45
+ end
46
+
47
+ def self.random_integer(low, high)
48
+ result = integer_generator.call(low, high).to_i
49
+ raise "generator (#{integer_generator}) generated invalid integer: #{result}" unless result >= low && result <= high
50
+ result
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class ArrayTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Nondeterminism::integer_generator = Proc.new { |x, y| x }
8
+ end
9
+
10
+ def test_rand
11
+ assert_equal 'a', ['a', 'b', 'c'].rand
12
+ end
13
+
14
+ def test_rand_when_empty
15
+ assert_nil [].rand
16
+ end
17
+
18
+ end
@@ -0,0 +1,45 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+
5
+ class NoToProc
6
+ end
7
+
8
+ class ToProcArityZero
9
+ def to_proc
10
+ Proc.new { nil }
11
+ end
12
+ end
13
+
14
+ class ToProcArityTwoReturnsFour
15
+ def to_proc
16
+ Proc.new { |x, y| 4 }
17
+ end
18
+ end
19
+
20
+ class ToProcArityTwoReturnsAverage
21
+ def to_proc
22
+ Proc.new { |x, y| ((x + y) / 2).to_i }
23
+ end
24
+ end
25
+
26
+ class IntegerGeneratorTest < Test::Unit::TestCase
27
+
28
+ def test_generator_must_define_to_proc
29
+ assert_raises(ArgumentError) { Nondeterminism::integer_generator = NoToProc.new }
30
+ end
31
+
32
+ def test_generator_must_have_arity_2
33
+ assert_raises(ArgumentError) do
34
+ Nondeterminism::integer_generator = ToProcArityZero.new
35
+ end
36
+ end
37
+
38
+ def test_generator_must_return_integers_in_bounds
39
+ Nondeterminism::integer_generator = ToProcArityTwoReturnsFour.new
40
+ assert_raises(RuntimeError) do
41
+ Nondeterminism::random_integer(5, 9)
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,42 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class PercentProbabilisticTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Nondeterminism::probability_generator = Proc.new { 0.5 }
8
+ end
9
+
10
+ def test_negative_percent_raises_argument_error
11
+ assert_raises(ArgumentError) { -4.percent_of_the_time { nil } }
12
+ end
13
+
14
+ def test_percent_over_100_raises_argument_error
15
+ assert_raises(ArgumentError) { 110.percent_of_the_time { nil } }
16
+ end
17
+
18
+ def test_simple_percent_probability
19
+ i = 0
20
+ 50.percent_of_the_time { i = 1 }
21
+ assert_equal 1, i
22
+
23
+ 49.9.percent_of_the_time { i = 2 }
24
+ assert_equal 1, i
25
+
26
+ 51.0001.percent_of_the_time { i = 3 }
27
+ assert_equal 3, i
28
+ end
29
+
30
+ def test_percent_probability_with_else
31
+ i = 0
32
+ 50.percent_of_the_time { i = 1 }.else { i = 2 }
33
+ assert_equal 1, i
34
+
35
+ 49.9.percent_of_the_time { i = 3 }.else { i = 4 }
36
+ assert_equal 4, i
37
+
38
+ 51.0001.percent_of_the_time { i = 5 }.else { i = 6 }
39
+ assert_equal 5, i
40
+ end
41
+
42
+ end
@@ -0,0 +1,43 @@
1
+ require 'test/unit'
2
+ require 'nondeterminism/probability/event_chain'
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class ProbabilityEventChainTest < Test::Unit::TestCase
6
+
7
+ def new_chain(*args)
8
+ Nondeterminism::Probability::EventChain.new(*args)
9
+ end
10
+
11
+ def test_initialize_probability
12
+ assert_equal 0.0, new_chain.total_probability
13
+ assert_equal 0.2, new_chain(0.2, 0.7).total_probability
14
+ end
15
+
16
+ def test_initialize_random
17
+ assert new_chain.random_value >= 0.0
18
+ assert new_chain.random_value <= 1.0
19
+ assert_equal 0.75, new_chain(0.2, 0.75).random_value
20
+ end
21
+
22
+ def test_completed
23
+ assert_equal false, new_chain(0.6, 0.7).completed?
24
+ assert_equal true, new_chain(0.8, 0.7).completed?
25
+ end
26
+
27
+ def test_else_requires_block
28
+ assert_raises(ArgumentError) { new_chain.else }
29
+ end
30
+
31
+ def test_else_with_probability_requires_block
32
+ assert_raises(ArgumentError) { new_chain.else_with_probability(0.9) }
33
+ end
34
+
35
+ def test_else_else_raises_error
36
+ assert_raises(NoMethodError) { 5.percent_of_the_time { nil }.else { nil }.else { nil } }
37
+ end
38
+
39
+ def test_else_else_with_probability_raises_error
40
+ assert_raises(NoMethodError) { 5.percent_of_the_time { nil }.else { nil }.else_with_probability(0.1) { nil } }
41
+ end
42
+
43
+ end
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+
5
+ class NoToProc
6
+ end
7
+
8
+ class ToProcReturnsNegativeOne
9
+ def to_proc
10
+ Proc.new { -1 }
11
+ end
12
+ end
13
+
14
+
15
+ class ToProcReturnsFour
16
+ def to_proc
17
+ Proc.new { 4 }
18
+ end
19
+ end
20
+
21
+ class ProbabilityGeneratorTest < Test::Unit::TestCase
22
+
23
+ def test_generator_must_define_to_proc
24
+ assert_raises(ArgumentError) { Nondeterminism::probability_generator = NoToProc.new }
25
+ end
26
+
27
+ def test_generator_must_return_integers_in_bounds
28
+ Nondeterminism::probability_generator = ToProcReturnsNegativeOne.new
29
+ assert_raises(RuntimeError) do
30
+ Nondeterminism::random_probability
31
+ end
32
+
33
+ Nondeterminism::probability_generator = ToProcReturnsFour.new
34
+ assert_raises(RuntimeError) do
35
+ Nondeterminism::random_probability
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,28 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class RangeTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Nondeterminism::integer_generator = Proc.new { |x, y| y }
8
+ end
9
+
10
+ def test_n_times
11
+ i = 0
12
+ (6..9).times { i += 1 }
13
+ assert_equal 9, i
14
+ end
15
+
16
+ def test_upper_bound
17
+ i = 0
18
+ (1...8).times { i += 1 }
19
+ assert_equal 7, i
20
+ end
21
+
22
+ def test_negative_value_raises_error
23
+ Nondeterminism::integer_generator = Proc.new { |x, y| x }
24
+ i = 0
25
+ assert_raises(RuntimeError) { (-4..2).times { i += 1 } }
26
+ end
27
+
28
+ end
@@ -0,0 +1,96 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+
4
+ class RatioProbabilisticTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Nondeterminism::probability_generator = Proc.new { 0.5 }
8
+ end
9
+
10
+ def test_negative_ratio_raises_argument_error
11
+ assert_raises(ArgumentError) { with_probability(-2.2) { nil } }
12
+ end
13
+
14
+ def test_ratio_over_one_raises_argument_error
15
+ assert_raises(ArgumentError) { with_probability(1.07) { nil } }
16
+ end
17
+
18
+ def test_simple_ratio_probability
19
+ i = 0
20
+ with_probability(0.3) { i = 1 }
21
+ assert_equal 0, i
22
+
23
+ with_probability(0.5) { i = 1 }
24
+ assert_equal 1, i
25
+
26
+ with_probability(1.0) { i = 10 }
27
+ assert_equal 10, i
28
+ end
29
+
30
+ def test_else
31
+ i = 0
32
+ with_probability(0.3) { i = 1 }.else { i = 2 }
33
+ assert_equal 2, i
34
+
35
+ with_probability(0.5) { i = 3 }.else { i = 4 }
36
+ assert_equal 3, i
37
+
38
+ with_probability(1.0) { i = 5 }.else { i = 6 }
39
+ assert_equal 5, i
40
+ end
41
+
42
+ def test_else_with_probability
43
+ i = 0
44
+ with_probability(0.2) do
45
+ i = 1
46
+ end.else_with_probability(0.2999) do
47
+ i = 2
48
+ end.else_with_probability(0.0002) do
49
+ i = 3
50
+ end.else_with_probability(0.15) do
51
+ i = 4
52
+ end
53
+
54
+ assert_equal 3, i
55
+ end
56
+
57
+ def test_chain_percent_over_100_raises_argument_error
58
+ assert_raises(ArgumentError) do
59
+ with_probability(0.9) do
60
+ nil
61
+ end.else.with_probability(0.09) do
62
+ nil
63
+ end.else.with_probability(0.05) do
64
+ nil
65
+ end
66
+ end
67
+ end
68
+
69
+ def test_long_chain
70
+ i = 0
71
+ with_probability(0.11) do
72
+ i = 1
73
+ end.else_with_probability(0.1) do
74
+ i = 2
75
+ end.else_with_probability(0.1) do
76
+ i = 3
77
+ end.else_with_probability(0.1) do
78
+ i = 4
79
+ end.else_with_probability(0.1) do
80
+ i = 5
81
+ end.else_with_probability(0.1) do
82
+ i = 6
83
+ end.else_with_probability(0.1) do
84
+ i = 7
85
+ end.else_with_probability(0.1) do
86
+ i = 8
87
+ end.else_with_probability(0.1) do
88
+ i = 9
89
+ end.else do
90
+ i = 100000
91
+ end
92
+
93
+ assert_equal 5, i
94
+ end
95
+
96
+ end
@@ -0,0 +1 @@
1
+ require 'nondeterminism'
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nondeterminism
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Rosen
8
+ autorequire: nondeterminism
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-01-30 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: james @nospam@ universal-presence.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - lib/nondeterminism.rb
26
+ - test/array_test.rb
27
+ - test/integer_generator_test.rb
28
+ - test/percent_probability_test.rb
29
+ - test/probability_event_chain_test.rb
30
+ - test/probability_generator_test.rb
31
+ - test/range_test.rb
32
+ - test/ratio_probability_test.rb
33
+ - test/test_helper.rb
34
+ - README
35
+ has_rdoc: true
36
+ homepage:
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 0.9.5
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: A package for nondeterministic and probabilistic programming.
61
+ test_files: []
62
+