musa-dsl 0.30.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.version +6 -0
- data/.yardopts +7 -0
- data/Gemfile +0 -1
- data/README.md +227 -6
- data/docs/README.md +83 -0
- data/docs/api-reference.md +86 -0
- data/docs/getting-started/quick-start.md +93 -0
- data/docs/getting-started/tutorial.md +58 -0
- data/docs/subsystems/core-extensions.md +316 -0
- data/docs/subsystems/datasets.md +465 -0
- data/docs/subsystems/generative.md +290 -0
- data/docs/subsystems/matrix.md +63 -0
- data/docs/subsystems/midi.md +123 -0
- data/docs/subsystems/music.md +544 -0
- data/docs/subsystems/musicxml-builder.md +264 -0
- data/docs/subsystems/neumas.md +71 -0
- data/docs/subsystems/repl.md +135 -0
- data/docs/subsystems/sequencer.md +98 -0
- data/docs/subsystems/series.md +302 -0
- data/docs/subsystems/transcription.md +152 -0
- data/docs/subsystems/transport.md +177 -0
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
- data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
- data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
- data/lib/musa-dsl/core-ext/extension.rb +53 -0
- data/lib/musa-dsl/core-ext/hashify.rb +162 -1
- data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
- data/lib/musa-dsl/core-ext/with.rb +114 -0
- data/lib/musa-dsl/datasets/dataset.rb +109 -0
- data/lib/musa-dsl/datasets/delta-d.rb +78 -0
- data/lib/musa-dsl/datasets/e.rb +186 -2
- data/lib/musa-dsl/datasets/gdv.rb +279 -2
- data/lib/musa-dsl/datasets/gdvd.rb +201 -0
- data/lib/musa-dsl/datasets/helper.rb +75 -0
- data/lib/musa-dsl/datasets/p.rb +177 -2
- data/lib/musa-dsl/datasets/packed-v.rb +91 -0
- data/lib/musa-dsl/datasets/pdv.rb +136 -1
- data/lib/musa-dsl/datasets/ps.rb +134 -4
- data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
- data/lib/musa-dsl/datasets/score/render.rb +105 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
- data/lib/musa-dsl/datasets/score.rb +279 -0
- data/lib/musa-dsl/datasets/v.rb +88 -0
- data/lib/musa-dsl/generative/darwin.rb +215 -1
- data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
- data/lib/musa-dsl/generative/markov.rb +135 -3
- data/lib/musa-dsl/generative/rules.rb +312 -4
- data/lib/musa-dsl/generative/variatio.rb +286 -2
- data/lib/musa-dsl/logger/logger.rb +267 -2
- data/lib/musa-dsl/matrix/matrix.rb +256 -10
- data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
- data/lib/musa-dsl/midi/midi-voices.rb +275 -4
- data/lib/musa-dsl/music/chord-definition.rb +233 -1
- data/lib/musa-dsl/music/chord-definitions.rb +33 -6
- data/lib/musa-dsl/music/chords.rb +353 -2
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
- data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
- data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
- data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
- data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
- data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
- data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
- data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
- data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
- data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
- data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
- data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
- data/lib/musa-dsl/music/scales.rb +1384 -40
- data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
- data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
- data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
- data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
- data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
- data/lib/musa-dsl/neumas/neumas.rb +67 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
- data/lib/musa-dsl/repl/repl.rb +550 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
- data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
- data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
- data/lib/musa-dsl/series/array-to-serie.rb +37 -1
- data/lib/musa-dsl/series/base-series.rb +843 -5
- data/lib/musa-dsl/series/buffer-serie.rb +54 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
- data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
- data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
- data/lib/musa-dsl/series/proxy-serie.rb +67 -0
- data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
- data/lib/musa-dsl/series/queue-serie.rb +78 -0
- data/lib/musa-dsl/series/series-composer.rb +701 -0
- data/lib/musa-dsl/series/timed-serie.rb +473 -28
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
- data/lib/musa-dsl/transcription/transcription.rb +265 -0
- data/lib/musa-dsl/transport/clock.rb +125 -0
- data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
- data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
- data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
- data/lib/musa-dsl/transport/timer-clock.rb +183 -1
- data/lib/musa-dsl/transport/timer.rb +83 -0
- data/lib/musa-dsl/transport/transport.rb +318 -0
- data/lib/musa-dsl/version.rb +2 -1
- data/lib/musa-dsl.rb +132 -25
- data/musa-dsl.gemspec +25 -18
- metadata +158 -16
|
@@ -1,8 +1,111 @@
|
|
|
1
1
|
require_relative '../core-ext/with'
|
|
2
2
|
|
|
3
3
|
module Musa
|
|
4
|
+
# Evolutionary selection algorithm based on fitness evaluation.
|
|
5
|
+
#
|
|
6
|
+
# Darwin implements a selection algorithm inspired by natural selection,
|
|
7
|
+
# evaluating and ranking a population of objects based on defined measures
|
|
8
|
+
# (features and dimensions) and weights. Objects are measured, scored, and
|
|
9
|
+
# sorted by fitness for evolutionary algorithms or optimization.
|
|
10
|
+
#
|
|
11
|
+
# ## Core Concepts
|
|
12
|
+
#
|
|
13
|
+
# - **Population**: Collection of objects to evaluate
|
|
14
|
+
# - **Measures**: Evaluation criteria for each object
|
|
15
|
+
# - **Features**: Boolean flags (present/absent)
|
|
16
|
+
# - **Dimensions**: Numeric values (continuous)
|
|
17
|
+
# - **Die**: Mark object as non-viable (eliminated)
|
|
18
|
+
# - **Weights**: Importance multipliers for features/dimensions
|
|
19
|
+
# - **Fitness**: Calculated score from normalized dimensions and features
|
|
20
|
+
# - **Selection**: Population sorted by fitness (highest first)
|
|
21
|
+
#
|
|
22
|
+
# ## Evaluation Process
|
|
23
|
+
#
|
|
24
|
+
# 1. **Measure**: Evaluate each object with measures block
|
|
25
|
+
# 2. **Normalize**: Scale dimension values to 0-1 range
|
|
26
|
+
# 3. **Weight**: Apply weights to dimensions and features
|
|
27
|
+
# 4. **Score**: Calculate total fitness for each object
|
|
28
|
+
# 5. **Sort**: Order population by fitness (descending)
|
|
29
|
+
#
|
|
30
|
+
# ## Musical Applications
|
|
31
|
+
#
|
|
32
|
+
# - Select best harmonic progressions from generated candidates
|
|
33
|
+
# - Rank melodic variations by aesthetic criteria
|
|
34
|
+
# - Optimize rhythmic patterns for complexity/simplicity
|
|
35
|
+
# - Evolve musical structures through iterative selection
|
|
36
|
+
#
|
|
37
|
+
# @example Basic selection with features and dimensions
|
|
38
|
+
# darwin = Musa::Darwin::Darwin.new do
|
|
39
|
+
# measures do |object|
|
|
40
|
+
# # Kill objects with unwanted property
|
|
41
|
+
# die if object[:bad_property]
|
|
42
|
+
#
|
|
43
|
+
# # Binary features
|
|
44
|
+
# feature :has_alpha if object[:type] == :alpha
|
|
45
|
+
# feature :has_beta if object[:type] == :beta
|
|
46
|
+
#
|
|
47
|
+
# # Numeric dimension (negative to prefer lower values)
|
|
48
|
+
# dimension :complexity, -object[:complexity].to_f
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# # Weight contributions to fitness
|
|
52
|
+
# weight complexity: 2.0, has_alpha: 1.0, has_beta: -0.5
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# population = generate_candidates()
|
|
56
|
+
# selected = darwin.select(population)
|
|
57
|
+
# # Returns population sorted by fitness (best first)
|
|
58
|
+
#
|
|
59
|
+
# @example Musical chord progression selection
|
|
60
|
+
# darwin = Musa::Darwin::Darwin.new do
|
|
61
|
+
# measures do |progression|
|
|
62
|
+
# # Eliminate progressions with parallel fifths
|
|
63
|
+
# die if has_parallel_fifths?(progression)
|
|
64
|
+
#
|
|
65
|
+
# # Prefer smooth voice leading
|
|
66
|
+
# dimension :voice_leading, -total_voice_leading_distance(progression)
|
|
67
|
+
#
|
|
68
|
+
# # Prefer certain cadences
|
|
69
|
+
# feature :authentic_cadence if ends_with_V_I?(progression)
|
|
70
|
+
# feature :plagal_cadence if ends_with_IV_I?(progression)
|
|
71
|
+
#
|
|
72
|
+
# # Penalize excessive chromaticism
|
|
73
|
+
# dimension :chromaticism, -count_chromatic_notes(progression)
|
|
74
|
+
# end
|
|
75
|
+
#
|
|
76
|
+
# weight voice_leading: 3.0,
|
|
77
|
+
# authentic_cadence: 2.0,
|
|
78
|
+
# plagal_cadence: 1.0,
|
|
79
|
+
# chromaticism: 1.5
|
|
80
|
+
# end
|
|
81
|
+
#
|
|
82
|
+
# candidates = generate_progressions()
|
|
83
|
+
# best = darwin.select(candidates).first(10) # Top 10 progressions
|
|
84
|
+
#
|
|
85
|
+
# @see Darwin Main evolutionary selector class
|
|
86
|
+
# @see Musa::Extension::With DSL context management for evaluation blocks
|
|
87
|
+
# @see https://en.wikipedia.org/wiki/Evolutionary_algorithm Evolutionary algorithm (Wikipedia)
|
|
88
|
+
# @see https://en.wikipedia.org/wiki/Fitness_function Fitness function (Wikipedia)
|
|
4
89
|
module Darwin
|
|
90
|
+
# Evolutionary selector for population-based optimization.
|
|
91
|
+
#
|
|
92
|
+
# Evaluates population using measures and weights, returning sorted
|
|
93
|
+
# population by fitness score.
|
|
5
94
|
class Darwin
|
|
95
|
+
# Creates Darwin selector with evaluation rules.
|
|
96
|
+
#
|
|
97
|
+
# @yield evaluation DSL block
|
|
98
|
+
# @yieldreturn [void]
|
|
99
|
+
#
|
|
100
|
+
# @raise [ArgumentError] if no block given
|
|
101
|
+
#
|
|
102
|
+
# @example
|
|
103
|
+
# darwin = Darwin.new do
|
|
104
|
+
# measures { |obj| dimension :value, obj[:score] }
|
|
105
|
+
# weight value: 1.0
|
|
106
|
+
# end
|
|
107
|
+
#
|
|
108
|
+
# @return [void]
|
|
6
109
|
def initialize(&block)
|
|
7
110
|
raise ArgumentError, 'block is needed' unless block
|
|
8
111
|
|
|
@@ -12,6 +115,22 @@ module Musa
|
|
|
12
115
|
@weights = main_context._weights
|
|
13
116
|
end
|
|
14
117
|
|
|
118
|
+
# Selects and ranks population by fitness.
|
|
119
|
+
#
|
|
120
|
+
# Evaluates each object with measures, normalizes dimensions across
|
|
121
|
+
# population, applies weights, and returns population sorted by fitness
|
|
122
|
+
# (highest first). Objects marked as died are excluded.
|
|
123
|
+
#
|
|
124
|
+
# @param population [Array] objects to evaluate
|
|
125
|
+
#
|
|
126
|
+
# @return [Array] population sorted by fitness (descending)
|
|
127
|
+
#
|
|
128
|
+
# @example
|
|
129
|
+
# candidates = [obj1, obj2, obj3, ...]
|
|
130
|
+
# ranked = darwin.select(candidates)
|
|
131
|
+
# best = ranked.first # Highest fitness
|
|
132
|
+
# worst = ranked.last # Lowest fitness
|
|
133
|
+
# top10 = ranked.first(10) # Top 10
|
|
15
134
|
def select(population)
|
|
16
135
|
measured_objects = []
|
|
17
136
|
|
|
@@ -46,7 +165,8 @@ module Musa
|
|
|
46
165
|
|
|
47
166
|
measure.dimensions.each do |dimension_name, value|
|
|
48
167
|
limit = limits[dimension_name]
|
|
49
|
-
measure.normalized_dimensions[dimension_name] =
|
|
168
|
+
measure.normalized_dimensions[dimension_name] =
|
|
169
|
+
limit[:range].zero? ? 0.5 : (value - limit[:min]) / limit[:range]
|
|
50
170
|
end
|
|
51
171
|
|
|
52
172
|
# warn "Darwin.select: #{measured_object[:object]} #{measured_object[:measure]} weight=#{measured_object[:measure].evaluate_weight(@weights).round(2)}"
|
|
@@ -57,24 +177,53 @@ module Musa
|
|
|
57
177
|
measured_objects.collect { |measured_object| measured_object[:object] }
|
|
58
178
|
end
|
|
59
179
|
|
|
180
|
+
# Compares two measures by their weighted fitness.
|
|
181
|
+
#
|
|
182
|
+
# @param measure_a [Measure] first measure to compare
|
|
183
|
+
# @param measure_b [Measure] second measure to compare
|
|
184
|
+
#
|
|
185
|
+
# @return [Integer] comparison result (-1, 0, 1)
|
|
186
|
+
#
|
|
187
|
+
# @api private
|
|
60
188
|
def evaluate_weights(measure_a, measure_b)
|
|
61
189
|
measure_b.evaluate_weight(@weights) <=> measure_a.evaluate_weight(@weights)
|
|
62
190
|
end
|
|
63
191
|
|
|
192
|
+
# DSL context for Darwin configuration.
|
|
193
|
+
#
|
|
194
|
+
# @api private
|
|
64
195
|
class MainContext
|
|
65
196
|
include Musa::Extension::With
|
|
66
197
|
|
|
198
|
+
# @return [Proc] measures evaluation block
|
|
199
|
+
# @return [Hash] weight assignments
|
|
67
200
|
attr_reader :_measures, :_weights
|
|
68
201
|
|
|
202
|
+
# @return [void]
|
|
203
|
+
# @api private
|
|
69
204
|
def initialize(&block)
|
|
70
205
|
@_weights = {}
|
|
71
206
|
with &block
|
|
72
207
|
end
|
|
73
208
|
|
|
209
|
+
# Defines measures evaluation block.
|
|
210
|
+
#
|
|
211
|
+
# @yield [object] measures DSL block
|
|
212
|
+
#
|
|
213
|
+
# @return [Proc] the measures block
|
|
214
|
+
#
|
|
215
|
+
# @api private
|
|
74
216
|
def measures(&block)
|
|
75
217
|
@_measures = block
|
|
76
218
|
end
|
|
77
219
|
|
|
220
|
+
# Assigns weights to features/dimensions.
|
|
221
|
+
#
|
|
222
|
+
# @param feature_or_dimension_weights [Hash{Symbol => Numeric}] name => weight pairs
|
|
223
|
+
#
|
|
224
|
+
# @return [void]
|
|
225
|
+
#
|
|
226
|
+
# @api private
|
|
78
227
|
def weight(**feature_or_dimension_weights)
|
|
79
228
|
feature_or_dimension_weights.each do |name, value|
|
|
80
229
|
@_weights[name] = value
|
|
@@ -82,39 +231,88 @@ module Musa
|
|
|
82
231
|
end
|
|
83
232
|
end
|
|
84
233
|
|
|
234
|
+
# DSL context for object measurement.
|
|
235
|
+
#
|
|
236
|
+
# @api private
|
|
85
237
|
class MeasuresEvalContext
|
|
86
238
|
include Musa::Extension::With
|
|
87
239
|
|
|
240
|
+
# @return [void]
|
|
241
|
+
# @api private
|
|
88
242
|
def initialize
|
|
89
243
|
@_features = {}
|
|
90
244
|
@_dimensions = {}
|
|
91
245
|
@_died = false
|
|
92
246
|
end
|
|
93
247
|
|
|
248
|
+
# Returns measure result.
|
|
249
|
+
#
|
|
250
|
+
# @return [Measure] measurement
|
|
251
|
+
# @api private
|
|
94
252
|
def _measure
|
|
95
253
|
Measure.new @_features, @_dimensions, @_died
|
|
96
254
|
end
|
|
97
255
|
|
|
256
|
+
# Marks object as having a feature.
|
|
257
|
+
#
|
|
258
|
+
# @param feature_name [Symbol] feature identifier
|
|
259
|
+
#
|
|
260
|
+
# @return [void]
|
|
261
|
+
#
|
|
262
|
+
# @api private
|
|
98
263
|
def feature(feature_name)
|
|
99
264
|
@_features[feature_name] = true
|
|
100
265
|
end
|
|
101
266
|
|
|
267
|
+
# Records dimensional measurement.
|
|
268
|
+
#
|
|
269
|
+
# @param dimension_name [Symbol] dimension identifier
|
|
270
|
+
# @param value [Numeric] measured value
|
|
271
|
+
#
|
|
272
|
+
# @return [void]
|
|
273
|
+
#
|
|
274
|
+
# @api private
|
|
102
275
|
def dimension(dimension_name, value)
|
|
103
276
|
@_dimensions[dimension_name] = value
|
|
104
277
|
end
|
|
105
278
|
|
|
279
|
+
# Marks object as non-viable (to be excluded).
|
|
280
|
+
#
|
|
281
|
+
# @return [void]
|
|
282
|
+
#
|
|
283
|
+
# @api private
|
|
106
284
|
def die
|
|
107
285
|
@_died = true
|
|
108
286
|
end
|
|
109
287
|
|
|
288
|
+
# Checks if object marked as died.
|
|
289
|
+
#
|
|
290
|
+
# @return [Boolean]
|
|
291
|
+
# @api private
|
|
110
292
|
def died?
|
|
111
293
|
@_died
|
|
112
294
|
end
|
|
113
295
|
end
|
|
114
296
|
|
|
297
|
+
# Measurement result for an object.
|
|
298
|
+
#
|
|
299
|
+
# Contains features, dimensions, and viability status.
|
|
300
|
+
#
|
|
301
|
+
# @attr_reader features [Hash{Symbol => Boolean}] feature flags
|
|
302
|
+
# @attr_reader dimensions [Hash{Symbol => Numeric}] raw dimension values
|
|
303
|
+
# @attr_reader normalized_dimensions [Hash{Symbol => Float}] normalized (0-1) dimensions
|
|
304
|
+
#
|
|
305
|
+
# @api private
|
|
115
306
|
class Measure
|
|
116
307
|
attr_reader :features, :dimensions, :normalized_dimensions
|
|
117
308
|
|
|
309
|
+
# @param features [Hash{Symbol => Boolean}] feature flags
|
|
310
|
+
# @param dimensions [Hash{Symbol => Numeric}] dimension values
|
|
311
|
+
# @param died [Boolean] viability status
|
|
312
|
+
#
|
|
313
|
+
# @return [void]
|
|
314
|
+
#
|
|
315
|
+
# @api private
|
|
118
316
|
def initialize(features, dimensions, died)
|
|
119
317
|
@features = features
|
|
120
318
|
@dimensions = dimensions
|
|
@@ -123,10 +321,23 @@ module Musa
|
|
|
123
321
|
@normalized_dimensions = {}
|
|
124
322
|
end
|
|
125
323
|
|
|
324
|
+
# Checks if object is non-viable.
|
|
325
|
+
#
|
|
326
|
+
# @return [Boolean]
|
|
327
|
+
# @api private
|
|
126
328
|
def died?
|
|
127
329
|
@died
|
|
128
330
|
end
|
|
129
331
|
|
|
332
|
+
# Calculates weighted fitness score.
|
|
333
|
+
#
|
|
334
|
+
# Sums weighted normalized dimensions and features.
|
|
335
|
+
#
|
|
336
|
+
# @param weights [Hash{Symbol => Numeric}] weight assignments
|
|
337
|
+
#
|
|
338
|
+
# @return [Float] total fitness score
|
|
339
|
+
#
|
|
340
|
+
# @api private
|
|
130
341
|
def evaluate_weight(weights)
|
|
131
342
|
total = 0.0
|
|
132
343
|
|
|
@@ -140,6 +351,9 @@ module Musa
|
|
|
140
351
|
total
|
|
141
352
|
end
|
|
142
353
|
|
|
354
|
+
# Returns string representation of measure.
|
|
355
|
+
#
|
|
356
|
+
# @return [String] formatted measure description
|
|
143
357
|
def inspect
|
|
144
358
|
"Measure features=#{@features.collect { |k, _v| k }} dimensions=#{@normalized_dimensions.collect { |k, v| [k, [@dimensions[k].round(5), v.round(2)]] }.to_h}"
|
|
145
359
|
end
|