/* Listing 2: John Rahn's "Logic, Set Theory, Music Theory" * College Music Symposium 19/1 (1979): 114 - 127. * Prolog versions of Definitions I through VIII * realized by John Roeder (c) 1991, 2018 */ /* Prolog program to accompany "Logic-Programming Models of Music: A Semiotic Evaluation" */ /* John Roeder, Perspectives of New Music 57/1-2 (2019): 375-402. */ /* This program may be run in the free online SWISH Prolog interpreter. * (see swish.swi-prolong.org). * In the left pane of the SWISH window, click the + button, then the "create program" button. Then paste * the contents of this file into the box labeled "Your Prolog rules and facts go here…" * In the lower right pane, you may then enter queries, such as those in the accompanying query file. */ /* Data structures: each piece is a set of asserted events of the form: * note(Pitch, Initiation, Release). * or * note(s, Initiation, Release). * The events of Rahn Ex. 5a are declared at the end of this file. */ /* "Primitive" predicates (p. 115) ******************************/ is_time(T) :- integer(T), T >= 0. is_pitch(P) :- integer(P). /* Definition I (p. 118) ***************************************/ note(X) :- note(Pitch, [Initiation, Release]), X =.. [note, Pitch, [Initiation, Release]], is_pitch(Pitch), is_time(Initiation), is_time(Release). /* Definition II (p. 118): the constant s denotes silence ******/ rest(X) :- note(s, [Initiation, Release]), X =.. [note, s, [Initiation, Release]], is_time(Initiation), is_time(Release). /* Definition III (p. 118) *************************************/ time_adjacent(X, Y) :- (note(X); rest(X)), (note(Y); rest(Y)), release(X, X_release), initiation(X, X_initiation), release(Y, Y_release), initiation(Y, Y_initiation), (X_release = Y_initiation ; Y_release = X_initiation). release(X, R) :- X =.. [note, _, [_,R]]; X =.. [rest, _, [_,R]]. initiation(X, I) :- X =.. [note, _, [I,_]]; X =.. [rest, _, [I,_]]. /* ancillary predicates and facts for Defs. IV */ cyclic_ordering(c_major_scale, [0,2,4,5,7,9,11,0]). pitch_class(Pitch, Pc) :- Pc is Pitch mod 12. less_than_an_octave_apart(P1, P2) :- P2 - P1 < 12, P2 - P1 > -12. adjacent_in(X,Y,[X,Y|_]). adjacent_in(X,Y,[Y,X|_]). adjacent_in(X,Y,[_|T]) :- adjacent_in(X,Y,T). pitch(X, P) :- X =.. [note, P, [_,_]]. /* Definition IVC (p. 119). Defs. IVA and IVB are special cases of it ********/ /* Type denotes a pc cycle of the form [a,b,c,…,z,a], such as: */ /* chromatic_scale, c_major_scale, g_harmonic_minor, circle_of_fifths */ pitch_adjacent_with_respect_to(X,Y,Type) :- note(X), note(Y), pitch(X,Px), pitch(Y,Py), less_than_an_octave_apart(Px,Py), pitch_class(Px,Pcx), pitch_class(Py, Pcy), cyclic_ordering(Type, C), adjacent_in(Pcx,Pcy,C). /* Definition VC (p. 120) Defs. VA and VB are similarly defined ***************/ neighbors_with_respect_to(X,Y,Type) :- time_adjacent(X,Y), /* Def III */ pitch_adjacent_with_respect_to(X,Y,Type). /* Def IV */ earliest_initiation(X, Y, XI):- initiation(X,XI), initiation(Y,YI), XI =< YI, !. earliest_initiation(_, Y, YI):- initiation(Y,YI). latest_release(X, Y, XR):- release(X,XR), release(Y,YR), XR >= YR, !. latest_release(_, Y, YR):- release(Y,YR). /* Definition VIC (p. 121) Defs. VIA and VIB are also easily defined *************/ nc_prolong(X, Y, Z, Type) :- neighbors_with_respect_to(X,Y,Type), /* Def V */ pitch(X, X_pitch), pitch(Y, Y_pitch), earliest_initiation(X, Y, Earliest_init), latest_release(X, Y, Latest_rel), ( Z =.. [note, X_pitch, [Earliest_init, Latest_rel]]; Z =.. [note, Y_pitch, [Earliest_init, Latest_rel]] ). /* Definition VII (p. 121) ********************************************************/ /* Rahn's definition needs to be modified slightly. */ /* His informal clause "a pitch is in A IFF it is in B" */ /* is problematic, since A is a set of notes or rests, and */ /* B is a set of notes, and a "pitch" is not a note or rest */ /* Note that: * Under this def. a single note may prolong a single note. * Also, this def. does not impose any structure upon the prolonged B * for example, that its pitches belong to a triad (as in Ex. 5). */ /* This definition is given in two versions, each intended for different use. * Version 1, arp_prolongs, will generate all backgrounds of all subsets * of declared facts; see sample query */ arp_prolongs(A, B) :- set_of_notes_or_rests(A), earliest_init_in_list(Init, A), latest_release_in_list(Rel, A), reset_times(A, R, Init, Rel), eliminate_duplicates(R, B). /* Add cut here to make only one B derivable from A */ /* ancillary predicates for def VII */ /* generates an ordered subset of all declared notes and rests) */ set_of_notes_or_rests(NRSet):- setof(X, note_or_rest(X), All), subset(All, NRSet). note_or_rest(X):- (note(X); rest(X)). /* default fetches declared events */ /* comment out the following rule if querying arp_prolong on all facts */ note_or_rest(X):- X =.. [note,_,_]. /* otherwise will simply verify noteness */ /* Version 2, arp_prolong2, is intended for use when foreground A is given * (rather than being inferred automatically from declared facts) * It is needed for definition VIII. */ arp_prolongs2(A, B) :- set_n_r_check(A), earliest_init_in_list(Init, A), latest_release_in_list(Rel, A), reset_times(A, R, Init, Rel), eliminate_duplicates(R, B). /* May add cut here to make only one B derivable from A */ /* ancillary predicates for def VII */ set_n_r_check([X]):- note_or_rest(X). set_n_r_check([H|T]):- note_or_rest(H), set_n_r_check(T). earliest_init_in_list(I, [X]) :- initiation(X, I). earliest_init_in_list(I, [X,Y|T]) :- initiation(X, Ix), initiation(Y, Iy), Ix >Iy, !, earliest_init_in_list(I, [Y|T]). earliest_init_in_list(I, [X,_|T]) :- earliest_init_in_list(I, [X|T]). latest_release_in_list(R, [X]) :- release(X, R). latest_release_in_list(R, [X,Y|T]) :- release(X, Rx), release(Y, Ry), Rx < Ry, !, latest_release_in_list(R, [Y|T]). latest_release_in_list(R, [X,_|T]) :- latest_release_in_list(R, [X|T]). eliminate_duplicates([X],[X]). eliminate_duplicates([X|T],N):- member(X,T), !, eliminate_duplicates(T, N). eliminate_duplicates([H|T],[H|N]):- eliminate_duplicates(T,N). reset_times([],[],_,_). reset_times([H|T], Reset, Init, Rel):- pitch(H,s), !, /* no rests in background*/ reset_times(T, Reset, Init, Rel). reset_times([H|T], [RH|RT], Init, Rel):- pitch(H,P), RH =.. [note, P, [Init, Rel]], reset_times(T, RT, Init, Rel). member(X,[X|_]). member(X,[_|T]):- member(X,T). subset([],[]). /* second argument is subset of first */ subset([_|T],X):- subset(T,X). subset([H|T],[H|X]):- subset(T,X). /* arguments must be instantiated */ same_set([],_). same_set([H|T], S):- member(H, S), same_set(T,S). /* Definition VIII (p. 122) ***************************************************************/ /* Rahn's version has to be modified slightly, because: */ /* (A) The meanings of A and B are inconsistent with those in the previous definitions */ /* (B) the clause "b NC-prolongs a" where b and a are members (subsets) of partitions B' */ /* and A' of B and A, respectively, is inconsistent with definition VIC of NC-prolong, */ /* which relates THREE NOTES. We patch this by adding the list-oriented predicate: */ /* members_nc_prolong([List_of_two_foreground_events], [List_of_the_event_they_n_prolong], Type) */ next_background(Background, Foreground) :- partition(Foreground_partition, Foreground), one_to_one_correspondence(Background_partition, Foreground_partition), not(same_set(Background_partition, Foreground_partition)), /* to preclude permutations */ partition(Background_partition, Background). /* next_background uses the ancillary predicate: */ one_to_one_correspondence([],[]). one_to_one_correspondence([First_back|Rest_back],[First_fore|Rest_fore]) :- (members_nc_prolong(First_fore, First_back,_); /* neighbors in some pc cycle, tried first */ arp_prolongs2(First_fore, First_back); /* or arpeggiation (NB simple test version) */ same_set(First_fore, First_back)),!, /* or same set of events */ one_to_one_correspondence(Rest_back,Rest_fore). members_nc_prolong([X,Y],[Z],Type):- nc_prolong(X,Y,Z, Type). /* ancillary predicates */ /* relation of partition to flat set */ partition([],[]). partition([H|T],S) :- set_difference(S,H,D), H \== [], partition(T,D). /* partition itself requires the following ancillary predicates: */ /* set_difference(Superset, Subset, Difference). */ /* Any two of the variables must be uninstantiated */ /* If Superset alone is instantiated then the predicate produces all two-part partitions of Superset */ set_difference(Superset,[],Superset). set_difference(Superset,[First|Rest_of_subset],Difference) :- subtract(First,Superset,Res), set_difference(Res,Rest_of_subset,Difference). subtract(Element,[Element|Rest],Rest). subtract(Element,[First|Rest_of_set],Remainder) :- subtract(Element,Rest_of_set,Res), conc([First],Res,Remainder), !. /* list concatentation, taken from Bratko, Prolog Programming for A.I., p. 68 */ conc([],L,L). conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3). /* Prolog version of Rahn's representation of Example 5 * The first argument is the event's pitch; * the second argument is a list of its * onset time and release time. * C4 = 36; times are integer multiples of the 16th note */ note(36,[0,3]). /* downward-stemmed notes */ note(35,[3,4]). note(36,[4,8]). note(s,[0,1]). /* upward-stemmed notes */ note(48,[1,2]). note(43,[1,2]). note(40,[2,3]). note(s,[3,4]). note(43,[4,5]). note(48,[5,6]). note(52,[6,7]). note(s,[7,8]).