opentox-ruby 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/model.rb CHANGED
@@ -1,143 +1,271 @@
1
1
  module OpenTox
2
- module Model
3
2
 
4
- class Generic
3
+ module Model
5
4
 
6
- MODEL_ATTRIBS = [:uri, :title, :creator, :date, :format, :predictedVariables, :independentVariables, :dependentVariables, :trainingDataset, :algorithm]
7
- MODEL_ATTRIBS.each{ |a| attr_accessor(a) }
5
+ include OpenTox
8
6
 
9
- def self.find(uri)
10
- owl = OpenTox::Owl.from_uri(uri, "Model")
11
- return self.new(owl)
7
+ # Run a model with parameters
8
+ # @param [Hash] params Parameters for OpenTox model
9
+ # @return [text/uri-list] Task or resource URI
10
+ def run(params)
11
+ if CONFIG[:yaml_hosts].include?(URI.parse(@uri).host)
12
+ accept = 'application/x-yaml'
13
+ else
14
+ accept = 'application/rdf+xml'
12
15
  end
13
-
14
- def self.to_rdf(model)
15
- owl = OpenTox::Owl.create 'Model', model.uri
16
- (MODEL_ATTRIBS - [:uri]).each do |a|
17
- owl.set(a.to_s,model.send(a.to_s))
18
- end
19
- owl.rdf
20
- end
21
-
22
- protected
23
- def initialize(owl)
24
- MODEL_ATTRIBS.each do |a|
25
- self.send("#{a.to_s}=".to_sym, owl.get(a.to_s)) unless a==:uri
26
- end
27
- @uri = owl.uri
28
- if ENV['RACK_ENV'] =~ /test|debug/
29
- begin
30
- raise "uri invalid" unless Utils.is_uri?(@uri)
31
- raise "no predicted variables" unless @predictedVariables and @predictedVariables.size>0
32
- rescue => ex
33
- RestClientWrapper.raise_uri_error "invalid model: '"+ex.message+"'\n"+self.to_yaml+"\n",@uri.to_s
34
- end
35
- LOGGER.warn "model has no dependent variable" unless @dependentVariables and @dependentVariables.size>0
36
- LOGGER.warn "model has no algorithm" unless @algorithm and @algorithm.size>0
37
- LOGGER.warn "model has no indenpendent variables" unless @independentVariables
38
- end
16
+ begin
17
+ RestClientWrapper.post(@uri,{:accept => accept},params).to_s
18
+ rescue => e
19
+ LOGGER.error "Failed to run #{@uri} with #{params.inspect} (#{e.inspect})"
20
+ raise "Failed to run #{@uri} with #{params.inspect}"
39
21
  end
40
22
  end
41
-
42
- class PredictionModel < Generic
43
-
44
- def self.build( algorithm_uri, algorithm_params )
45
-
46
- LOGGER.debug "Build model, algorithm_uri:"+algorithm_uri.to_s+", algorithm_parms: "+algorithm_params.inspect.to_s
47
- uri = OpenTox::RestClientWrapper.post(algorithm_uri,algorithm_params).to_s
48
- LOGGER.debug "Build model done: "+uri.to_s
49
- RestClientWrapper.raise_uri_error("Invalid build model result: '"+uri.to_s+"'", algorithm_uri, algorithm_params ) unless Utils.model_uri?(uri)
50
- return PredictionModel.find(uri)
51
- end
52
-
53
- def predict_dataset( dataset_uri )
54
-
55
- LOGGER.debug "Predict dataset: "+dataset_uri.to_s+" with model "+@uri.to_s
56
- uri = RestClientWrapper.post(@uri, {:accept => "text/uri-list", :dataset_uri=>dataset_uri})
57
- RestClientWrapper.raise_uri_error("Prediciton result no dataset uri: "+uri.to_s, @uri, {:dataset_uri=>dataset_uri} ) unless Utils.dataset_uri?(uri)
58
- uri
59
- end
60
-
61
- def classification?
62
- #HACK replace with request to ontology server
63
- if @title =~ /(?i)classification/
64
- return true
65
- elsif @title =~ /(?i)regression/
66
- return false
67
- elsif @uri =~/ntua/ and @title =~ /mlr/
68
- return false
69
- elsif @uri =~/tu-muenchen/ and @title =~ /regression|M5P|GaussP/
70
- return false
71
- elsif @uri =~/ambit2/ and @title =~ /pKa/ || @title =~ /Regression|Caco/
72
- return false
73
- elsif @uri =~/majority/
74
- return (@uri =~ /class/) != nil
75
- else
76
- raise "unknown model, uri:'"+@uri.to_s+"' title:'"+@title.to_s+"'"
77
- end
78
- end
23
+
24
+ # Generic OpenTox model class for all API compliant services
25
+ class Generic
26
+ include Model
79
27
  end
80
28
 
81
- class Lazar < Generic
82
-
83
- attr_accessor :feature_dataset_uri, :effects, :activities, :p_values, :fingerprints, :features
84
-
85
- def initialize
86
- @source = "http://github.com/helma/opentox-model"
87
- @algorithm = File.join(@@config[:services]["opentox-algorithm"],"lazar")
88
- #@independent_variables = File.join(@@config[:services]["opentox-algorithm"],"fminer#BBRC_representative")
29
+ # Lazy Structure Activity Relationship class
30
+ class Lazar
31
+
32
+ include Model
33
+ include Algorithm
34
+
35
+ attr_accessor :compound, :prediction_dataset, :features, :effects, :activities, :p_values, :fingerprints, :feature_calculation_algorithm, :similarity_algorithm, :prediction_algorithm, :min_sim
36
+
37
+ def initialize(uri=nil)
38
+
39
+ if uri
40
+ super uri
41
+ else
42
+ super CONFIG[:services]["opentox-model"]
43
+ end
44
+
45
+ @metadata[OT.algorithm] = File.join(CONFIG[:services]["opentox-algorithm"],"lazar")
46
+
89
47
  @features = []
90
48
  @effects = {}
91
49
  @activities = {}
92
50
  @p_values = {}
93
51
  @fingerprints = {}
52
+
53
+ @feature_calculation_algorithm = "Substructure.match"
54
+ @similarity_algorithm = "Similarity.tanimoto"
55
+ @prediction_algorithm = "Neighbors.weighted_majority_vote"
56
+
57
+ @min_sim = 0.3
58
+
94
59
  end
95
60
 
96
- def save
97
- @features.uniq!
98
- resource = RestClient::Resource.new(@@config[:services]["opentox-model"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
99
- resource.post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
61
+ # Get URIs of all lazar models
62
+ # @return [Array] List of lazar model URIs
63
+ def self.all
64
+ RestClientWrapper.get(CONFIG[:services]["opentox-model"]).to_s.split("\n")
100
65
  end
101
66
 
102
- def self.find_all
103
- RestClientWrapper.get(@@config[:services]["opentox-model"]).chomp.split("\n")
67
+ # Find a lazar model
68
+ # @param [String] uri Model URI
69
+ # @return [OpenTox::Model::Lazar] lazar model
70
+ def self.find(uri)
71
+ YAML.load RestClientWrapper.get(uri,:accept => 'application/x-yaml')
104
72
  end
105
73
 
106
- def self.predict(compound_uri,model_uri)
107
- #RestClientWrapper.post(model_uri,{:compound_uri => compound_uri, :accept => 'application/x-yaml'})
108
- `curl -X POST -d 'compound_uri=#{compound_uri}' -H 'Accept:application/x-yaml' #{model_uri}`
74
+ # Create a new lazar model
75
+ # @param [optional,Hash] params Parameters for the lazar algorithm (OpenTox::Algorithm::Lazar)
76
+ # @return [OpenTox::Model::Lazar] lazar model
77
+ def self.create(params)
78
+ lazar_algorithm = OpenTox::Algorithm::Generic.new File.join( CONFIG[:services]["opentox-algorithm"],"lazar")
79
+ model_uri = lazar_algorithm.run(params)
80
+ OpenTox::Model::Lazar.find(model_uri)
109
81
  end
110
- end
111
-
112
- class PropertyLazar < Generic
113
-
114
- attr_accessor :feature_dataset_uri, :properties, :features, :activities#, :effects, :p_values
115
-
116
- def initialize
117
- @source = "http://github.com/helma/opentox-model"
118
- @algorithm = File.join(@@config[:services]["opentox-algorithm"],"property_lazar")
119
- #@independent_variables = File.join(@@config[:services]["opentox-algorithm"],"fminer#BBRC_representative")
120
- @features = []
121
- #@effects = {}
122
- @activities = {}
123
- #@p_values = {}
124
- @properties = {}
82
+
83
+ # Get a parameter value
84
+ # @param [String] param Parameter name
85
+ # @return [String] Parameter value
86
+ def parameter(param)
87
+ @metadata[OT.parameters].collect{|p| p[OT.paramValue] if p[DC.title] == param}.compact.first
125
88
  end
126
89
 
127
- def save
128
- @features.uniq!
129
- resource = RestClient::Resource.new(@@config[:services]["opentox-model"], :user => @@users[:users].keys[0], :password => @@users[:users].values[0])
130
- resource.post(self.to_yaml, :content_type => "application/x-yaml").chomp.to_s
90
+ # Predict a dataset
91
+ # @param [String] dataset_uri Dataset URI
92
+ # @return [OpenTox::Dataset] Dataset with predictions
93
+ def predict_dataset(dataset_uri)
94
+ @prediction_dataset = Dataset.create
95
+ @prediction_dataset.add_metadata({
96
+ OT.hasSource => @uri,
97
+ DC.creator => @uri,
98
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
99
+ OT.parameters => [{DC.title => "dataset_uri", OT.paramValue => dataset_uri}]
100
+ })
101
+ d = Dataset.new(dataset_uri)
102
+ d.load_compounds
103
+ d.compounds.each do |compound_uri|
104
+ predict(compound_uri,false)
105
+ end
106
+ @prediction_dataset.save
107
+ @prediction_dataset
131
108
  end
132
109
 
133
- def self.find_all
134
- RestClientWrapper.get(@@config[:services]["opentox-model"]).chomp.split("\n")
110
+ # Predict a compound
111
+ # @param [String] compound_uri Compound URI
112
+ # @param [optinal,Boolean] verbose Verbose prediction (output includes neighbors and features)
113
+ # @return [OpenTox::Dataset] Dataset with prediction
114
+ def predict(compound_uri,verbose=false)
115
+
116
+ @compound = Compound.new compound_uri
117
+ features = {}
118
+
119
+ unless @prediction_dataset
120
+ #@prediction_dataset = cached_prediction
121
+ #return @prediction_dataset if cached_prediction
122
+ @prediction_dataset = Dataset.create
123
+ @prediction_dataset.add_metadata( {
124
+ OT.hasSource => @uri,
125
+ DC.creator => @uri,
126
+ # TODO: fix dependentVariable
127
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
128
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
129
+ } )
130
+ end
131
+
132
+ return @prediction_dataset if database_activity
133
+
134
+ neighbors
135
+ prediction = eval("#{@prediction_algorithm}(@neighbors,{:similarity_algorithm => @similarity_algorithm, :p_values => @p_values})")
136
+
137
+ prediction_feature_uri = File.join( @prediction_dataset.uri, "feature", "prediction", File.basename(@metadata[OT.dependentVariables]),@prediction_dataset.compounds.size.to_s)
138
+ # TODO: fix dependentVariable
139
+ @prediction_dataset.metadata[OT.dependentVariables] = prediction_feature_uri
140
+
141
+ if @neighbors.size == 0
142
+ @prediction_dataset.add_feature(prediction_feature_uri, {
143
+ OT.isA => OT.MeasuredFeature,
144
+ OT.hasSource => @uri,
145
+ DC.creator => @uri,
146
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
147
+ OT.error => "No similar compounds in training dataset.",
148
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
149
+ })
150
+ @prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
151
+
152
+ else
153
+ @prediction_dataset.add_feature(prediction_feature_uri, {
154
+ OT.isA => OT.ModelPrediction,
155
+ OT.hasSource => @uri,
156
+ DC.creator => @uri,
157
+ DC.title => URI.decode(File.basename( @metadata[OT.dependentVariables] )),
158
+ OT.prediction => prediction[:prediction],
159
+ OT.confidence => prediction[:confidence],
160
+ OT.parameters => [{DC.title => "compound_uri", OT.paramValue => compound_uri}]
161
+ })
162
+ @prediction_dataset.add @compound.uri, prediction_feature_uri, prediction[:prediction]
163
+
164
+ if verbose
165
+ if @feature_calculation_algorithm == "Substructure.match"
166
+ f = 0
167
+ @compound_features.each do |feature|
168
+ feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s)
169
+ features[feature] = feature_uri
170
+ @prediction_dataset.add_feature(feature_uri, {
171
+ OT.isA => OT.Substructure,
172
+ OT.smarts => feature,
173
+ OT.pValue => @p_values[feature],
174
+ OT.effect => @effects[feature]
175
+ })
176
+ @prediction_dataset.add @compound.uri, feature_uri, true
177
+ f+=1
178
+ end
179
+ else
180
+ @compound_features.each do |feature|
181
+ features[feature] = feature
182
+ @prediction_dataset.add @compound.uri, feature, true
183
+ end
184
+ end
185
+ n = 0
186
+ @neighbors.each do |neighbor|
187
+ neighbor_uri = File.join( @prediction_dataset.uri, "feature", "neighbor", n.to_s )
188
+ @prediction_dataset.add_feature(neighbor_uri, {
189
+ OT.compound => neighbor[:compound],
190
+ OT.similarity => neighbor[:similarity],
191
+ OT.measuredActivity => neighbor[:activity],
192
+ OT.isA => OT.Neighbor
193
+ })
194
+ @prediction_dataset.add @compound.uri, neighbor_uri, true
195
+ f = 0 unless f
196
+ neighbor[:features].each do |feature|
197
+ if @feature_calculation_algorithm == "Substructure.match"
198
+ feature_uri = File.join( @prediction_dataset.uri, "feature", "descriptor", f.to_s) unless feature_uri = features[feature]
199
+ else
200
+ feature_uri = feature
201
+ end
202
+ @prediction_dataset.add neighbor[:compound], feature_uri, true
203
+ unless features.has_key? feature
204
+ features[feature] = feature_uri
205
+ @prediction_dataset.add_feature(feature_uri, {
206
+ OT.isA => OT.Substructure,
207
+ OT.smarts => feature,
208
+ OT.pValue => @p_values[feature],
209
+ OT.effect => @effects[feature]
210
+ })
211
+ f+=1
212
+ end
213
+ end
214
+ n+=1
215
+ end
216
+ # what happens with dataset predictions?
217
+ end
218
+ end
219
+
220
+ @prediction_dataset.save
221
+ @prediction_dataset
135
222
  end
136
223
 
137
- def self.predict(compound_uri,model_uri)
138
- #RestClientWrapper.post(model_uri,{:compound_uri => compound_uri, :accept => 'application/x-yaml'})
139
- `curl -X POST -d 'compound_uri=#{compound_uri}' -H 'Accept:application/x-yaml' #{model_uri}`
224
+ # Find neighbors and store them as object variable
225
+ def neighbors
226
+
227
+ @compound_features = eval("#{@feature_calculation_algorithm}(@compound,@features)") if @feature_calculation_algorithm
228
+
229
+ @neighbors = []
230
+ @fingerprints.each do |training_compound,training_features|
231
+ sim = eval("#{@similarity_algorithm}(@compound_features,training_features,@p_values)")
232
+ if sim > @min_sim
233
+ @activities[training_compound].each do |act|
234
+ @neighbors << {
235
+ :compound => training_compound,
236
+ :similarity => sim,
237
+ :features => training_features,
238
+ :activity => act
239
+ }
240
+ end
241
+ end
242
+ end
243
+
140
244
  end
245
+
246
+ # Find database activities and store them in @prediction_dataset
247
+ # @return [Boolean] true if compound has databasse activities, false if not
248
+ def database_activity
249
+ if @activities[@compound.uri]
250
+ @activities[@compound.uri].each { |act| @prediction_dataset.add @compound.uri, @metadata[OT.dependentVariables], act }
251
+ @prediction_dataset.add_metadata(OT.hasSource => @metadata[OT.trainingDataset])
252
+ @prediction_dataset.save
253
+ true
254
+ else
255
+ false
256
+ end
257
+ end
258
+
259
+ # Save model at model service
260
+ def save
261
+ self.uri = RestClientWrapper.post(@uri,{:content_type => "application/x-yaml"},self.to_yaml)
262
+ end
263
+
264
+ # Delete model at model service
265
+ def delete
266
+ RestClientWrapper.delete @uri unless @uri == CONFIG[:services]["opentox-model"]
267
+ end
268
+
141
269
  end
142
270
  end
143
271
  end
@@ -0,0 +1,13 @@
1
+ ['rubygems', 'sinatra', 'sinatra/url_for', 'rest_client', 'yaml', 'cgi', 'spork', 'overwrite', 'environment'].each do |lib|
2
+ require lib
3
+ end
4
+
5
+ begin
6
+ require 'openbabel'
7
+ rescue LoadError
8
+ puts "Please install Openbabel with 'rake openbabel:install' in the compound component"
9
+ end
10
+
11
+ ['opentox', 'compound','dataset', 'parser','serializer', 'algorithm','model','task','validation','feature', 'rest_client_wrapper'].each do |lib|
12
+ require lib
13
+ end
data/lib/opentox.rb ADDED
@@ -0,0 +1,47 @@
1
+ module OpenTox
2
+
3
+ attr_reader :uri
4
+ attr_accessor :metadata
5
+
6
+ # Initialize OpenTox object with optional uri
7
+ # @param [optional, String] URI
8
+ def initialize(uri=nil)
9
+ @metadata = {}
10
+ self.uri = uri if uri
11
+ end
12
+
13
+ # Set URI
14
+ # @param [String] URI
15
+ def uri=(uri)
16
+ @uri = uri
17
+ @metadata[XSD.anyURI] = uri
18
+ end
19
+
20
+ # Get all objects from a service
21
+ # @return [Array] List of available URIs
22
+ def self.all(uri)
23
+ RestClientWrapper.get(uri,:accept => "text/uri-list").to_s.split(/\n/)
24
+ end
25
+
26
+ # Load (and return) metadata from object URI
27
+ # @return [Hash] Metadata
28
+ def load_metadata
29
+ @metadata = Parser::Owl::Generic.new(@uri).load_metadata
30
+ @metadata
31
+ end
32
+
33
+ def add_metadata(metadata)
34
+ metadata.each { |k,v| @metadata[k] = v }
35
+ end
36
+
37
+ # Get OWL-DL representation in RDF/XML format
38
+ # @return [application/rdf+xml] RDF/XML representation
39
+ def to_rdfxml
40
+ s = Serializer::Owl.new
41
+ s.add_metadata(@uri,@metadata)
42
+ #s.add_parameters(@uri,@parameters) if @parameters
43
+ s.to_rdfxml
44
+ end
45
+
46
+ end
47
+
data/lib/overwrite.rb CHANGED
@@ -12,3 +12,75 @@ class Sinatra::Base
12
12
  end
13
13
  end
14
14
 
15
+ class String
16
+ def task_uri?
17
+ self.uri? && !self.match(/task/).nil?
18
+ end
19
+
20
+ def dataset_uri?
21
+ self.uri? && !self.match(/dataset/).nil?
22
+ end
23
+
24
+ def self.model_uri?
25
+ self.uri? && !self.match(/model/).nil?
26
+ end
27
+
28
+ def uri?
29
+ begin
30
+ u = URI::parse(self)
31
+ return (u.scheme!=nil and u.host!=nil)
32
+ rescue URI::InvalidURIError
33
+ return false
34
+ end
35
+ end
36
+ end
37
+
38
+ require 'logger'
39
+ # logging
40
+ #class Logger
41
+ class OTLogger < Logger
42
+
43
+ def pwd
44
+ path = Dir.pwd.to_s
45
+ index = path.rindex(/\//)
46
+ return path if index==nil
47
+ path[(index+1)..-1]
48
+ end
49
+
50
+ def trace()
51
+ lines = caller(0)
52
+ n = 2
53
+ line = lines[n]
54
+
55
+ while (line =~ /spork.rb/ or line =~ /create/ or line =~ /ot-logger.rb/)
56
+ n += 1
57
+ line = lines[n]
58
+ end
59
+
60
+ index = line.rindex(/\/.*\.rb/)
61
+ return line if index==nil
62
+ line[index..-1]
63
+ end
64
+
65
+ def format(msg)
66
+ pwd.ljust(18)+" :: "+msg.to_s+" :: "+trace+" :: "+($sinatra ? $sinatra.request.env['REMOTE_ADDR'] : nil).to_s
67
+ end
68
+
69
+ def debug(msg)
70
+ super format(msg)
71
+ end
72
+
73
+ def info(msg)
74
+ super format(msg)
75
+ end
76
+
77
+ def warn(msg)
78
+ super format(msg)
79
+ end
80
+
81
+ def error(msg)
82
+ super format(msg)
83
+ end
84
+
85
+ end
86
+