Class: FeldtRuby::Optimize::Objective
- Includes:
- Logging
- Defined in:
- lib/feldtruby/optimize/objective.rb
Overview
An Objective maps candidate solutions to qualities so they can be compared and ranked.
One objective can have one or more sub-objectives, called goals. Each goal is specified as a separate method and its name indicates if the returned Numeric should be minimized or maximized. To create your own objective you subclass and add instance methods named as
goal_min_qualityAspectName (for a goal value to be minimized), or
goal_max_qualityAspectName (for a goal value to be minimized).
An objective keeps track of the min and max value that has been seen so far for each goal. An objective has version numbers to indicate the number of times a new min or max value has been identified for a goal.
This base class uses weigthed sum as the quality mapper and number comparator as the comparator. But the mapper and comparator to be used can, of course, be changed.
Direct Known Subclasses
DiversityObjective, ObjectiveMaximizeBlock, ObjectiveMinimizeBlock, MinContinousFunction
Defined Under Namespace
Classes: LowerAggregateQualityIsBetterComparator, MeanWeigthedGlobalRatios, QualityAggregator, WeightedSumAggregator
Instance Attribute Summary collapse
-
#best_candidate ⇒ Object
readonly
Returns the value of attribute best_candidate.
-
#current_version ⇒ Object
Current version of this objective.
-
#global_max_values_per_goal ⇒ Object
readonly
Returns the value of attribute global_max_values_per_goal.
-
#global_min_values_per_goal ⇒ Object
readonly
Returns the value of attribute global_min_values_per_goal.
Attributes included from Logging
Instance Method Summary collapse
-
#aggregated_quality(subQualities) ⇒ Object
Return the aggregated quality value given sub qualities.
-
#calculate_sub_qualities_of(candidate, updateGlobals = true) ⇒ Object
Calculate the sub-qualities from scratch, i.e.
-
#goal_methods ⇒ Object
Return the names of the goal methods.
-
#hat_compare(candidate1, candidate2) ⇒ Object
Return true iff candidate1 is better than candidate2.
-
#initialize(qualityAggregator = MeanWeigthedGlobalRatios.new, comparator = LowerAggregateQualityIsBetterComparator.new) ⇒ Objective
constructor
WeightedSumAggregator.new,.
-
#invalidate_quality_of(candidate) ⇒ Object
Invalidate the current quality object to ensure it will be recalculated the next time quality_of is called with this candidate.
-
#is_better_than?(candidate1, candidate2) ⇒ Boolean
Return true iff candidate1 is better than candidate2.
-
#is_better_than_for_goal?(index, candidate1, candidate2) ⇒ Boolean
Return true iff candidate1 is better than candidate2 for goal index.
-
#is_goal_method?(methodNameAsSymbolOrString) ⇒ Boolean
Return true iff the method with the given name is a goal method.
-
#is_min_goal?(index) ⇒ Boolean
Return true iff the goal method with the given index is a minimizing goal.
-
#is_min_goal_method?(methodNameAsSymbolOrString) ⇒ Boolean
Return true iff the method with the given name is a goal method.
-
#map_candidate_to_object_to_be_evaluated(candidate) ⇒ Object
The candidate objects can be mapped to another object before we call the goal methods to calc the quality values.
- #note_end_of_optimization(optimizer) ⇒ Object
-
#num_goals ⇒ Object
Return the number of goals of this objective.
-
#quality_of(candidate, weights = self.weights) ⇒ Object
Return a quality value for a given candidate and weights for the whole objective for a given candidate.
-
#rank_candidates(candidates, weights = self.weights) ⇒ Object
Rank candidates from best to worst.
-
#sub_qualities_of(candidate, updateGlobals = true) ⇒ Object
Return a vector of the “raw” quality values, i.e.
-
#weights ⇒ Object
Weights is a map from goal method names to a number that represents the weight for that goal.
-
#weights=(goalNameToNumberHash) ⇒ Object
Set the weights given a hash mapping each goal method name to a number.
Methods included from Logging
#__find_logger_set_on_instance_vars, #new_default_logger, #setup_logger_and_distribute_to_instance_variables
Constructor Details
#initialize(qualityAggregator = MeanWeigthedGlobalRatios.new, comparator = LowerAggregateQualityIsBetterComparator.new) ⇒ Objective
WeightedSumAggregator.new,
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/feldtruby/optimize/objective.rb', line 44 def initialize(qualityAggregator = MeanWeigthedGlobalRatios.new, #WeightedSumAggregator.new, comparator = LowerAggregateQualityIsBetterComparator.new) # A quality aggregator maps the goal values of a candidate to a single number. @aggregator = qualityAggregator @aggregator.objective = self # A comparator compares two or more candidates and ranks them based on their # qualities. @comparator = comparator @comparator.objective = self self.current_version = 0 # We set all mins to INFINITY. This ensures that the first value seen will # be smaller, and thus set as the new min. @global_min_values_per_goal = [Float::INFINITY] * num_goals # We set all maxs to -INFINITY. This ensures that the first value seen will # be larger, and thus set as the new max. @global_max_values_per_goal = [-Float::INFINITY] * num_goals setup_logger_and_distribute_to_instance_variables() # An array to keep the best object per goal. @best_objects = Array.new end |
Instance Attribute Details
#best_candidate ⇒ Object (readonly)
Returns the value of attribute best_candidate.
219 220 221 |
# File 'lib/feldtruby/optimize/objective.rb', line 219 def best_candidate @best_candidate end |
#current_version ⇒ Object
Current version of this objective. Is updated when the min or max values for a sub-objective has been updated or when the weights are changed. Candidates are always compared based on the latest version of an objective.
40 41 42 |
# File 'lib/feldtruby/optimize/objective.rb', line 40 def current_version @current_version end |
#global_max_values_per_goal ⇒ Object (readonly)
Returns the value of attribute global_max_values_per_goal.
42 43 44 |
# File 'lib/feldtruby/optimize/objective.rb', line 42 def global_max_values_per_goal @global_max_values_per_goal end |
#global_min_values_per_goal ⇒ Object (readonly)
Returns the value of attribute global_min_values_per_goal.
42 43 44 |
# File 'lib/feldtruby/optimize/objective.rb', line 42 def global_min_values_per_goal @global_min_values_per_goal end |
Instance Method Details
#aggregated_quality(subQualities) ⇒ Object
Return the aggregated quality value given sub qualities.
204 205 206 |
# File 'lib/feldtruby/optimize/objective.rb', line 204 def aggregated_quality(subQualities) @aggregator.aggregate_from_sub_qualities(subQualities, weights) end |
#calculate_sub_qualities_of(candidate, updateGlobals = true) ⇒ Object
Calculate the sub-qualities from scratch, i.e. by mapping the candidate to an object to be evaluated and then calculate the value of each goal.
141 142 143 144 145 146 |
# File 'lib/feldtruby/optimize/objective.rb', line 141 def calculate_sub_qualities_of(candidate, updateGlobals = true) obj = map_candidate_to_object_to_be_evaluated(candidate) sub_qualitites = goal_methods.map {|gmethod| self.send(gmethod, obj)} update_global_mins_and_maxs(sub_qualitites, candidate) if updateGlobals sub_qualitites end |
#goal_methods ⇒ Object
Return the names of the goal methods.
79 80 81 |
# File 'lib/feldtruby/optimize/objective.rb', line 79 def goal_methods @goal_methods ||= self.methods.select {|m| is_goal_method?(m)} end |
#hat_compare(candidate1, candidate2) ⇒ Object
Return true iff candidate1 is better than candidate2. Will update their quality values if they are out of date.
193 194 195 |
# File 'lib/feldtruby/optimize/objective.rb', line 193 def hat_compare(candidate1, candidate2) @comparator.hat_compare(candidate1, candidate2) end |
#invalidate_quality_of(candidate) ⇒ Object
Invalidate the current quality object to ensure it will be recalculated the next time quality_of is called with this candidate. This is needed when there is a new best candidate set in an Archive, for example.
169 170 171 |
# File 'lib/feldtruby/optimize/objective.rb', line 169 def invalidate_quality_of(candidate) update_quality_value_in_object candidate, nil end |
#is_better_than?(candidate1, candidate2) ⇒ Boolean
Return true iff candidate1 is better than candidate2. Will update their quality values if they are out of date.
187 188 189 |
# File 'lib/feldtruby/optimize/objective.rb', line 187 def is_better_than?(candidate1, candidate2) @comparator.is_better_than?(candidate1, candidate2) end |
#is_better_than_for_goal?(index, candidate1, candidate2) ⇒ Boolean
Return true iff candidate1 is better than candidate2 for goal index. Will update their quality values if they are out of date.
199 200 201 |
# File 'lib/feldtruby/optimize/objective.rb', line 199 def is_better_than_for_goal?(index, candidate1, candidate2) @comparator.is_better_than_for_goal?(index, candidate1, candidate2) end |
#is_goal_method?(methodNameAsSymbolOrString) ⇒ Boolean
Return true iff the method with the given name is a goal method.
89 90 91 |
# File 'lib/feldtruby/optimize/objective.rb', line 89 def is_goal_method?(methodNameAsSymbolOrString) (methodNameAsSymbolOrString.to_s =~ /^(goal|objective)_(min|max)_([\w_]+)$/) != nil end |
#is_min_goal?(index) ⇒ Boolean
Return true iff the goal method with the given index is a minimizing goal.
84 85 86 |
# File 'lib/feldtruby/optimize/objective.rb', line 84 def is_min_goal?(index) (@is_min_goal ||= (goal_methods.map {|m| is_min_goal_method?(m)}))[index] end |
#is_min_goal_method?(methodNameAsSymbolOrString) ⇒ Boolean
Return true iff the method with the given name is a goal method.
94 95 96 |
# File 'lib/feldtruby/optimize/objective.rb', line 94 def is_min_goal_method?(methodNameAsSymbolOrString) (methodNameAsSymbolOrString.to_s =~ /^(goal|objective)_min_([\w_]+)$/) != nil end |
#map_candidate_to_object_to_be_evaluated(candidate) ⇒ Object
The candidate objects can be mapped to another object before we call the goal methods to calc the quality values. Default is no mapping but subclasses can override this for more complex evaluation schemes.
101 102 103 |
# File 'lib/feldtruby/optimize/objective.rb', line 101 def map_candidate_to_object_to_be_evaluated(candidate) candidate end |
#note_end_of_optimization(optimizer) ⇒ Object
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/feldtruby/optimize/objective.rb', line 208 def note_end_of_optimization(optimizer) logger.log_data :objective_optimization_ends, { "Best object, aggregated" => @best_candidate, "Quality of best object" => @best_quality_value, "Version" => current_version, "Comparator" => @comparator, "Aggregator" => @aggregator, "Best objects per goal" => @best_objects }, "Objective: Report at end of optimization", true end |
#num_goals ⇒ Object
Return the number of goals of this objective.
74 75 76 |
# File 'lib/feldtruby/optimize/objective.rb', line 74 def num_goals @num_goals ||= goal_methods.length end |
#quality_of(candidate, weights = self.weights) ⇒ Object
Return a quality value for a given candidate and weights for the whole objective for a given candidate. Updates the best candidate if this is the best seen so far.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/feldtruby/optimize/objective.rb', line 151 def quality_of(candidate, weights = self.weights) q = quality_in_object(candidate) return q if q sub_qualities = sub_qualities_of(candidate) qv = update_quality_value_of candidate, sub_qualities, weights update_best_candidate candidate, qv qv end |
#rank_candidates(candidates, weights = self.weights) ⇒ Object
Rank candidates from best to worst. Candidates that have the same quality are randomly ordered.
175 176 177 178 179 180 181 182 183 |
# File 'lib/feldtruby/optimize/objective.rb', line 175 def rank_candidates(candidates, weights = self.weights) # We first ensure all candidates have calculated sub-qualities, then we # call the comparator. This ensures that the gobals are already correctly # updated on each quality value. candidates.map {|c| sub_qualities_of(c, true)} # Now just let the comparator rank the candidates @comparator.rank_candidates candidates, weights end |
#sub_qualities_of(candidate, updateGlobals = true) ⇒ Object
Return a vector of the “raw” quality values, i.e. the fitness value for each goal. If we have already calculated the sub-qualities we just return them. If not we calculate them.
132 133 134 135 136 137 |
# File 'lib/feldtruby/optimize/objective.rb', line 132 def sub_qualities_of(candidate, updateGlobals = true) # Return the sub_qualities if we already have calculated them. sqs = sub_qualities_if_already_calculated?(candidate) return sqs if sqs calculate_sub_qualities_of candidate, updateGlobals end |
#weights ⇒ Object
Weights is a map from goal method names to a number that represents the weight for that goal. Default is to set all weights to 1.
107 108 109 |
# File 'lib/feldtruby/optimize/objective.rb', line 107 def weights @weights ||= ([1] * num_goals) end |
#weights=(goalNameToNumberHash) ⇒ Object
Set the weights given a hash mapping each goal method name to a number. The mapper and/or comparator can use the weights in their calculations.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/feldtruby/optimize/objective.rb', line 113 def weights=(goalNameToNumberHash) raise "Must be same number of weights as there are goals (#{num_aspects}), but is #{weights.length}" unless weights.length == num_goals weights = goal_methods.map {|gm| goalNameToNumberHash[gm]} logger.log_value :objective_weights_changed, {"New weights" => goalNameToNumberHash}, "Weights updated from #{@weights} to #{weights}" inc_version_number @weights = weights end |