cmdstan 0.1.9 → 0.2.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: 86b23f06c13af5825711739c0fc3a66c141fabc38507fd6b4e3e067137422c38
4
- data.tar.gz: 4921aae2623e273021f1a3f37f11bd16631902f0b6fb663b36a28d8412968abf
3
+ metadata.gz: ca789cf84801bb8236e317d7fa3d0f4946f1dde0af355975adf48fba6d4aae38
4
+ data.tar.gz: fcf91454404ba92fa54e68102d86826fb51e390f8277d6552b9d8805c8f48ad7
5
5
  SHA512:
6
- metadata.gz: bfe6e78ce9194750c3ee8c89ac02eb9e1b5526c502f9e0908bd5a3c2f3296bb7650de0e0abc61381142a2888c3bb0dedac2d9c80db252084ccf51a7d7f91e41c
7
- data.tar.gz: c6176782e0ffb1c9817ab4db949a0e208a143c87c2420ee1747de3149e3b590d6298b019052d5425dd8ec533f32acd190698dee8bb17fc291e96fd2770f0f1ed
6
+ metadata.gz: 7e093e742ddd620087178b2db7ae8c854f40392e0f8b89f6db84bc9492d897c800d39ed0951523809808b91df08c0b2342503bcfbf2495a750ceb0651b5ea199
7
+ data.tar.gz: 45b9a5b9bc4fdad879d0a72ef8c0aa1d3fbe06a9ea3aeadd5ca1f18b7fae6add8190e539d55412bc46861621e9cba1bcc0e7110aa472237e2cb5383ecd781f68
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.2.0 (2022-04-23)
2
+
3
+ - CmdStan now installs when the first model is compiled rather than on gem installation
4
+ - Added `cmdstan_installed?` and `install_cmdstan` methods
5
+ - Updated CmdStan to 2.29.2
6
+ - Fixed issue with `summary` method
7
+ - Dropped support for Ruby < 2.7
8
+
1
9
  ## 0.1.9 (2022-02-07)
2
10
 
3
11
  - Added support for Linux ARM
data/README.md CHANGED
@@ -12,8 +12,6 @@ Add this line to your application’s Gemfile:
12
12
  gem "cmdstan"
13
13
  ```
14
14
 
15
- Installation can take a few minutes as CmdStan downloads and builds.
16
-
17
15
  ## Getting Started
18
16
 
19
17
  Create a Stan file, like `bernoulli.stan`
@@ -32,7 +30,7 @@ model {
32
30
  }
33
31
  ```
34
32
 
35
- Compile the model
33
+ Compile the model (this can take a few minutes the first time as CmdStan downloads and builds)
36
34
 
37
35
  ```ruby
38
36
  model = CmdStan::Model.new(stan_file: "bernoulli.stan")
@@ -51,6 +49,12 @@ Summarize the results
51
49
  fit.summary
52
50
  ```
53
51
 
52
+ Load a compiled model
53
+
54
+ ```ruby
55
+ model = CmdStan::Model.new(exe_file: "bernoulli")
56
+ ```
57
+
54
58
  ## Maximum Likelihood Estimation
55
59
 
56
60
  ```ruby
@@ -58,6 +62,20 @@ mle = model.optimize(data: data)
58
62
  mle.optimized_params
59
63
  ```
60
64
 
65
+ ## Reference
66
+
67
+ Check if CmdStan is installed
68
+
69
+ ```ruby
70
+ CmdStan.cmdstan_installed?
71
+ ```
72
+
73
+ Install CmdStan manually
74
+
75
+ ```ruby
76
+ CmdStan.install_cmdstan
77
+ ```
78
+
61
79
  ## Credits
62
80
 
63
81
  This library is modeled after the [CmdStanPy API](https://github.com/stan-dev/cmdstanpy).
@@ -81,6 +99,5 @@ To get started with development:
81
99
  git clone https://github.com/ankane/cmdstan-ruby.git
82
100
  cd cmdstan-ruby
83
101
  bundle install
84
- bundle exec ruby ext/cmdstan/extconf.rb
85
102
  bundle exec rake test
86
103
  ```
@@ -0,0 +1,100 @@
1
+ module CmdStan
2
+ module Install
3
+ def cmdstan_version
4
+ "2.29.2"
5
+ end
6
+
7
+ def cmdstan_installed?
8
+ Dir.exist?(CmdStan.path)
9
+ end
10
+
11
+ def install_cmdstan
12
+ version = cmdstan_version
13
+ dir = CmdStan.path
14
+
15
+ # TODO figure out Mac ARM
16
+ if RbConfig::CONFIG["host_os"] !~ /darwin/i && RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
17
+ checksum = "9b7eec78e217cab39d3d794e817a1ca08f36b1e5cb434c4cd8263bb2650ba125"
18
+ url = "https://github.com/stan-dev/cmdstan/releases/download/v#{version}/cmdstan-#{version}-linux-arm64.tar.gz"
19
+ else
20
+ checksum = "567b531fa73ffdf706caa17eb3344e1dfb41e86993caf8ba40875ff910335153"
21
+ url = "https://github.com/stan-dev/cmdstan/releases/download/v#{version}/cmdstan-#{version}.tar.gz"
22
+ end
23
+
24
+ puts "Installing CmdStan version: #{version}"
25
+ puts "Install directory: #{dir}"
26
+
27
+ # only needed if default path
28
+ FileUtils.mkdir_p(File.expand_path("../../tmp", __dir__)) unless ENV["CMDSTAN"]
29
+
30
+ if Dir.exist?(dir)
31
+ puts "Already installed"
32
+ return true
33
+ end
34
+
35
+ Dir.mktmpdir do |tmpdir|
36
+ puts "Downloading..."
37
+ download_path = File.join(tmpdir, "cmdstan-#{version}.tar.gz")
38
+ download_file(url, download_path, checksum)
39
+
40
+ puts "Unpacking..."
41
+ path = File.join(tmpdir, "cmdstan-#{version}")
42
+ FileUtils.mkdir_p(path)
43
+ tar_args = Gem.win_platform? ? ["--force-local"] : []
44
+ system "tar", "xzf", download_path, "-C", path, "--strip-components=1", *tar_args
45
+
46
+ puts "Building..."
47
+ make_command = Gem.win_platform? ? "mingw32-make" : "make"
48
+ Dir.chdir(path) do
49
+ # disable precompiled header to save space
50
+ output, status = Open3.capture2e(make_command, "build", "PRECOMPILED_HEADERS=false")
51
+ if status.exitstatus != 0
52
+ puts output
53
+ raise Error, "Build failed"
54
+ end
55
+ end
56
+
57
+ FileUtils.mv(path, dir)
58
+ end
59
+
60
+ puts "Installed"
61
+
62
+ true
63
+ end
64
+
65
+ private
66
+
67
+ def download_file(url, download_path, checksum, redirects = 0)
68
+ raise Error, "Too many redirects" if redirects > 10
69
+
70
+ uri = URI(url)
71
+ location = nil
72
+
73
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
74
+ request = Net::HTTP::Get.new(uri)
75
+ http.request(request) do |response|
76
+ case response
77
+ when Net::HTTPRedirection
78
+ location = response["location"]
79
+ when Net::HTTPSuccess
80
+ digest = Digest::SHA2.new
81
+
82
+ File.open(download_path, "wb") do |f|
83
+ response.read_body do |chunk|
84
+ f.write(chunk)
85
+ digest.update(chunk)
86
+ end
87
+ end
88
+
89
+ raise Error, "Bad checksum: #{digest.hexdigest}" if digest.hexdigest != checksum
90
+ else
91
+ raise Error, "Bad response"
92
+ end
93
+ end
94
+ end
95
+
96
+ # outside of Net::HTTP block to close previous connection
97
+ download_file(location, download_path, checksum, redirects + 1) if location
98
+ end
99
+ end
100
+ end
data/lib/cmdstan/mcmc.rb CHANGED
@@ -30,7 +30,7 @@ module CmdStan
30
30
  run_command "#{CmdStan.path}/bin/stansummary#{extension}", "--csv_filename=#{path}", *@output_files.map(&:path)
31
31
 
32
32
  result = {}
33
- CSV.foreach(path, headers: true, converters: :numeric) do |row|
33
+ CSV.foreach(path, skip_lines: /^#/, headers: true, converters: :numeric) do |row|
34
34
  value = row.to_h
35
35
  name = value.delete("name")
36
36
  result[name] = value if name == "lp__" || !name.end_with?("__")
data/lib/cmdstan/model.rb CHANGED
@@ -18,8 +18,12 @@ module CmdStan
18
18
  end
19
19
 
20
20
  def compile
21
+ unless ENV["CMDSTAN"] || CmdStan.cmdstan_installed?
22
+ CmdStan.install_cmdstan
23
+ end
24
+
21
25
  Dir.chdir(CmdStan.path) do
22
- run_command make_command, @exe_file
26
+ run_command make_command, @exe_file, "PRECOMPILED_HEADERS=false"
23
27
  end
24
28
  end
25
29
 
@@ -1,3 +1,3 @@
1
1
  module CmdStan
2
- VERSION = "0.1.9"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/cmdstan.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  # stdlib
2
+ require "digest"
2
3
  require "csv"
4
+ require "fileutils"
3
5
  require "json"
6
+ require "net/http"
4
7
  require "open3"
5
8
  require "tempfile"
6
9
 
7
10
  # modules
11
+ require "cmdstan/install"
8
12
  require "cmdstan/utils"
9
13
  require "cmdstan/mcmc"
10
14
  require "cmdstan/mle"
@@ -14,6 +18,8 @@ require "cmdstan/version"
14
18
  module CmdStan
15
19
  class Error < StandardError; end
16
20
 
21
+ extend Install
22
+
17
23
  class << self
18
24
  attr_accessor :path
19
25
  end
metadata CHANGED
@@ -1,28 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmdstan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-07 00:00:00.000000000 Z
11
+ date: 2022-04-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: andrew@ankane.org
15
15
  executables: []
16
- extensions:
17
- - ext/cmdstan/extconf.rb
16
+ extensions: []
18
17
  extra_rdoc_files: []
19
18
  files:
20
19
  - CHANGELOG.md
21
20
  - LICENSE.txt
22
21
  - README.md
23
- - ext/cmdstan/Makefile
24
- - ext/cmdstan/extconf.rb
25
22
  - lib/cmdstan.rb
23
+ - lib/cmdstan/install.rb
26
24
  - lib/cmdstan/mcmc.rb
27
25
  - lib/cmdstan/mle.rb
28
26
  - lib/cmdstan/model.rb
@@ -40,14 +38,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
40
38
  requirements:
41
39
  - - ">="
42
40
  - !ruby/object:Gem::Version
43
- version: '2.4'
41
+ version: '2.7'
44
42
  required_rubygems_version: !ruby/object:Gem::Requirement
45
43
  requirements:
46
44
  - - ">="
47
45
  - !ruby/object:Gem::Version
48
46
  version: '0'
49
47
  requirements: []
50
- rubygems_version: 3.3.3
48
+ rubygems_version: 3.3.7
51
49
  signing_key:
52
50
  specification_version: 4
53
51
  summary: Bayesian inference for Ruby, powered by CmdStan
data/ext/cmdstan/Makefile DELETED
@@ -1,5 +0,0 @@
1
- install:
2
- @echo "Nothing to do"
3
-
4
- clean:
5
- @echo "Nothing to do"
@@ -1,75 +0,0 @@
1
- require "digest"
2
- require "fileutils"
3
- require "net/http"
4
- require "tmpdir"
5
-
6
- version = "2.28.2"
7
- # TODO figure out Mac ARM
8
- if RbConfig::CONFIG["host_os"] !~ /darwin/i && RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
9
- checksum = "cd8f588a1267e3e36761b7bcdff1b7f36bf43b8ec119667654995bfa5f921fa4"
10
- url = "https://github.com/stan-dev/cmdstan/releases/download/v#{version}/cmdstan-#{version}-linux-arm64.tar.gz"
11
- else
12
- checksum = "4e6ed2685460d7f33ddbc81ccb1e59befa7efd91e46d5b040027aab8daec4526"
13
- url = "https://github.com/stan-dev/cmdstan/releases/download/v#{version}/cmdstan-#{version}.tar.gz"
14
- end
15
-
16
- path = ENV["CMDSTAN"] || File.expand_path("../../tmp/cmdstan", __dir__)
17
- FileUtils.mkdir_p(path)
18
- raise "Directory not empty. Run: rake clean" unless Dir.empty?(path)
19
-
20
- $stdout.sync = true
21
-
22
- def download_file(url, download_path, checksum, redirects = 0)
23
- raise "Too many redirects" if redirects > 10
24
-
25
- uri = URI(url)
26
- location = nil
27
-
28
- Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
29
- request = Net::HTTP::Get.new(uri)
30
- http.request(request) do |response|
31
- case response
32
- when Net::HTTPRedirection
33
- location = response["location"]
34
- when Net::HTTPSuccess
35
- digest = Digest::SHA2.new
36
-
37
- i = 0
38
- File.open(download_path, "wb") do |f|
39
- response.read_body do |chunk|
40
- f.write(chunk)
41
- digest.update(chunk)
42
-
43
- # print progress
44
- putc "." if i % 50 == 0
45
- i += 1
46
- end
47
- end
48
- puts # newline
49
-
50
- abort "Bad checksum: #{digest.hexdigest}" if digest.hexdigest != checksum
51
- else
52
- abort "Bad response"
53
- end
54
- end
55
- end
56
-
57
- # outside of Net::HTTP block to close previous connection
58
- download_file(location, download_path, checksum, redirects + 1) if location
59
- end
60
-
61
- # download
62
- puts "Downloading #{url}..."
63
- download_path = "#{Dir.tmpdir}/cmdstan-#{version}.tar.gz"
64
- download_file(url, download_path, checksum)
65
-
66
- # extract
67
- Dir.chdir(path)
68
- # TODO use Gem::Package::TarReader from Rubygems
69
- tar_args = Gem.win_platform? ? ["--force-local"] : []
70
- system "tar", "zxf", download_path, "-C", path, "--strip-components=1", *tar_args
71
-
72
- # build
73
- make_command = Gem.win_platform? ? "mingw32-make" : "make"
74
- success = system(make_command, "build")
75
- raise "Build failed" unless success