musa-dsl 0.30.2 → 0.40.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/README.md +227 -6
  6. data/docs/README.md +83 -0
  7. data/docs/api-reference.md +86 -0
  8. data/docs/getting-started/quick-start.md +93 -0
  9. data/docs/getting-started/tutorial.md +58 -0
  10. data/docs/subsystems/core-extensions.md +316 -0
  11. data/docs/subsystems/datasets.md +465 -0
  12. data/docs/subsystems/generative.md +290 -0
  13. data/docs/subsystems/matrix.md +63 -0
  14. data/docs/subsystems/midi.md +123 -0
  15. data/docs/subsystems/music.md +233 -0
  16. data/docs/subsystems/musicxml-builder.md +264 -0
  17. data/docs/subsystems/neumas.md +71 -0
  18. data/docs/subsystems/repl.md +135 -0
  19. data/docs/subsystems/sequencer.md +98 -0
  20. data/docs/subsystems/series.md +302 -0
  21. data/docs/subsystems/transcription.md +152 -0
  22. data/docs/subsystems/transport.md +177 -0
  23. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  24. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  25. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  26. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  27. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  28. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  29. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  30. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  31. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  32. data/lib/musa-dsl/core-ext/with.rb +114 -0
  33. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  34. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  35. data/lib/musa-dsl/datasets/e.rb +186 -2
  36. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  37. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  38. data/lib/musa-dsl/datasets/helper.rb +75 -0
  39. data/lib/musa-dsl/datasets/p.rb +177 -2
  40. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  41. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  42. data/lib/musa-dsl/datasets/ps.rb +134 -4
  43. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  44. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  45. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  48. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  49. data/lib/musa-dsl/datasets/score.rb +279 -0
  50. data/lib/musa-dsl/datasets/v.rb +88 -0
  51. data/lib/musa-dsl/generative/darwin.rb +180 -1
  52. data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
  53. data/lib/musa-dsl/generative/markov.rb +133 -3
  54. data/lib/musa-dsl/generative/rules.rb +258 -4
  55. data/lib/musa-dsl/generative/variatio.rb +217 -2
  56. data/lib/musa-dsl/logger/logger.rb +267 -2
  57. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  58. data/lib/musa-dsl/midi/midi-recorder.rb +108 -1
  59. data/lib/musa-dsl/midi/midi-voices.rb +265 -4
  60. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  61. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  62. data/lib/musa-dsl/music/chords.rb +308 -2
  63. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
  64. data/lib/musa-dsl/music/scales.rb +957 -40
  65. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  66. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  67. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  68. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  69. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  70. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  71. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  72. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  73. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  74. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  75. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  76. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  77. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  78. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  79. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  80. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  81. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  82. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  83. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  84. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  85. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  86. data/lib/musa-dsl/repl/repl.rb +550 -0
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  88. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  89. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  91. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  92. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  93. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  94. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  95. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  96. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  97. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  98. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  99. data/lib/musa-dsl/series/base-series.rb +843 -5
  100. data/lib/musa-dsl/series/buffer-serie.rb +48 -0
  101. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -0
  102. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  103. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  104. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  105. data/lib/musa-dsl/series/quantizer-serie.rb +45 -7
  106. data/lib/musa-dsl/series/queue-serie.rb +65 -0
  107. data/lib/musa-dsl/series/series-composer.rb +701 -0
  108. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  109. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  110. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  111. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  112. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  113. data/lib/musa-dsl/transport/clock.rb +125 -0
  114. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  115. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  116. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  117. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  118. data/lib/musa-dsl/transport/timer.rb +83 -0
  119. data/lib/musa-dsl/transport/transport.rb +318 -0
  120. data/lib/musa-dsl/version.rb +1 -1
  121. data/lib/musa-dsl.rb +132 -25
  122. data/musa-dsl.gemspec +12 -10
  123. metadata +87 -8
@@ -1,8 +1,109 @@
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
6
107
  def initialize(&block)
7
108
  raise ArgumentError, 'block is needed' unless block
8
109
 
@@ -12,6 +113,22 @@ module Musa
12
113
  @weights = main_context._weights
13
114
  end
14
115
 
116
+ # Selects and ranks population by fitness.
117
+ #
118
+ # Evaluates each object with measures, normalizes dimensions across
119
+ # population, applies weights, and returns population sorted by fitness
120
+ # (highest first). Objects marked as died are excluded.
121
+ #
122
+ # @param population [Array] objects to evaluate
123
+ #
124
+ # @return [Array] population sorted by fitness (descending)
125
+ #
126
+ # @example
127
+ # candidates = [obj1, obj2, obj3, ...]
128
+ # ranked = darwin.select(candidates)
129
+ # best = ranked.first # Highest fitness
130
+ # worst = ranked.last # Lowest fitness
131
+ # top10 = ranked.first(10) # Top 10
15
132
  def select(population)
16
133
  measured_objects = []
17
134
 
@@ -46,7 +163,8 @@ module Musa
46
163
 
47
164
  measure.dimensions.each do |dimension_name, value|
48
165
  limit = limits[dimension_name]
49
- measure.normalized_dimensions[dimension_name] = (value - limit[:min]) / limit[:range]
166
+ measure.normalized_dimensions[dimension_name] =
167
+ limit[:range].zero? ? 0.5 : (value - limit[:min]) / limit[:range]
50
168
  end
51
169
 
52
170
  # warn "Darwin.select: #{measured_object[:object]} #{measured_object[:measure]} weight=#{measured_object[:measure].evaluate_weight(@weights).round(2)}"
@@ -61,20 +179,34 @@ module Musa
61
179
  measure_b.evaluate_weight(@weights) <=> measure_a.evaluate_weight(@weights)
62
180
  end
63
181
 
182
+ # DSL context for Darwin configuration.
183
+ #
184
+ # @api private
64
185
  class MainContext
65
186
  include Musa::Extension::With
66
187
 
188
+ # @return [Proc] measures evaluation block
189
+ # @return [Hash] weight assignments
67
190
  attr_reader :_measures, :_weights
68
191
 
192
+ # @api private
69
193
  def initialize(&block)
70
194
  @_weights = {}
71
195
  with &block
72
196
  end
73
197
 
198
+ # Defines measures evaluation block.
199
+ #
200
+ # @yield [object] measures DSL block
201
+ # @api private
74
202
  def measures(&block)
75
203
  @_measures = block
76
204
  end
77
205
 
206
+ # Assigns weights to features/dimensions.
207
+ #
208
+ # @param feature_or_dimension_weights [Hash] name => weight pairs
209
+ # @api private
78
210
  def weight(**feature_or_dimension_weights)
79
211
  feature_or_dimension_weights.each do |name, value|
80
212
  @_weights[name] = value
@@ -82,39 +214,73 @@ module Musa
82
214
  end
83
215
  end
84
216
 
217
+ # DSL context for object measurement.
218
+ #
219
+ # @api private
85
220
  class MeasuresEvalContext
86
221
  include Musa::Extension::With
87
222
 
223
+ # @api private
88
224
  def initialize
89
225
  @_features = {}
90
226
  @_dimensions = {}
91
227
  @_died = false
92
228
  end
93
229
 
230
+ # Returns measure result.
231
+ #
232
+ # @return [Measure] measurement
233
+ # @api private
94
234
  def _measure
95
235
  Measure.new @_features, @_dimensions, @_died
96
236
  end
97
237
 
238
+ # Marks object as having a feature.
239
+ #
240
+ # @param feature_name [Symbol] feature identifier
241
+ # @api private
98
242
  def feature(feature_name)
99
243
  @_features[feature_name] = true
100
244
  end
101
245
 
246
+ # Records dimensional measurement.
247
+ #
248
+ # @param dimension_name [Symbol] dimension identifier
249
+ # @param value [Numeric] measured value
250
+ # @api private
102
251
  def dimension(dimension_name, value)
103
252
  @_dimensions[dimension_name] = value
104
253
  end
105
254
 
255
+ # Marks object as non-viable (to be excluded).
256
+ #
257
+ # @api private
106
258
  def die
107
259
  @_died = true
108
260
  end
109
261
 
262
+ # Checks if object marked as died.
263
+ #
264
+ # @return [Boolean]
265
+ # @api private
110
266
  def died?
111
267
  @_died
112
268
  end
113
269
  end
114
270
 
271
+ # Measurement result for an object.
272
+ #
273
+ # Contains features, dimensions, and viability status.
274
+ #
275
+ # @attr_reader features [Hash{Symbol => Boolean}] feature flags
276
+ # @attr_reader dimensions [Hash{Symbol => Numeric}] raw dimension values
277
+ # @attr_reader normalized_dimensions [Hash{Symbol => Float}] normalized (0-1) dimensions
278
+ #
279
+ # @api private
115
280
  class Measure
116
281
  attr_reader :features, :dimensions, :normalized_dimensions
117
282
 
283
+ # @api private
118
284
  def initialize(features, dimensions, died)
119
285
  @features = features
120
286
  @dimensions = dimensions
@@ -123,10 +289,23 @@ module Musa
123
289
  @normalized_dimensions = {}
124
290
  end
125
291
 
292
+ # Checks if object is non-viable.
293
+ #
294
+ # @return [Boolean]
295
+ # @api private
126
296
  def died?
127
297
  @died
128
298
  end
129
299
 
300
+ # Calculates weighted fitness score.
301
+ #
302
+ # Sums weighted normalized dimensions and features.
303
+ #
304
+ # @param weights [Hash{Symbol => Numeric}] weight assignments
305
+ #
306
+ # @return [Float] total fitness score
307
+ #
308
+ # @api private
130
309
  def evaluate_weight(weights)
131
310
  total = 0.0
132
311