compsci 0.3.1.1 → 0.3.2.3

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: 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: []