cmdstan 0.1.9 → 0.2.0

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