This functor introduces means which allow to constrain the shape of a timing tree. The duration of temporal items (e.g. notes or sequentials) may be 0 (zero). A temporal item with duration 0 is considered 'not existing' (e.g. its Lilypond output is skipped). Obviously, such an approach only allows to reduce the size of a tree (by 'removing' branches). Nevertheless, not only notes but also temporal aspects can be 'removed' -- in an extreme case the whole score may be effectively 'empty'.
This functor mainly defines AvoidSymmetries, a rule on the score which avoids symmetries in CSPs (i.e. irrelevant additional solutions).
NB: Every constraint on any temporal item -- which potentially shall be of duration=0 -- must be formulated not to conflict with AvoidSymmetries. For instance, AvoidSymmetries constraints the pitch of a note with duration=0 to its minimal domain value (by reflection!). A rule constraining all notes to have distict pitches will conflict in case multiple notes have duration=0 and thus potentially equal pitch. A rule on a single item is reformulated, e.g., by a reified rule as {FD.impl ({MyItem getDuration($)} \=: 0) {MyRuleR MyItem} 1}
. Rules on multiple items (e.g. pattern rules or FD.distinct) require more drastic reformulation (e.g. sum the number of items with duration=0 and apply Pattern.howManyDistinct accordingly).
NB: the memory needed for a score with constrained timing tree is always the memory needed the full score. Consequently, allowing for great flexibility in the effective size of the timing tree results in increases memory usage with more copying time etc (until Mozart supports recomputation).
BTW: constraining the size/shape of the timing tree increases the size of the search tree no more then increasing the domain of any variable in the score (to which essentially it comes down to). Still, the size of the search tree may increase significantly.
Functor
ConstrainTimingTree ("ConstrainTimingTree.oz")
Import
- FD
- Combinator
- GUtils at "x-ozlib://anders/strasheela/source/GeneralUtils.ozf"
- Pattern at "x-ozlib://anders/strasheela/Pattern/Pattern.ozf"
Export
Define
proc{AvoidSymmetries MyScore}
AvoidSymmetries applies constrains on all temporal items in MyScore to avoid symmeries in case the duration of some temporal items is 0.
Two rules are applied to the score: (i) for all temporal items, all parameter values are determined to their minimal domain value (execept the values of the parameters start time, duration and end time). (ii) for all temporal items in temporal aspects, 'non-existing' items are only 'collected' at the end of a temporal aspect.
NB: Constraints are only aplied to the tree of temporal items whose root is MyScore. That is, AvoidSymmetries can be applied to a sub-score only.
NB: this scheme only determines variables in temporal items which are parameter values of the item. As Strasheela is designed for distribution strategies over parameters, this should be sufficient (i.e. further variables which are no parameter values would not have been distributed neither).
NB: Think about: AvoidSymmetries can cause problems, because it determines variables to a reflected min domain value which can be inconsistent with some other constraints on these variables. For example, perhaps the variable would be bound anyway by propagation before the next distribution, but AvoidSymmetries interferes and causes a fail.
Idea: would AvoidSymmetries work more securely if optionally added to the distribution strategy?
proc{IsExisting TemporalItem B}
B=1 <-> TemporalItem is existing (i.e. its duration \= 0).
!! TODO: this constraint is possibly applied very often: consider memoizing.
proc{RelevantLength TemporalAspect N}
Returns the number of temporal items in TemporalAspect which are relevant (i.e. whose duration is NOT 0).
NB (efficiency notice): This constraint implicitly constrains that all non-existing' items in TemporalAspect are only 'collected' at the end of the aspect. That is, this constrain makes AvoidSymmetries _partly_ redundant.
proc{IsLastExistingItem TemporalItem B}
B=1 <-> TemporalItem is an existing item which is either the last in its temporal container or is followed by a non-existing item.
proc{AccessLastItem TemporalAspect Fn Result}
Accesses a value from the last "existing" item in TemporalAspect. Fn is a unary function or method: the result of Fn -- applied to the last existing item -- is returned in X.
NB: AccessLastItem does not block, but Result remains undetermined until the last existing item in TemporalAspect is known (i.e. the durations of the items are sufficiently known).
proc{GetExistingItems TemporalAspect Items}
Returns the list of existing items in TemporalAspect.
NB: blocks until for all items in TemporalAspect it is known whether they exist (i.e. the durations of the items are sufficiently known).
End