Module: STAN

Defined in:
lib/rbbt/stan.rb

Class Method Summary collapse

Class Method Details

.data_header(types) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/rbbt/stan.rb', line 54

def self.data_header(types)

  types_str = ""
  types.each do |name,type|
    name = name.to_s
    case type
    when 'real'
      types_str << "  " << "real " << name << ";\n"
    when 'integer'
      types_str << "  " << "int " << name << ";\n"
    when 'array'
      types_str << "  " << "int<lower=0> " << name << "_l" << ";\n"
      #types_str << "  " << "real " << name << "[" << name << "_l]" << ";\n"
      types_str << "  " << "vector" << "[" << name << "_l] "<< name  << ";\n"
    when 'iarray'
      types_str << "  " << "int<lower=0> " << name << "_l" << ";\n"
      #types_str << "  " << "real " << name << "[" << name << "_l]" << ";\n"
      types_str << "  " << "int " << name << "[" << name << "_l]" << ";\n"
    when 'matrix'
      types_str << "  " << "int<lower=0> " << name << "_c" << ";\n"
      types_str << "  " << "int<lower=0> " << name << "_r" << ";\n"
      #types_str << "  " << "real " << name << "[" << name << "_r," << name << "_c]" << ";\n"
      types_str << "  " << "matrix" << "[" << name << "_r," << name << "_c] " << name << ";\n"
    else
      raise "Unknown type for #{ name }: #{type}"
    end

  end
  <<-EOF
data{

#{types_str}
}
  EOF
end

.exec(data, model, input_directory, parameter_chains, sample_file, debug = FALSE, stan_options = {}) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rbbt/stan.rb', line 90

def self.exec(data, model, input_directory, parameter_chains, sample_file, debug = FALSE, stan_options = {})
  stan_options = Misc.add_defaults stan_options, :iter => 1000, :warmup => 500, :chains => 1, :seed => 2887, :refresh => 1200

  data = {} if data.nil?

  types, load_str = save_data(input_directory, data)
  data_header = self.data_header(types)
  stan_model = data_header + "\n" + model

  stan_file = Rbbt.var.stan_models[Misc.digest(stan_model).to_s << ".stan"].find
  Open.write(stan_file, stan_model)

  Log.debug "STAN model:\n" + stan_model

  script = <<-EOF
rbbt.require('rstan')
rbbt.require('jsonlite')

rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())

#{load_str} 

fit <- stan(file='#{stan_file}', data=input_data, sample_file='#{sample_file}', verbose=#{debug ? 'TRUE' : 'FALSE'}, #{R.hash2Rargs(stan_options)})

params <- as.data.frame(fit)

print(fit)
#{parameter_chains.nil? ? "" : "rbbt.tsv.write('#{parameter_chains}', params)" }
  EOF

  R.run script, nil, :monitor => debug
end

.fit(data, model, options = {}) ⇒ Object



196
197
198
199
200
201
202
# File 'lib/rbbt/stan.rb', line 196

def self.fit(data, model, options = {})
  TmpFile.with_file do |directory|
    FileUtils.mkdir_p directory
    res = self.run(data, model, directory, options)
    TSV.open(res, :type => :list, :cast => :to_f)
  end
end

.run(data, model, directory, options = {}) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rbbt/stan.rb', line 180

def self.run(data, model, directory, options = {})
  debug = Misc.process_options options, :debug

  input_directory = File.join(directory, 'inputs')

  parameter_chains = File.join(directory, 'chains')
  summary = File.join(directory, 'summary')
  sample_file = File.join(directory, 'sample')

  res = self.exec(data, model, input_directory, parameter_chains, sample_file, debug, options)
  Log.debug "Result from STAN:\n" << res.read
  Open.write(summary, res.read)
  
  Open.open(parameter_chains)
end

.save_data(directory, data = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rbbt/stan.rb', line 6

def self.save_data(directory, data = {})
  directory = directory.find if Path === directory
  types = {}
  load_str = "input_data = list()\n"

  data.each do |name, value|
    file = File.join(directory, name.to_s)
    case value
    when Integer
      Open.write(file, value.to_json)
      types[name] = 'integer'
      load_str << "input_data[['" << name.to_s << "']] = " << "fromJSON(txt='#{file}')" << "\n"
    when Float
      Open.write(file, value.to_json)
      types[name] = 'real'
      load_str << "input_data[['" << name.to_s << "']] = " << "fromJSON(txt='#{file}')" << "\n"
    when Array
      Open.write(file, value.to_json)
      if value.select{|v| Float === v}.empty?
        types[name] = 'iarray'
      else
        types[name] = 'array'
      end
      load_str << "input_data[['" << name.to_s << "_l']] = " << value.length.to_s << "\n"
      load_str << "input_data[['" << name.to_s << "']] = " << "fromJSON(txt='#{file}')" << "\n"
    when TSV
      Open.write(file, value.to_s)
      types[name] = 'matrix'
      load_str << "input_data[['" << name.to_s << "_c']] = " << value.fields.length.to_s << "\n"
      load_str << "input_data[['" << name.to_s << "_r']] = " << value.size.to_s << "\n"
      load_str << "#{name}.tmp = " << "rbbt.impute(rbbt.tsv('#{file}'))" << "\n"
      load_str << "input_data[['" << name.to_s << "']] = " << "#{name}.tmp" << "\n"
    when Path
      value = TSV.open(value)
      Open.write(file, value.to_s)
      types[name] = 'matrix'
      load_str << "input_data[['" << name.to_s << "_c']] = " << value.fields.length.to_s << "\n"
      load_str << "input_data[['" << name.to_s << "_r']] = " << value.size.to_s << "\n"
      load_str << "#{name}.tmp = " << "rbbt.impute(rbbt.tsv('#{file}'))" << "\n"
      load_str << "input_data[['" << name.to_s << "']] = " << "#{name}.tmp" << "\n"
    else
      raise "Unknown type of data #{ name }: #{Misc.fingerprint value}"
    end
  end

  [types, load_str]
end

.stream_chain(data, model, directory = nil, options = {}) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/rbbt/stan.rb', line 124

def self.stream_chain(data, model, directory = nil, options = {})
  options, directory = directory, nil if Hash === directory
  debug = Misc.process_options options, :debug

  if directory.nil?
    directory = TmpFile.tmp_file 
    erase = true
  end

  FileUtils.mkdir_p directory unless File.exists? directory
  input_directory  = File.join(directory, 'inputs')
  parameter_chains = File.join(directory, 'chains') unless erase
  summary          = File.join(directory, 'summary') unless erase
  sample_file      = File.join(directory, 'samples')

  File.mkfifo(sample_file)

  io = Misc.open_pipe do |sin|
    iteration = 1
    sin << "#: :type=:list#:cast=:to_f" << "\n"
    begin
      reader = File.open(sample_file, 'r')
      while line = reader.gets
        if line =~ /^#/
          new_line = line
          next
        elsif line =~ /^lp__/
          parts = line.split(",")
          new_line = "#Iteration\t" << parts * "\t"
        else
          parts = line.split(",")
          new_line = iteration.to_s << "\t" << parts * "\t"
          iteration += 1
        end
        sin << new_line 
      end
    rescue 
      Log.exception $!
      raise $!
    end
  end

  exec_thread = Thread.new do
    res = self.exec(data, model, input_directory, parameter_chains, sample_file, debug, options)
    Open.write(summary, res.read.to_s) unless summary.nil?
    Log.debug "Result from STAN:\n" << res.read
  end

  ConcurrentStream.setup io, :threads => [exec_thread] do
    Log.debug "Done chains for STAN"
    if erase
      FileUtils.rm_rf directory
    end
  end
end