def tonal_space_align(sem1, sem2): """ Like the other alignment functions, but returns the list of aligned pairs. """ from jazzparser.utils.distance import align # Get a list of (coord,fun) pairs for the logical forms seq1 = _lf_to_coord_funs(sem1) seq2 = _lf_to_coord_funs(sem2) # Produce a version of the paths made up of steps and functions, # rather than points and functions steps1 = _steps_list(seq1) steps2 = _steps_list(seq2) pairs = align(steps1, steps2, delins_cost=2, subst_cost=_subst_cost) return pairs
def chord_sequence_alignment(seq1, seq2, transposition=0): """ Performs the same alignment as L{chord_sequence_match_score}, but returns the actual alignment of chords. The alignment assumes that the first sequence is transposed by C{transposition}. """ # Give a half-point to alignments of roots without labels or vice versa def _subst_cost(crd1, crd2): cost = 0 if (crd1.root+transposition) % 12 == crd2.root: cost += -1 if crd1.type == crd2.type: cost += -1 return cost # Try aligning the two sequences alignment = align(seq1, seq2, delins_cost=0, subst_cost=_subst_cost) return alignment
def chord_sequence_alignment(seq1, seq2, transposition=0): """ Performs the same alignment as L{chord_sequence_match_score}, but returns the actual alignment of chords. The alignment assumes that the first sequence is transposed by C{transposition}. """ # Give a half-point to alignments of roots without labels or vice versa def _subst_cost(crd1, crd2): cost = 0 if (crd1.root + transposition) % 12 == crd2.root: cost += -1 if crd1.type == crd2.type: cost += -1 return cost # Try aligning the two sequences alignment = align(seq1, seq2, delins_cost=0, subst_cost=_subst_cost) return alignment
def fscore_match(self, sem1, sem2): """ The core computation of the distance metric. Takes care of the tree comparison and cadence alignment and return the vital statistics. """ from jazzparser.formalisms.music_halfspan.harmstruct import \ semantics_to_dependency_trees from jazzparser.misc.tree.lces import lces_size from jazzparser.utils.distance import align res_score = self.options['res_score'] # Get dependency graphs for the two logical forms if sem1 is None: trees1 = [] else: trees1 = semantics_to_dependency_trees(sem1) if sem2 is None: trees2 = [] else: trees2 = semantics_to_dependency_trees(sem2) if sem1 is None or sem2 is None: # Empty input: give zero score to everything alignment_score = 0.0 alignment = [] transpose = None else: # Try each possible transposition of the second tree to make this # metric key independent distances = [] for x_trans in range(4): for y_trans in range(3): def _align(tree1, tree2): # Transpose the label in the second tree label2 = ((tree2.root.label[0] + x_trans) % 4, (tree2.root.label[1] + y_trans) % 3) # Check the root to find out whether they have the same resolution same_res = tree1.root.label == label2 # Find out what cadence type each is same_cad = _cadence_type(tree1) == _cadence_type(tree2) if same_cad: # Compare the structure of the cadences tree_similarity = lces_size(tree1, tree2) else: tree_similarity = 0 # Work out how much score to give a matching resolution if res_score == -1: res_match = tree_similarity + 1 else: res_match = res_score return - tree_similarity - (res_match if same_res else 0) aligned,dist = align(trees1, trees2, delins_cost=0, subst_cost=_align, dist=True) distances.append((dist,aligned,(x_trans,y_trans))) alignment_score,alignment,transpose = min(distances, key=lambda x:x[0]) alignment_score = -float(alignment_score) def _max_score(trees): """ Get the maximum possible score that could be assigned to a match with this tree set. """ score = 0 for tree in trees: # Do the same things as _align (below), but max possible score # Maximum similarity is just the size of the tree tree_sim = len(tree) if res_score == -1: res_match = tree_sim + 1 else: res_match = res_score # Assume the same resolution and cadence type score += tree_sim + res_match return score max_score1 = _max_score(trees1) max_score2 = _max_score(trees2) return alignment_score, max_score1, max_score2, alignment, transpose