"""One complete-event evaluator. No learned or constructor-family rewards.""" import collections import fractions import generator.solo.scoring import generator.solo.types VERSION = 'melodic-phrases-1' WEIGHTS = {'continuity': 2, 'repetition': 3, 'interruption': 2, 'activity': 2, 'harmony': 2, 'register': 1, 'identity': 3} def signature(notes): notes = generator.solo.types.sounding_notes(notes) if not notes: return () return tuple((n.start-notes[0].start, n.end-n.start, n.pitch.number()-notes[0].pitch.number()) for n in notes) def evaluate(notes, start, end, harmony, *, incoming=(), home=(), purpose='develop', contracts=(), center=60): notes = generator.solo.types.sounding_notes(notes) duration = end-start if duration <= 0: raise ValueError('Evaluation requires a positive span') checked = harmony.evaluate(notes, expressions=contracts, until=end, complete=True) sounding = sum((n.end-n.start for n in notes), fractions.Fraction()) pitches = [n.pitch.number() for n in notes] joined = [n.pitch.number() for n in incoming[-1:]]+pitches intervals = [b-a for a,b in zip(joined, joined[1:])] continuity = sum(max(0, abs(d)-7) for d in intervals)/max(1,len(intervals)) oscillation = sum(a == -b == c and a != 0 for a,b,c in zip(intervals,intervals[1:],intervals[2:])) figures = collections.Counter(tuple(pitches[i:i+4]) for i in range(max(0,len(pitches)-3))) repetition = (oscillation+sum(a==b for a,b in zip(pitches,pitches[1:]))+sum(max(0,n-2) for n in figures.values()))/max(1,len(notes)) interruption = sum(a.endstart for s in harmony.spans) activity = max(0,density-7)+(2 if active and not notes else 0) if notes and sounding0)!=(b[2]>0) for a,b in zip(signature(home),signature(sample)))/len(home) register = sum(float(n.end-n.start)*max(0,abs(n.pitch.number()-center)-10) for n in notes)/max(1,float(sounding)) values = dict(continuity=continuity,repetition=repetition,interruption=interruption, activity=activity,harmony=checked.exposure,register=register,identity=identity) raw = {k:fractions.Fraction(str(v)).limit_denominator(1000000) for k,v in values.items()} contributions = {k:v*WEIGHTS[k] for k,v in raw.items()} return generator.solo.scoring.Evaluation(VERSION,checked.valid,raw,dict(WEIGHTS),contributions, sum(contributions.values(),fractions.Fraction()),{'span':str(duration),'attacks':len(notes),'sounding':str(sounding)})