---------- phase 2 -----------------------------------------------------

1. Mark W. McDowell   markm@cs.washington.edu

2. Domain: voiceleading

3. Voiceleading is the process of planning a succession of chords in music
   such that they are aesthetically pleasing when played or sung.  Each
   voice (e.g. soprano, alto, tenor, bass) has a particular note from each
   chord in the progression.  There are rules which govern the position of
   voices in the chord (e.g. they cannot be pitched too high or too low),
   relationship of voices to each other in the chord (e.g. they cannot
   cross each other), relationship of voices to each other in successive
   chords (e.g. they cannot move in parallel if they are certain distances
   apart), and relationships of like voices in successive chords (e.g. the
   tenor note cannot skip to a note more than a given distance away.)
   Given a key, the appropriate valid ranges for the voices, and a chord
   progression, this domain plans out the assignment of notes in the
   chords to the various voices so that the rules of voiceleading are
   maintained.  By specifying in the goal what note is to be found for
   the soprano of each chord, it is possible to harmonize a melody given
   its chord progression.

4. Approximate time to encode domain: 35 hours.

5. This domain is not similar to any existing Prodigy domains.

6. In implementing the voiceleading domain, no ideas have been borrowed
   from the existing Prodigy domains that I have looked at.

---------- phase 3 -----------------------------------------------------

1a. After experimenting with several different ways of expressing this
    domain with Prodigy, the cleanest solution appeared to be to have
    only one non-static, closed world predicate called pitch.  This pre-
    dicate is the relationship between a chord in the progression, a
    voice in that chord, and the note/octave combination for that voice.
    There is a vast amount of other information concerning the relation-
    ships between notes in a chord and notes between chords.  Instead of
    explicitly adding and deleting closed world predicates pertaining to
    these relationships each time a pitch is added or deleted, these re-
    lationships are inferred using inference rules.  This had advantages
    and disadvantages.
	 The advantage was not having to constantly explicitly deal
    with so many predicates.  For example, if a note were added to a
    chord, it meant not having to handle predicates that pertained to
    its relationship to each of the other three voices in the chord (if
    they were present), the four voices in the preceding chord, and the
    four voices in the following chord.  Such relationships involve
    being higher or lower, being within a certain distance, and being
    at a certain interval.  Deleting a note would mean working with all
    these predicates, too.  Many predicates would make operators huge
    and writing them would be an error prone process.
	 Another advantage was that open world predicates were main-
    tained automatically.  When the conditions for a predicate were no
    longer true, the predicate disappeared.
	 One disadvantage in using open world predicates in this way
    was that this meant that if an invalid relationship were introduced
    into the state from adding or deleting a note, it was not found until
    an inference was fired to detect it.  In voiceleading this wasn't
    too much of a problem.  It was fine to have an invalid interim
    state just as long as it was on the path to a valid final state.
    Reducing these invalid interim states was done with search control
    rules, but to eliminate them completely would require many more. 
    The solution is many search control rules and operators with lots
    more preconditions.
	 The crux of the problem in trying to express this domain with
    Prodigy is that it is far easier to tell that you have broken a voice-
    leading rule after you have performed some action than it is to give
    all the prerequisites for a valid action.  That is, it is easier to
    recognize that the application of an operator has introduced an inva-
    lid state than it is to precisely define what preconditions are neces-
    sary for the application of that operator.  This difficulty was the
    largest contributor to using open world predicates in an unnatural
    fashion.
         I was pleased with the solutions that Prodigy produced for the
    problems I gave it.  However, although the solutions are correct,
    there is no guarantee that the solutions are interesting.  It is
    possible to be correct but be boring.  Perhaps the ability to easily
    introduce randomness into Prodigy would alleviate this to a degree.

1b. When the preconditions to an operator are not met so subgoaling is
    needed, I anticipated that it would be difficult to meet those pre-
    conditions in a manner that wouldn't produce a huge search.  For
    example, in the case where two voices move in parallel octaves, it
    could mean considering all the possibilities for two voices each
    having two notes with each note being any valid note in the chord
    and any valid octave.  Although this number itself isn't excessively
    large, the potential for picking a combination that clobbers some
    other goal down the line is very large.  To remedy this situation,
    I wrote search control rules that fix two out of the four notes.
    This will in turn mean subgoaling on only the other two notes and
    since the relationships of the first two notes are maintained, the
    chance of clobbering goals is greatly reduced.  This was done for
    several operators.

1c. The tracing capabilities of Prodigy were especially helpful.  It
    was valuable to be able to go into analyze mode and view various
    data concerning the state, stack, and the nodes.  I used the 
    "optrace" and "opfail" extensively.

1d. If I could make one modification to Prodigy it would be to allow
    greater flexibility over bindings in search control rules.  I'd
    like the ability to explicitly state exactly what should be bound
    to what variable instead of having to use the one and only param
    list given with an operator.  This would be similar to those pro-
    gramming languages that allow explicit bindings of actual arguments
    to formal arguments during procedure calls by naming the formal
    arguments.  Also, the param list should not be used for tracing.
    It should be possible to specify a separate list that indicates
    exactly what variables you want to see during a trace.
