biopsy 0.1.0.alpha → 0.1.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4b7eed6bdea005473b5bab0e14b2d05eadb7dbf
4
+ data.tar.gz: 788a2e6a6bd4f67d5d35c39e5b710634d72c45cb
5
+ SHA512:
6
+ metadata.gz: ddfe6c7044e51f9f59774b2750670cc5e44f88ea61cacdcdf42f5cb8e8000979da68f9c7326d8c3fcf05aea5125c5d12e27c23a9b9be73de55306fe5b71b1590
7
+ data.tar.gz: 1e8ad4d20403f7c6abf300663ad2dd670518c67d93626573011cdf9220ebc38ee330952af24e175b16e12d3ab99562f72ab9ecef286a707333af388f0c331be5
data/README.md CHANGED
@@ -7,6 +7,28 @@ Biopsy is a framework for optimising any program or pipeline which produces a me
7
7
 
8
8
  A simple example of the power of this approach is *de-novo* transcriptome assembly. Typically, the assembly process takes many GB of data as input, uses many GB of RAM and takes many hours to complete. This prevents researchers from performing full parameter sweeps, and they are therefore forced to use word-of-mouth and very basic optimisation to choose assembler settings. Assemblotron, which uses the Biopsy framework, can fully optimise any *de-novo* assembler to produce the optimal assembly possible given a particular input. This typically takes little more time than running a single assembly.
9
9
 
10
+ ## Installation
11
+
12
+ Make sure you have Ruby installed, then:
13
+
14
+ `gem install biopsy --pre`
15
+
16
+ ## Usage
17
+
18
+ []
19
+
20
+ Detailed usage instructions are on the wiki. Here's a quick overview:
21
+
22
+ 1. Define your optimisation target. This is a program or pipeline you want to optimise, and you define it by filling in a template [YAML file](http://en.wikipedia.org/wiki/YAML). Easy!
23
+ 2. Define your objective function. This is a program that analyses the output of your program and gives it a score. You define it by writing a small amount of Ruby code. Don't worry - there's a template and detailed instructions on the wiki.
24
+ 3. Run Biopsy, and wait while the experiment runs.
25
+
26
+ ### Command line examples
27
+
28
+ `biopsy list targets`
29
+ `biopsy list objectives`
30
+ `biposy run --target test_target --objective test_objective --input test_file.txt --time-limit 24h`
31
+
10
32
  ## Development status
11
33
 
12
34
  [![Gem Version](https://badge.fury.io/rb/biopsy.png)][gem]
@@ -46,4 +68,5 @@ Documentation is in development and will be released with the beta.
46
68
 
47
69
  ### Citation
48
70
 
49
- This is *pre-release*, *pre-publication* academic software. In lieu of a paper to cite, please cite this Github repo if your use of the software leads to a publication.
71
+ This is *pre-release*, *pre-publication* academic software. In lieu of a paper to cite, please cite this Github repo and/or the [Figshare DOI (http://dx.doi.org/10.6084/m9.figshare.790660
72
+ )](http://dx.doi.org/10.6084/m9.figshare.790660) if your use of the software leads to a publication.
@@ -32,6 +32,7 @@ class Hash
32
32
  target = dup
33
33
  target.inject({}) do |memo, (key, value)|
34
34
  value = value.deep_symbolize if value.is_a?(Hash)
35
+ value = value.map{ |x| x.is_a?(Hash) ? x.deep_symbolize : x } if value.is_a?(Array)
35
36
  memo[(key.to_sym rescue key) || key] = value
36
37
  memo
37
38
  end
@@ -54,11 +55,7 @@ class Array
54
55
  # Requires the array to contain only objects of class Fixnum.
55
56
  # If any other class is encountered, an error will be raised.
56
57
  def mean
57
- self.sum / self.size.to_f
58
- end
59
-
60
- def sum
61
- self.inject(0, :+)
58
+ self.inject(0, :+) / self.size.to_f
62
59
  end
63
60
 
64
61
  end # Array
@@ -19,13 +19,12 @@ module Biopsy
19
19
  attr_reader :inputs, :outputs, :retain_intermediates, :target, :start, :algorithm
20
20
 
21
21
  # Returns a new Experiment
22
- def initialize(target_name, domain_name, start=nil, algorithm=nil)
23
- @domain = Domain.new domain_name
22
+ def initialize(target_name, start=nil, algorithm=nil)
24
23
  @start = start
25
24
  @algorithm = algorithm
26
25
 
27
26
  self.load_target target_name
28
- @objective = ObjectiveHandler.new(@domain, @target)
27
+ @objective = ObjectiveHandler.new @target
29
28
  self.select_algorithm
30
29
  self.select_starting_point
31
30
  @scores = {}
@@ -44,19 +43,23 @@ module Biopsy
44
43
 
45
44
  # Return a random set of parameters from the parameter space.
46
45
  def random_start_point
47
- Hash[@target.parameter_ranges.map { |p, r| [p, r.sample] }]
46
+ Hash[@target.parameters.map { |p, r| [p, r.sample] }]
48
47
  end
49
48
 
50
49
  # select the optimisation algorithm to use
51
50
  def select_algorithm
52
- @algorithm = ParameterSweeper.new(@target.parameter_ranges)
53
- return if @algorithm.combinations.size < Settings.instance.sweep_cutoff
54
- @algorithm = TabuSearch.new(@target.parameter_ranges)
51
+ max = Settings.instance.sweep_cutoff
52
+ n = @target.count_parameter_permutations
53
+ if n < max
54
+ @algorithm = ParameterSweeper.new(@target.parameters)
55
+ else
56
+ @algorithm = TabuSearch.new(@target.parameters)
57
+ end
55
58
  end
56
59
 
57
60
  # load the target named +:target_name+
58
61
  def load_target target_name
59
- @target = Target.new @domain
62
+ @target = Target.new
60
63
  @target.load_by_name target_name
61
64
  end
62
65
 
@@ -82,20 +85,54 @@ module Biopsy
82
85
  # encompassing the program, objective(s) and optimiser.
83
86
  # Returns the output of the optimiser.
84
87
  def run_iteration
85
- # run the target
86
- run_data = @target.run @current_params
87
- # evaluate with objectives
88
- param_key = @current_params.to_s
89
- result = nil
90
- if @scores.has_key? param_key
91
- result = @scores[param_key]
92
- else
93
- result = @objective.run_for_output run_data
94
- @iteration_count += 1
88
+ # create temp dir
89
+ Dir.chdir(self.create_tempdir) do
90
+ # run the target
91
+ raw_output = @target.run @current_params
92
+ # evaluate with objectives
93
+ param_key = @current_params.to_s
94
+ result = nil
95
+ if @scores.has_key? param_key
96
+ result = @scores[param_key]
97
+ else
98
+ result = @objective.run_for_output raw_output
99
+ @iteration_count += 1
100
+ end
101
+ @scores[@current_params.to_s] = result
102
+ # get next steps from optimiser
103
+ @current_params = @algorithm.run_one_iteration(@current_params, result)
104
+ end
105
+ self.cleanup
106
+ end
107
+
108
+ def cleanup
109
+ # TODO: make this work
110
+ # remove all but essential files
111
+ if Settings.instance.keep_intermediates
112
+ @objectives.values.each{ |objective| essential_files += objective.essential_files }
113
+ end
114
+ Dir["*"].each do |file|
115
+ next if File.directory? file
116
+ if essential_files && essential_files.include?(file)
117
+ `gzip #{file}` if Settings.instance.gzip_intermediates
118
+ FileUtils.mv("#{file}.gz", '../output')
119
+ end
120
+ end
121
+ FileUtils.rm_rf @last_tempdir
122
+ end
123
+
124
+ # create a guaranteed random temporary directory for storing outputs
125
+ # return the dirname
126
+ def create_tempdir
127
+ token = loop do
128
+ # generate random dirnames until we find one that
129
+ # doesn't exist
130
+ test_token = SecureRandom.hex
131
+ break test_token unless File.exists? test_token
95
132
  end
96
- @scores[@current_params.to_s] = result
97
- # get next steps from optimiser
98
- @current_params = @algorithm.run_one_iteration(@current_params, result)
133
+ Dir.mkdir(token)
134
+ @last_tempdir = token
135
+ return token
99
136
  end
100
137
 
101
138
  end # end of class RunHandler
@@ -39,10 +39,8 @@ module Biopsy
39
39
  attr_reader :last_tempdir
40
40
  attr_accessor :objectives
41
41
 
42
- def initialize domain, target
43
- @domain = domain
42
+ def initialize target
44
43
  @target = target
45
- base_dir = Settings.instance.base_dir
46
44
  @objectives_dir = Settings.instance.objectives_dir.first
47
45
  @objectives = {}
48
46
  $LOAD_PATH.unshift(@objectives_dir)
@@ -76,12 +74,14 @@ module Biopsy
76
74
 
77
75
  # Run a specific +:objective+ on the +:output+ of a target
78
76
  # with max +:threads+.
79
- def run_objective(objective, name, output, threads)
77
+ def run_objective(objective, name, raw_output, output_files, threads)
80
78
  begin
81
- # output is a hash containing the file(s) output
82
- # by the target in the format expected by the
83
- # objective function(s).
84
- return objective.run(output, threads)
79
+ # output is a, array: [raw_output, output_files].
80
+ # output_files is a hash containing the absolute paths
81
+ # to file(s) output by the target in the format expected by the
82
+ # objective function(s), with keys as the keys expected by the
83
+ # objective function
84
+ return objective.run(raw_output, output_files, threads)
85
85
  rescue NotImplementedError => e
86
86
  puts "Error: objective function #{objective.class} does not implement the run() method"
87
87
  puts "Please refer to the documentation for instructions on adding objective functions"
@@ -90,7 +90,6 @@ module Biopsy
90
90
  end
91
91
 
92
92
  # Perform a euclidean distance dimension reduction of multiple objectives
93
- # using weighting specified in the domain definition.
94
93
  def dimension_reduce(results)
95
94
  # calculate the weighted Euclidean distance from optimal
96
95
  # d(p, q) = \sqrt{(p_1 - q_1)^2 + (p_2 - q_2)^2+...+(p_i - q_i)^2+...+(p_n - q_n)^2}
@@ -107,40 +106,26 @@ module Biopsy
107
106
  end
108
107
 
109
108
  # Run all objectives functions for +:output+.
110
- def run_for_output(output, threads=6, cleanup=true, allresults=false)
109
+ def run_for_output(raw_output, threads=6, allresults=false)
111
110
  # check output files exist
112
- @target.output_files.each_pair do |key, name|
113
- unless File.exists?(output[key]) && File.size(output[key]) > 0
114
- info("file #{output[key]} does not exist or is empty")
111
+ output_files = {}
112
+ @target.output.each_pair do |key, glob|
113
+ files = Dir[glob]
114
+ zerosize = files.reduce(false) { |empty, f| File.size(f) == 0 }
115
+ if files.empty? || zerosize
116
+ puts Dir.pwd
117
+ raise ObjectiveHandlerError.new "output files for #{key} matching #{glob} do not exist or are empty"
115
118
  return nil
116
119
  end
120
+ output_files[key] = files.map { |f| File.expand_path(f) }
117
121
  end
122
+
118
123
  # run all objectives for output
119
124
  results = {}
120
- # create temp dir
121
- Dir.chdir(self.create_tempdir) do
122
- @objectives.each_pair do |name, objective|
123
- results[name] = self.run_objective(objective, name, output, threads)
124
- end
125
- if cleanup == 1
126
- # remove all but essential files
127
- essential_files = @domain.keep_intermediates
128
- if essential_files
129
- @objectives.values.each{ |objective| essential_files += objective.essential_files }
130
- end
131
- Dir["*"].each do |file|
132
- next if File.directory? file
133
- if essential_files && essential_files.include?(file)
134
- `gzip #{file}` if @domain.gzip_intermediates
135
- FileUtils.mv("#{file}.gz", '..')
136
- end
137
- end
138
- end
139
- end
140
- if cleanup
141
- # clean up temp dir
142
- FileUtils.rm_rf @last_tempdir
125
+ @objectives.each_pair do |name, objective|
126
+ results[name] = self.run_objective(objective, name, raw_output, output_files, threads)
143
127
  end
128
+
144
129
  if allresults
145
130
  return {:results => results,
146
131
  :reduced => self.dimension_reduce(results)}
@@ -151,20 +136,6 @@ module Biopsy
151
136
  end
152
137
  end
153
138
 
154
- # create a guaranteed random temporary directory for storing outputs
155
- # return the dirname
156
- def create_tempdir
157
- token = loop do
158
- # generate random dirnames until we find one that
159
- # doesn't exist
160
- test_token = SecureRandom.hex
161
- break test_token unless File.exists? test_token
162
- end
163
- Dir.mkdir(token)
164
- @last_tempdir = token
165
- return token
166
- end
167
-
168
139
  end
169
140
 
170
141
  end
@@ -42,8 +42,12 @@ module Biopsy
42
42
  end
43
43
  end
44
44
 
45
+ def setup *args
46
+ nil
47
+ end
48
+
45
49
  # return the next parameter set to evaluate
46
- def run_one_iteration(*args)
50
+ def run_one_iteration *args
47
51
  @combinations.pop
48
52
  rescue
49
53
  nil
@@ -62,5 +66,14 @@ module Biopsy
62
66
  generate_combinations(index + 1, opts)
63
67
  end
64
68
  end
69
+
70
+ def knows_starting_point?
71
+ true
72
+ end
73
+
74
+ def select_starting_point
75
+ self.run_one_iteration
76
+ end
77
+
65
78
  end
66
79
  end
@@ -201,8 +201,10 @@ module Biopsy
201
201
  @backtracks = 1.0
202
202
 
203
203
  # convergence
204
- @num_threads = 2
204
+ @num_threads = 5
205
205
  @threads = []
206
+ @convergence_alpha = 0.05
207
+ @global_best = {:parameters => nil, :score => nil}
206
208
 
207
209
  end # initialize
208
210
 
@@ -249,6 +251,8 @@ module Biopsy
249
251
  thread.loaded = false
250
252
  end
251
253
  @current_thread = @num_threads - 2
254
+ # adjust the alpha for multiple testing in convergence
255
+ @adjusted_alpha = @convergence_alpha / @num_threads
252
256
  end
253
257
 
254
258
  def load_next_thread
@@ -276,6 +280,13 @@ module Biopsy
276
280
  else
277
281
  @iterations_since_best += 1
278
282
  end
283
+ if @global_best[:score].nil? || @best[:score] > @global_best[:score]
284
+ @global_best = @best.clone
285
+ end
286
+ end
287
+
288
+ def best
289
+ @global_best
279
290
  end
280
291
 
281
292
  # use probability distributions to define the
@@ -385,9 +396,26 @@ module Biopsy
385
396
  # check termination conditions
386
397
  # and return true if met
387
398
  def finished?
388
- return false if @threads.first.recent_scores.size < @jump_cutoff
389
- scores = @threads.map { |t| t.recent_scores }
390
- scores.map { |s| s.mean }.uniq.length == 1
399
+ return false unless @threads.all? { |t| t.recent_scores.size == @jump_cutoff }
400
+ probabilities = self.recent_scores_combination_test
401
+ n_significant = 0
402
+ probabilities.each do |mann_u, levene|
403
+ if mann_u <= @adjusted_alpha && levene <= @convergence_alpha
404
+ n_significant += 1
405
+ end
406
+ end
407
+ finish = n_significant >= probabilities.size * 0.5
408
+ end
409
+
410
+ # returns a matrix of correlation probabilities for recent
411
+ # scores between all threads
412
+ def recent_scores_combination_test
413
+ combinations =
414
+ @threads.map{ |t| t.recent_scores.to_scale }.combination(2).to_a
415
+ combinations.map do |a, b|
416
+ [Statsample::Test.u_mannwhitney(a, b).probability_exact,
417
+ Statsample::Test::Levene.new([a,b]).probability]
418
+ end
391
419
  end
392
420
 
393
421
  # True if this algorithm chooses its own starting point
@@ -25,11 +25,11 @@ module Biopsy
25
25
 
26
26
  attr_accessor :base_dir
27
27
  attr_accessor :target_dir
28
- attr_accessor :domain_dir
29
- attr_accessor :domain
30
28
  attr_accessor :objectives_dir
31
29
  attr_accessor :objectives_subset
32
30
  attr_accessor :sweep_cutoff
31
+ attr_accessor :keep_intermediates
32
+ attr_accessor :gzip_intermediates
33
33
 
34
34
  def initialize
35
35
  self.set_defaults
@@ -40,11 +40,11 @@ module Biopsy
40
40
  @config_file = '~/.biopsyrc'
41
41
  @base_dir = ['.']
42
42
  @target_dir = ['targets']
43
- @domain_dir = ['domains']
44
- @domain = 'test_domain'
45
43
  @objectives_dir = ['objectives']
46
44
  @objectives_subset = nil
47
45
  @sweep_cutoff = 100
46
+ @keep_intermediates = false
47
+ @gzip_intermediates = false
48
48
  end
49
49
 
50
50
  # Loads settings from a YAML config file. If no file is
@@ -86,25 +86,6 @@ module Biopsy
86
86
  all_settings.to_yaml
87
87
  end
88
88
 
89
- # Locate the first YAML config file whose name
90
- # excluding extension matches +:name+ (case insensitive)
91
- # in dirs listed by the +:dir_key+ setting.
92
- def locate_config(dir_key, name)
93
- dir_key = "@#{dir_key.to_s}".to_sym
94
- unless self.instance_variables.include? dir_key
95
- raise SettingsError.new "no setting found for compulsory key #{dir_key}"
96
- end
97
- self.instance_variable_get(dir_key).each do |dir|
98
- Dir.chdir ::File.expand_path(dir) do
99
- Dir[name + '.yml'].each do |file|
100
- return ::File.expand_path(file) if ::File.basename(file, '.yml').downcase == name.downcase
101
- end
102
- end
103
- end
104
-
105
- nil
106
- end
107
-
108
89
  end # Settings
109
90
 
110
91
  end # Biopsy
data/lib/biopsy/target.rb CHANGED
@@ -5,25 +5,13 @@ module Biopsy
5
5
 
6
6
  class Target
7
7
  require 'yaml'
8
- require 'ostruct'
9
-
10
- # array of input files expected by the target constructor
11
- attr_accessor :input_files
12
- # array of output files to keep for submission to objective
13
- # functions during optimisation
14
- attr_accessor :output_files
15
- # hash mapping parameters to the ranges of values they can take
16
- attr_reader :parameter_ranges
17
- # path to the constructor code
18
- attr_reader :constructor_path
19
- attr_reader :domain
8
+ require 'set'
20
9
 
21
- # create a new Target instance.
22
- # arguments:
23
- # +:domain+ the domain to which this target belongs (see Domain documentation)
24
- def initialize domain
25
- @domain = domain
26
- end
10
+ attr_accessor :parameters
11
+ attr_accessor :options
12
+ attr_accessor :output
13
+ attr_accessor :name
14
+ attr_reader :constructor_path
27
15
 
28
16
  # load target with +name+.
29
17
  def load_by_name name
@@ -31,17 +19,9 @@ module Biopsy
31
19
  raise TargetLoadError.new("Target definition file does not exist for #{name}") if path.nil?
32
20
  config = YAML::load_file(path)
33
21
  raise TargetLoadError.new("Target definition file #{path} is not valid YAML") if config.nil?
34
- missing = self.check_config config.deep_symbolize
35
- if missing
36
- msg = "Target definition file #{path} is missing required fields: #{missing}"
37
- raise TargetLoadError.new(msg)
38
- end
39
- errors = self.validate_config config
40
- unless errors.empty?
41
- raise TargetLoadError.new("Target definition file #{path} contains the following errors:\n - #{errors.join("\n - ")}")
42
- end
22
+ config = config.deep_symbolize
43
23
  self.store_config config
44
- self.check_constructor
24
+ self.check_constructor name
45
25
  self.load_constructor
46
26
  end
47
27
 
@@ -49,48 +29,52 @@ module Biopsy
49
29
  # to the definition YAML file. All +:target_dir+s defined in Settings are
50
30
  # searched and the first matching YAML file is loaded.
51
31
  def locate_definition name
52
- Settings.instance.locate_config(:target_dir, name)
32
+ self.locate_file(name + '.yml')
53
33
  end
54
34
 
55
- # verify that +:config+ contains values for all essential target settings
56
- # returning false if no keys are missing, or an array of the missing keys
57
- # if any cannot be found
58
- def check_config config
59
- required = %w(input_files output_files parameter_ranges constructor_path)
60
- missing = false
61
- required.each do |key|
62
- unless config.has_key? key.to_sym
63
- missing ||= []
64
- missing << key
35
+ # store the values in +:config+, checking they are valid
36
+ def store_config config
37
+ required = Set.new([:name, :parameters, :output])
38
+ missing = required - config.keys
39
+ raise TargetLoadError.new("Required keys are missing from target definition: #{missing.to_a.join(",")}") unless missing.empty?
40
+ config.each_pair do |param, data|
41
+ case param
42
+ when :name
43
+ raise TargetLoadError.new('Target name must be a string') unless data.is_a? String
44
+ @name = data
45
+ when :shortname
46
+ raise TargetLoadError.new('Target shortname must be a string') unless data.is_a? String
47
+ @shortname = data
48
+ when :parameters
49
+ self.generate_parameters data
50
+ when :output
51
+ raise TargetLoadError.new('Target output must be a hash') unless data.is_a?(Hash)
52
+ @output = data
65
53
  end
66
54
  end
67
- missing
68
- end
69
-
70
- # validate the config against the domain definition. Return an array
71
- # whose length will be the number of errors found. Thus an array of
72
- # length 0 indicates that the config is valid according to the domain
73
- # specification.
74
- def validate_config config
75
- @domain.target_valid? config
76
55
  end
77
56
 
78
- # Store the values in +:config+
79
- def store_config config
80
- config.each_pair do |key, value|
81
- self.instance_variable_set('@' + key.to_s, value)
57
+ # Locate a file with name in one of the target_dirs
58
+ def locate_file name
59
+ Settings.instance.target_dir.each do |dir|
60
+ Dir.chdir File.expand_path(dir) do
61
+ return File.expand_path(name) if File.exists? name
62
+ end
82
63
  end
64
+ raise TargetLoadError.new("Couldn't find file #{name}")
65
+ nil
83
66
  end
84
67
 
85
68
  # Validate the constructor. True if valid, false otherwise.
86
- def check_constructor
87
- raise "constructor path is not defined for this target" if @constructor_path.nil?
69
+ def check_constructor name
70
+ @constructor_path = self.locate_file name + '.rb'
71
+ raise TargetLoadError.new("constructor path is not defined for this target") if @constructor_path.nil?
88
72
  self.valid_ruby? @constructor_path
89
73
  end
90
74
 
91
75
  # Load constructor
92
76
  def load_constructor
93
- require File.join(Settings.instance.target_dir, @constructor_path)
77
+ require @constructor_path
94
78
  file_name = File.basename(@constructor_path, '.rb')
95
79
  constructor_name = file_name.camelize
96
80
  @constructor = Module.const_get(constructor_name).new
@@ -104,10 +88,41 @@ module Biopsy
104
88
  # true if file is valid ruby
105
89
  def valid_ruby? file
106
90
  return false unless ::File.exists? file
107
- result = `ruby -c #{file} &> /dev/null`
91
+ result = `ruby -c #{file} > /dev/null 2>&1`
108
92
  !result.size.zero?
109
93
  end
110
94
 
95
+ # convert parameter specification to a hash of arrays and ranges
96
+ def generate_parameters params
97
+ @parameters = {}
98
+ @options = {}
99
+ params.each_pair do |param, data|
100
+ if data[:opt]
101
+ # optimise this parameter
102
+ if data[:values]
103
+ # definition has provided an array of values
104
+ raise TargetLoadError.new("'values' for parameter #{param} is not an array") unless data[:values].is_a? Array
105
+ @parameters[param] = data[:values]
106
+ else
107
+ # definition has specified a range
108
+ min, max, step = data[:min], data[:max], data[:step]
109
+ raise TargetLoadError.new("min and max must be set for parameter #{param}") unless min && max
110
+ range = (min..max)
111
+ range = range.step(step) if step
112
+ @parameters[param] = range.to_a
113
+ end
114
+ else
115
+ # present option to user
116
+
117
+ end
118
+ end
119
+ end
120
+
121
+ # return the total number of possible permutations of
122
+ def count_parameter_permutations
123
+ @parameters.each_pair.map{ |k, v| v }.reduce(1) { |n, arr| n * arr.size }
124
+ end
125
+
111
126
  end # end of class Domain
112
127
 
113
128
  end # end of module Biopsy
@@ -4,7 +4,7 @@ module Biopsy
4
4
  module VERSION
5
5
  MAJOR = 0
6
6
  MINOR = 1
7
- PATCH = 0
7
+ PATCH = 1
8
8
  BUILD = 'alpha'
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
data/lib/biopsy.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "biopsy/version"
2
2
  require "biopsy/base_extensions"
3
3
  require "biopsy/settings"
4
- require "biopsy/domain"
5
4
  require "biopsy/experiment"
6
5
  require "biopsy/target"
7
6
  require "biopsy/objective_handler"