compsci 0.3.1.1 → 0.3.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1ad9a59954ea3377e0e03992dd660fabaf7c65389d633f1e607d3a5b6731ebd
4
- data.tar.gz: 5b893649cae5840c590bf4998429087cf50ad9dc97169a2c3ee1566c86748196
3
+ metadata.gz: 265cce45b9c0791d768d79d890bdfd3b2f3dd0f9d3b5346798274095bd38efa1
4
+ data.tar.gz: c921f58718dc3df31cb89572a9b428cbf9add53e6ed8d7b3f433fb8eb4dde0a4
5
5
  SHA512:
6
- metadata.gz: 64100e3719380956c844e7f9eb3949eae097a6d9426b039d0d224260d4bd18a1e1dd7ad7f147643203e21b3ba95d0109bed198d8e5421c9e3c6c21312665b426
7
- data.tar.gz: c8944161d1ee4b4df7afac5d27bc0801b2953893ea45db03c30ec1e9f5ee74764638464ee1c3d4d3707107370dd547eedb1f62d09cfe9f20f2efb399e462258f
6
+ metadata.gz: 6f7bf484573f12573ed1f3705634aacd3f7c47508df8983e5bfe92908923d4bf9495dc3ae65deb69749fa326185ec08a8233dd2689f4b400db553a65d2272708
7
+ data.tar.gz: c5db4b1e31b10b031dec20a21806fae10853ad7cd9bc62817f374fbef10df4934d1a16c6ea1d9a7d0ebb3c46f9c470077984d983f94755a417a130330d101431
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
- [![Build Status](https://travis-ci.org/rickhull/compsci.svg?branch=master)](https://travis-ci.org/rickhull/compsci)
1
+ [![CI Status](https://github.com/rickhull/compsci/actions/workflows/ci.yaml/badge.svg)](https://github.com/rickhull/compsci/actions/workflows/ci.yaml)
2
2
  [![Gem Version](https://badge.fury.io/rb/compsci.svg)](https://badge.fury.io/rb/compsci)
3
- [![Dependency Status](https://gemnasium.com/rickhull/compsci.svg)](https://gemnasium.com/rickhull/compsci)
4
- [![Security Status](https://hakiri.io/github/rickhull/compsci/master.svg)](https://hakiri.io/github/rickhull/compsci/master)
5
3
 
6
4
  # CompSci
7
5
 
@@ -213,6 +211,8 @@ alphabetical order.
213
211
 
214
212
  ## [`Simplex`](lib/compsci/simplex.rb) class
215
213
 
214
+ ### WORK IN PROGRESS; DO NOT USE
215
+
216
216
  The [Simplex algorithm](https://en.wikipedia.org/wiki/Simplex_algorithm)
217
217
  is a technique for
218
218
  [Linear programming](https://en.wikipedia.org/wiki/Linear_programming).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1.1
1
+ 0.3.2.3
data/compsci.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.homepage = "https://github.com/rickhull/compsci"
7
7
  s.license = "LGPL-3.0"
8
8
 
9
- s.required_ruby_version = "~> 2"
9
+ s.required_ruby_version = "~> 3.0"
10
10
 
11
11
  s.version = File.read(File.join(__dir__, 'VERSION')).chomp
12
12
 
@@ -15,9 +15,11 @@ Gem::Specification.new do |s|
15
15
  s.files += Dir['test/**/*.rb']
16
16
  s.files += Dir['examples/**/*.rb']
17
17
 
18
+ s.add_dependency "matrix", "~> 0.4"
19
+
18
20
  s.add_development_dependency "buildar", "~> 3.0"
19
- s.add_development_dependency "minitest", ">= 5.0"
20
- s.add_development_dependency "rake", ">= 12.3.3" # CVE-2020-8130
21
+ s.add_development_dependency "minitest", "~> 5.0"
22
+ s.add_development_dependency "rake", "~> 12.3" # CVE-2020-8130
21
23
  s.add_development_dependency "flog", "~> 0"
22
24
  s.add_development_dependency "flay", "~> 0"
23
25
  s.add_development_dependency "roodi", "~> 0"
data/lib/compsci/fit.rb CHANGED
@@ -153,5 +153,22 @@ module CompSci
153
153
 
154
154
  return Math.exp(a), b, self.error(xys) { |x| (Math.exp(a) * (x ** b)) }
155
155
  end
156
+
157
+ def self.predict(model, a, b, x)
158
+ case model
159
+ when :constant
160
+ a
161
+ when :logarithmic
162
+ a + b * Math.log(x)
163
+ when :linear
164
+ a + b * x
165
+ when :exponential
166
+ a * Math::E ** (b * x)
167
+ when :power
168
+ a * x ** b
169
+ else
170
+ raise("unknown model: #{model}")
171
+ end
172
+ end
156
173
  end
157
174
  end
@@ -34,8 +34,8 @@ module CompSci::Names::Pokemon
34
34
  # a hash of all the names, keyed by the first letter
35
35
  def self.read_hash(path: DATAFILE)
36
36
  hsh = Hash.new { |h, k| h[k] = [] }
37
- File.open(path).each_line { |l|
38
- hsh[l[0]] << l.chomp
37
+ File.open(path).each_line { |line|
38
+ hsh[line.chr] << line.chomp
39
39
  }
40
40
  hsh
41
41
  end
@@ -82,7 +82,11 @@ class CompSci::Simplex
82
82
  end
83
83
  end
84
84
 
85
- def self.problem(maximize: nil, constraints: [], **kwargs)
85
+ def self.problem(**kwargs)
86
+ self.new(*self.get_params(**kwargs))
87
+ end
88
+
89
+ def self.get_params(maximize: nil, constraints: [], **kwargs)
86
90
  if maximize
87
91
  expr, maximize = maximize, true
88
92
  elsif kwargs[:minimize]
@@ -116,7 +120,7 @@ class CompSci::Simplex
116
120
  a.push cofs
117
121
  b.push rhs
118
122
  }
119
- self.new(c, a, b)
123
+ [c, a, b]
120
124
  end
121
125
 
122
126
  def self.maximize(expression, *ineqs)
@@ -26,35 +26,36 @@ class CompSci::Simplex
26
26
  @max_pivots = DEFAULT_MAX_PIVOTS
27
27
 
28
28
  # Problem dimensions; these never change
29
- @num_non_slack_vars = num_vars
30
- @num_constraints = num_inequalities
31
- @num_vars = @num_non_slack_vars + @num_constraints
29
+ @num_free_vars = num_vars
30
+ @num_basic_vars = num_inequalities
31
+ @total_vars = @num_free_vars + @num_basic_vars
32
32
 
33
33
  # Set up initial matrix A and vectors b, c
34
- @c = c.map { |flt| -1 * flt } + Array.new(@num_constraints, 0)
34
+ # store all input values as Rational (via #rationalize)
35
+ @c = c.map { |flt| -1 * flt.rationalize } + Array.new(@num_basic_vars, 0)
35
36
  @a = a.map.with_index { |ary, i|
36
- if ary.size != @num_non_slack_vars
37
+ if ary.size != @num_free_vars
37
38
  raise ArgumentError, "a is inconsistent"
38
39
  end
39
- # set diagonal to 1 (identity matrix?)
40
- ary + Array.new(@num_constraints) { |ci| ci == i ? 1 : 0 }
40
+ # add identity matrix
41
+ ary.map { |flt| flt.rationalize } +
42
+ Array.new(@num_basic_vars) { |ci| ci == i ? 1 : 0 }
41
43
  }
42
- @b = b
44
+ @b = b.map { |flt| flt.rationalize }
43
45
 
44
- # set initial solution: all non-slack variables = 0
45
- @basic_vars = (@num_non_slack_vars...@num_vars).to_a
46
+ @basic_vars = (@num_free_vars...@total_vars).to_a
46
47
  self.update_solution
47
48
  end
48
49
 
49
50
  # does not modify vector / matrix
50
51
  def update_solution
51
- @x = Array.new(@num_vars, 0)
52
+ @x = Array.new(@total_vars, 0)
52
53
 
53
54
  @basic_vars.each { |basic_var|
54
55
  idx = nil
55
- @num_constraints.times { |i|
56
+ @num_basic_vars.times { |i|
56
57
  if @a[i][basic_var] == 1
57
- idx =i
58
+ idx = i
58
59
  break
59
60
  end
60
61
  }
@@ -78,7 +79,7 @@ class CompSci::Simplex
78
79
  end
79
80
 
80
81
  def current_solution
81
- @x[0...@num_non_slack_vars]
82
+ @x[0...@num_free_vars]
82
83
  end
83
84
 
84
85
  def can_improve?
@@ -121,7 +122,7 @@ class CompSci::Simplex
121
122
  }
122
123
 
123
124
  # update A and B
124
- @num_constraints.times { |i|
125
+ @num_basic_vars.times { |i|
125
126
  next if i == pivot_row
126
127
  r = @a[i][pivot_column]
127
128
  @a[i] = @a[i].map.with_index { |val, j| val - r * @a[pivot_row][j] }
@@ -134,7 +135,7 @@ class CompSci::Simplex
134
135
  def pivot_row(column_ix)
135
136
  min_ratio = nil
136
137
  idx = nil
137
- @num_constraints.times { |i|
138
+ @num_basic_vars.times { |i|
138
139
  a, b = @a[i][column_ix], @b[i]
139
140
  next if a == 0 or (b < 0) ^ (a < 0)
140
141
  ratio = Rational(b, a)
data/lib/compsci/timer.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  module CompSci
2
- module Timer
2
+ class Timer
3
+ SECS_PER_MIN = 60
4
+ MINS_PER_HOUR = 60
5
+ SECS_PER_HOUR = SECS_PER_MIN * MINS_PER_HOUR
6
+
3
7
  # lifted from seattlerb/minitest
4
8
  if defined? Process::CLOCK_MONOTONIC
5
9
  def self.now
@@ -32,5 +36,61 @@ module CompSci
32
36
  }
33
37
  return val, self.since(start) / i.to_f
34
38
  end
39
+
40
+ # YYYY-MM-DD HH::MM::SS.mmm
41
+ def self.timestamp(t)
42
+ t.strftime "%Y-%m-%d %H:%M:%S.%L"
43
+ end
44
+
45
+ # HH::MM::SS.mmm.uuuuuuuu
46
+ def self.elapsed_display(elapsed_ms, show_us: false)
47
+ elapsed_s, ms = elapsed_ms.divmod 1000
48
+ ms_only, ms_fraction = ms.round(8).divmod 1
49
+
50
+ h = elapsed_s / SECS_PER_HOUR
51
+ elapsed_s -= h * SECS_PER_HOUR
52
+ m, s = elapsed_s.divmod SECS_PER_MIN
53
+
54
+ hmsms = [[h, m, s].map { |i| i.to_s.rjust(2, '0') }.join(':'),
55
+ ms_only.to_s.rjust(3, '0')]
56
+ hmsms << (ms_fraction * 10 ** 8).round.to_s.ljust(8, '0') if show_us
57
+ hmsms.join('.')
58
+ end
59
+
60
+ def restart(t = Time.now)
61
+ @start = t
62
+ self
63
+ end
64
+ alias_method :initialize, :restart
65
+
66
+ def timestamp(t = Time.now)
67
+ self.class.timestamp t
68
+ end
69
+
70
+ def timestamp!(t = Time.now)
71
+ puts '-' * 70, timestamp(t), '-' * 70
72
+ end
73
+
74
+ def elapsed(t = Time.now)
75
+ t - @start
76
+ end
77
+
78
+ def elapsed_ms(t = Time.now)
79
+ elapsed(t) * 1000
80
+ end
81
+
82
+ def elapsed_display(t = Time.now)
83
+ self.class.elapsed_display(elapsed_ms(t))
84
+ end
85
+ alias_method :to_s, :elapsed_display
86
+ alias_method :inspect, :elapsed_display
87
+
88
+ def stamp(msg = '', t = Time.now)
89
+ format("%s %s", elapsed_display(t), msg)
90
+ end
91
+
92
+ def stamp!(msg = '', t = Time.now)
93
+ puts stamp(msg, t)
94
+ end
35
95
  end
36
96
  end
data/test/fit.rb CHANGED
@@ -15,14 +15,14 @@ describe Fit do
15
15
  end
16
16
 
17
17
  describe "Fit.sigma" do
18
- it "must answer correctly" do
18
+ it "answers correctly" do
19
19
  expect(Fit.sigma([1, 2, 3])).must_equal 6
20
20
  expect(Fit.sigma([1, 2, 3]) { |n| n ** 2 }).must_equal 14
21
21
  end
22
22
  end
23
23
 
24
24
  describe "Fit.error" do
25
- it "must calculate r^2" do
25
+ it "calculates r^2" do
26
26
  expect(Fit.error([[1, 1], [2, 2], [3, 3]]) { |x| x }).must_equal 1.0
27
27
  expect(Fit.error([[1, 1], [2, 2], [3, 4]]) { |x|
28
28
  x
@@ -36,7 +36,7 @@ describe Fit do
36
36
  # alternate measure. A low slope and r2 for linear fit, maybe.
37
37
  #
38
38
  describe "Fit.constant" do
39
- it "must stuff" do
39
+ it "returns zero variance with truly constant inputs" do
40
40
  [0, 1, 10, 100, 1000, 9999].each { |a|
41
41
  y_bar, variance = Fit.constant(@xs, @xs.map { |x| a })
42
42
  expect(y_bar).must_equal a
@@ -47,7 +47,7 @@ describe Fit do
47
47
 
48
48
  # y = a + b*ln(x)
49
49
  describe "Fit.logarithmic" do
50
- it "must accept logarithmic data" do
50
+ it "accepts logarithmic data" do
51
51
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
52
52
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
53
53
  ary = Fit.logarithmic(@xs, @xs.map { |x| a + b * Math.log(x) })
@@ -61,7 +61,7 @@ describe Fit do
61
61
 
62
62
  # y = a + bx
63
63
  describe "Fit.linear" do
64
- it "must accept linear data" do
64
+ it "accepts linear data" do
65
65
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |a|
66
66
  [-9999, -2000, -500, -0.01, 0.01, 500, 2000, 9999].each { |b|
67
67
  ary = Fit.linear(@xs, @xs.map { |x| a + b * x })
@@ -72,7 +72,7 @@ describe Fit do
72
72
  }
73
73
  end
74
74
 
75
- it "must accept constant data" do
75
+ it "accepts constant data" do
76
76
  [0, 1, 10, 100, 1000, 9999].each { |a|
77
77
  ary = Fit.linear(@xs, @xs.map { |x| a })
78
78
  expect(ary[0]).must_equal a
@@ -84,7 +84,7 @@ describe Fit do
84
84
  # note, this test can possibly fail depending on the uniformity of
85
85
  # rand's output for our sample
86
86
  #
87
- it "must accept noisy constant data" do
87
+ it "accepts noisy constant data" do
88
88
  r2s = []
89
89
  [0, 1, 10, 100, 1000, 9999].each { |a|
90
90
  ary = Fit.linear(@xs, @xs.map { |x| a + noise() })
@@ -96,16 +96,14 @@ describe Fit do
96
96
  expect(mean_r2).must_be_close_to 0.15, 0.15
97
97
  end
98
98
 
99
- it "must reject x^2" do
100
- skip "it does not reject x^2 at r^2 < 0.99"
101
- xs = [1, 10, 100, 1000]
99
+ it "rejects x^2" do
100
+ xs = Array.new(99) { |i| i }
102
101
  _a, _b, r2 = Fit.linear(xs, xs.map { |x| x**2 })
103
102
  expect(r2).must_be :<, 0.99
104
103
  end
105
104
 
106
- it "must reject x^3" do
107
- skip "it does not reject x^3 at r^2 < 0.99"
108
- xs = [1, 10, 100, 1000]
105
+ it "rejects x^3" do
106
+ xs = Array.new(99) { |i| i }
109
107
  _a, _b, r2 = Fit.linear(xs, xs.map { |x| x**3 })
110
108
  expect(r2).must_be :<, 0.99
111
109
  end
@@ -113,7 +111,7 @@ describe Fit do
113
111
 
114
112
  # y = ae^(bx)
115
113
  describe "Fit.exponential" do
116
- it "must accept exponential data" do
114
+ it "accepts exponential data" do
117
115
  [0.001, 7.5, 500, 1000, 5000, 9999].each { |a|
118
116
  [-1.4, -1.1, -0.1, 0.01, 0.5, 0.75].each { |b|
119
117
  ary = Fit.exponential(@xs, @xs.map { |x| a * Math::E**(b * x) })
@@ -127,10 +125,13 @@ describe Fit do
127
125
 
128
126
  # y = ax^b
129
127
  describe "Fit.power" do
130
- it "must accept power data" do
128
+ it "accepts power data" do
131
129
  [0.01, 7.5, 500, 1000, 5000, 9999].each { |a|
132
130
  [-114, -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
133
- next if b == -114 # Fit.error warning: Bignum out of Float range
131
+ # [ -100, -10, -0.5, -0.1, 0.1, 0.75, 10, 50, 60].each { |b|
132
+ # note: on Ruby 2.4.x and older, b == -114 throws
133
+ # warning: Bignum out of Float range
134
+ # also: TruffleRuby as of Jan '22: ary[2] is NaN rather than 1.0
134
135
  ary = Fit.power(@xs, @xs.map { |x| a * x**b })
135
136
  expect(ary[0]).must_be_close_to a
136
137
  expect(ary[1]).must_be_close_to b
@@ -139,4 +140,41 @@ describe Fit do
139
140
  }
140
141
  end
141
142
  end
143
+
144
+ describe "Fit.predict" do
145
+ before do
146
+ @a, @b, @x = 1, 2, 3
147
+ end
148
+
149
+ it "accepts a few different models" do
150
+ [:constant, :logarithmic, :linear, :exponential, :power].each { |model|
151
+ expect(Fit.predict(model, @a, @b, @x)).must_be_kind_of(Numeric)
152
+ }
153
+ expect { Fit.predict(:invalid, @a, @b, @x) }.must_raise RuntimeError
154
+ end
155
+
156
+ it "predicts constant relationships" do
157
+ expect(Fit.predict(:constant, @a, @b, @x)).must_equal @a
158
+ expect(Fit.predict(:constant, @a, @x, @b)).must_equal @a
159
+ expect(Fit.predict(:constant, @x, @a, @b)).must_equal @x
160
+ end
161
+
162
+ it "predicts logarithmic relationships" do
163
+ expect(Fit.predict(:logarithmic, @a, @b, @x)
164
+ ).must_equal @a + @b * Math.log(@x)
165
+ end
166
+
167
+ it "predicts linear relationships" do
168
+ expect(Fit.predict(:linear, @a, @b, @x)).must_equal @a + @b * @x
169
+ end
170
+
171
+ it "predicts exponential relationships" do
172
+ expect(Fit.predict(:exponential, @a, @b, @x)
173
+ ).must_equal @a * Math::E ** (@b * @x)
174
+ end
175
+
176
+ it "predicts power relationships" do
177
+ expect(Fit.predict(:power, @a, @b, @x)).must_equal @a * @x ** @b
178
+ end
179
+ end
142
180
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: compsci
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1.1
4
+ version: 0.3.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-29 00:00:00.000000000 Z
11
+ date: 1980-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: matrix
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: buildar
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -28,30 +42,30 @@ dependencies:
28
42
  name: minitest
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - ">="
45
+ - - "~>"
32
46
  - !ruby/object:Gem::Version
33
47
  version: '5.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - ">="
52
+ - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '5.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - ">="
59
+ - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: 12.3.3
61
+ version: '12.3'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - ">="
66
+ - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: 12.3.3
68
+ version: '12.3'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: flog
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +137,7 @@ dependencies:
123
137
  - !ruby/object:Gem::Version
124
138
  version: '2.0'
125
139
  description: Trees, Heaps, Timers, Error fitting, etc
126
- email:
140
+ email:
127
141
  executables: []
128
142
  extensions: []
129
143
  extra_rdoc_files: []
@@ -172,7 +186,7 @@ homepage: https://github.com/rickhull/compsci
172
186
  licenses:
173
187
  - LGPL-3.0
174
188
  metadata: {}
175
- post_install_message:
189
+ post_install_message:
176
190
  rdoc_options: []
177
191
  require_paths:
178
192
  - lib
@@ -180,15 +194,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
180
194
  requirements:
181
195
  - - "~>"
182
196
  - !ruby/object:Gem::Version
183
- version: '2'
197
+ version: '3.0'
184
198
  required_rubygems_version: !ruby/object:Gem::Requirement
185
199
  requirements:
186
200
  - - ">="
187
201
  - !ruby/object:Gem::Version
188
202
  version: '0'
189
203
  requirements: []
190
- rubygems_version: 3.0.6
191
- signing_key:
204
+ rubygems_version: 3.2.26
205
+ signing_key:
192
206
  specification_version: 4
193
207
  summary: Toy implementations for some basic computer science problems
194
208
  test_files: []