compsci 0.3.1.1 → 0.3.2.1

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: 5a7f1f7282649dec4f08ef08ce3eb3b5b3b97183137624deaa9f20567650185a
4
+ data.tar.gz: 57081ef332e9adc6d3dbdc06b3ff2c9c76955d26b4fdd90402b7547b5544e0fd
5
5
  SHA512:
6
- metadata.gz: 64100e3719380956c844e7f9eb3949eae097a6d9426b039d0d224260d4bd18a1e1dd7ad7f147643203e21b3ba95d0109bed198d8e5421c9e3c6c21312665b426
7
- data.tar.gz: c8944161d1ee4b4df7afac5d27bc0801b2953893ea45db03c30ec1e9f5ee74764638464ee1c3d4d3707107370dd547eedb1f62d09cfe9f20f2efb399e462258f
6
+ metadata.gz: cdb60b8e1230cdc175ac43afca1efe51bb163ce71dfb2cdd29ffa8ff8ab586456ec181dd46389d54b768edb360f5f9f56d6729bbd65ee86fcd7fa7acee0f417e
7
+ data.tar.gz: 3f20b69bd337aebc862a7f40e1e1177386979f71065200fa18e4dd69bdefb3a382dd8cbd066903d2ba5793cf5c71acd9cd09b79088c4c15b38982b8196bfb0c2
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.1
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
 
@@ -16,8 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.files += Dir['examples/**/*.rb']
17
17
 
18
18
  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
19
+ s.add_development_dependency "minitest", "~> 5.0"
20
+ s.add_development_dependency "rake", "~> 12.3" # CVE-2020-8130
21
21
  s.add_development_dependency "flog", "~> 0"
22
22
  s.add_development_dependency "flay", "~> 0"
23
23
  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,14 +1,14 @@
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.1
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
13
  - !ruby/object:Gem::Dependency
14
14
  name: buildar
@@ -28,30 +28,30 @@ dependencies:
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '5.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 12.3.3
47
+ version: '12.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 12.3.3
54
+ version: '12.3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: flog
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '2.0'
125
125
  description: Trees, Heaps, Timers, Error fitting, etc
126
- email:
126
+ email:
127
127
  executables: []
128
128
  extensions: []
129
129
  extra_rdoc_files: []
@@ -172,7 +172,7 @@ homepage: https://github.com/rickhull/compsci
172
172
  licenses:
173
173
  - LGPL-3.0
174
174
  metadata: {}
175
- post_install_message:
175
+ post_install_message:
176
176
  rdoc_options: []
177
177
  require_paths:
178
178
  - lib
@@ -180,15 +180,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
180
180
  requirements:
181
181
  - - "~>"
182
182
  - !ruby/object:Gem::Version
183
- version: '2'
183
+ version: '3.0'
184
184
  required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  requirements:
186
186
  - - ">="
187
187
  - !ruby/object:Gem::Version
188
188
  version: '0'
189
189
  requirements: []
190
- rubygems_version: 3.0.6
191
- signing_key:
190
+ rubygems_version: 3.2.26
191
+ signing_key:
192
192
  specification_version: 4
193
193
  summary: Toy implementations for some basic computer science problems
194
194
  test_files: []