Save
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
"""Isolated production-role benchmark. No target discovery or audition capture."""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import cProfile
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import pstats
|
||||
import time
|
||||
from generator import production, parts, musicxml, vocabulary
|
||||
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--source',type=Path,required=True)
|
||||
p.add_argument('--data-dir',type=Path,default=Path('data'))
|
||||
p.add_argument('--role',choices=production.ROLES,required=True)
|
||||
p.add_argument('--cache',type=Path)
|
||||
p.add_argument('--output',type=Path,required=True)
|
||||
p.add_argument('--profile',action='store_true')
|
||||
args=p.parse_args();args.output.mkdir(parents=True,exist_ok=True)
|
||||
start=time.monotonic()
|
||||
source=production.SourceAnalysis.read(parts.prepare_document(musicxml.load_musicxml(args.source)))
|
||||
analysis_seconds=time.monotonic()-start
|
||||
profile=cProfile.Profile() if args.profile else None
|
||||
if profile:profile.enable()
|
||||
start=time.monotonic()
|
||||
result=production.generate_role(source,args.role,args.data_dir,cache_dir=args.cache)
|
||||
seconds=time.monotonic()-start
|
||||
if profile:
|
||||
profile.disable();profile.dump_stats(args.output/'role.prof')
|
||||
with (args.output/'profile.txt').open('w') as out:pstats.Stats(profile,stream=out).sort_stats('cumulative').print_stats(70)
|
||||
evidence={'source':str(args.source),'canonical_source':source.musical_key,'role':args.role,
|
||||
'platform':platform.platform(),'processor':platform.processor(),'cpu_count':os.cpu_count(),'workers':1,
|
||||
'cache_hit':result['cache_hit'],'analysis_seconds':analysis_seconds,'role_seconds':seconds,
|
||||
'cprofile_enabled':bool(profile),'musical_document_hash':vocabulary.fingerprint(result['document']),
|
||||
'input_trace':result['trace'],'observed_reads':result.get('observed_reads',[])}
|
||||
(args.output/'result.json').write_text(json.dumps(result,sort_keys=True,indent=2))
|
||||
(args.output/'benchmark.json').write_text(json.dumps(evidence,sort_keys=True,indent=2))
|
||||
print(json.dumps({k:v for k,v in evidence.items() if k!='input_trace'},indent=2))
|
||||
|
||||
if __name__=='__main__':main()
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Explicit reproducibility exercise with authored synthetic input; no targets needed."""
|
||||
from __future__ import annotations
|
||||
import argparse,concurrent.futures,contextlib,copy,json,multiprocessing,tempfile
|
||||
from pathlib import Path
|
||||
from generator import production,vocabulary
|
||||
from tests.test_solo import song
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--output',type=Path,required=True);a=p.parse_args();a.output.mkdir(parents=True,exist_ok=True)
|
||||
data=Path('data').resolve();source=production.SourceAnalysis.read(song(count=1,chords=('C7',)))
|
||||
results={};evidence={'scope':'One-bar independently authored source, real production catalogs, all six roles; not a whole-song musical quality test.','canonical_source':source.musical_key,'cases':{}}
|
||||
def collect(label,values):
|
||||
hashes={role:vocabulary.fingerprint(value['document']) for role,value in values.items()}
|
||||
evidence['cases'][label]=hashes
|
||||
evidence.setdefault('reads',{})[label]={r:v.get('observed_reads',[]) for r,v in values.items()}
|
||||
if results:assert hashes==results['base'],label
|
||||
else:results['base']=hashes
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
cache=Path(temp)/'cache'
|
||||
collect('alone_cold',{r:production.generate_role(source,r,data,cache_dir=cache) for r in production.ROLES})
|
||||
collect('reverse_warm',{r:production.generate_role(source,r,data,cache_dir=cache) for r in reversed(production.ROLES)})
|
||||
with concurrent.futures.ProcessPoolExecutor(max_workers=2,mp_context=multiprocessing.get_context('spawn')) as pool:
|
||||
futures={r:pool.submit(production._job,(source,r,data,{},None)) for r in reversed(production.ROLES)}
|
||||
collect('parallel_cold',{r:f.result() for r,f in futures.items()})
|
||||
with contextlib.chdir(temp):
|
||||
refs=Path('build');refs.mkdir()
|
||||
for name in ('all-of-me','autumn-leaves'):
|
||||
for i in (1,2):(refs/f'{name}-solo-{i}.xml').write_text('invalid optional content')
|
||||
collect('targets_present_cold',{r:production.generate_role(source,r,data) for r in production.ROLES})
|
||||
for path in refs.glob('*.xml'):path.unlink()
|
||||
collect('targets_absent_cold',{r:production.generate_role(source,r,data) for r in production.ROLES})
|
||||
production.generate_role(source,'electric_solo',data,{'choice_seed':'different electric only'})
|
||||
collect('after_electric_setting',{r:production.generate_role(source,r,data,cache_dir=cache) for r in production.ROLES})
|
||||
evidence['passed']=True
|
||||
(a.output/'dependency.json').write_text(json.dumps(evidence,indent=2))
|
||||
print(json.dumps({'passed':True,'hashes':results['base']},indent=2))
|
||||
if __name__=='__main__':main()
|
||||
@@ -23,7 +23,8 @@ def region(document, part):
|
||||
def staged(document, details, stage, tempo):
|
||||
notes=[]
|
||||
for phrase in details['phrases']:
|
||||
for n in phrase['lead_stages'].get(stage,{}).get('notes',[]):
|
||||
selected=phrase['lead_stages'].get(stage) or phrase['draft']
|
||||
for n in selected.get('notes',[]):
|
||||
p=n['pitch']
|
||||
notes.append(types.Note(F(n['start']),F(n['end']),types.Pitch(p['letter'],F(p['alter']),p['octave']),
|
||||
tuple(n['sources']),n['role'],continuation=n['continuation'],event_id=n['event_id']))
|
||||
@@ -86,7 +87,8 @@ def export(assets,baseline,output):
|
||||
notes_report.append(f"{phrase['id']}: {phrase['start']}–{phrase['end']}; {phrase['ending']}; completed={phrase['ending_completed']}\n")
|
||||
for n in phrase['draft']['notes']:
|
||||
p=n['pitch'];tone=types.Pitch(p['letter'],F(p['alter']),p['octave'])
|
||||
pos=n['position'];notes_report.append(f" {n['start']}–{n['end']} {tone.label()} {n['role']} string {pos[0]} fret {pos[1]}\n")
|
||||
pos=n['position'];tab=f' string {pos[0]} fret {pos[1]}' if pos else ' (draft pitch; final TAB in XML)'
|
||||
notes_report.append(f" {n['start']}–{n['end']} {tone.label()} {n['role']}{tab}\n")
|
||||
back_audio=mix([backing],tempo,output,tone_cache)
|
||||
for name,document in versions.items():
|
||||
label=key+'_'+name
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Summarize finished production decisions; no composition or reference reading."""
|
||||
from __future__ import annotations
|
||||
import argparse,collections,json
|
||||
from fractions import Fraction as F
|
||||
from pathlib import Path
|
||||
from generator import vocabulary
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--assets',type=Path,required=True);p.add_argument('--output',type=Path,required=True);args=p.parse_args();args.output.mkdir(parents=True,exist_ok=True)
|
||||
evidence={};text=['# Finished production evidence','', 'Times below are exact quarter-note beats relative to the solo. Counts describe the selected music; they are not targets or listening verdicts.','']
|
||||
for path in sorted((args.assets/'analysis').glob('*.roles')):
|
||||
roles=json.loads(path.read_text())
|
||||
for role,v in roles.items():
|
||||
key=path.stem+'/'+role
|
||||
evidence[key]={'document_hash':vocabulary.fingerprint(v['document']),'trace':v['trace']}
|
||||
if not role.endswith('_solo'):continue
|
||||
d=v['details'];ps=d['phrases'];ns=[n for p in ps for n in p['draft']['notes']]
|
||||
operations=collections.Counter(t['operation'] for p in ps for t in p['draft']['transformations'])
|
||||
span=max(F(p['end']) for p in ps)-min(F(p['start']) for p in ps)
|
||||
sounding=sum((F(n['end'])-F(n['start']) for n in ns),F())
|
||||
summary={'attacks':sum(not n['continuation'] for n in ns),'quarter_beats':str(span),'occupancy':float(sounding/span),'fretboard':d['fretboard'],'operations_selected':dict(operations),'operation_funnel':d['selection_trace'].get('operations',{}),'expressions_selected':dict(collections.Counter(e['kind'] for e in d['expressions'])),'expression_funnel':d['selection_trace'].get('expression_operations',{}),'evaluation_total':d['selection_trace']['selection_total'],'unresolved':d['unresolved_treatments'],'boundaries':[]}
|
||||
evidence[key].update(summary)
|
||||
text += ['## '+key,'',f"{summary['attacks']} attacks over {span} quarter beats; sounding occupancy {summary['occupancy']:.1%}; sounding MIDI range {d['fretboard']['range_midi']}. Harmonic valid: {d['harmonic_valid']}; unresolved obligations: {len(d['unresolved_treatments'])}.",'', '| Span | Behavior | Destination / actual hold | Home identity |','|---|---|---|---|']
|
||||
for phrase in ps:
|
||||
anchors=[{k:a[k] for k in ('role','effect','pitch','actual_start','actual_end','completed')} for a in phrase['anchors']]
|
||||
summary['boundaries'].append({'start':phrase['start'],'end':phrase['end'],'closure':phrase['closure'],'anchors':anchors,'home':phrase['home']})
|
||||
destination='; '.join(a['effect']+' '+a['pitch']+' '+str(a['actual_start'])+'–'+str(a['actual_end']) for a in anchors) or 'no forced arrival'
|
||||
text.append(f"| {phrase['start']}–{phrase['end']} | {phrase['closure']} / {phrase['ending']} | {destination} | `{phrase['home']}` |")
|
||||
text += ['', 'Selected actions: '+', '.join(f'{k}: {v}' for k,v in sorted(operations.items()))+'.','', 'Typed treatments: '+str(summary['expressions_selected'])+'.','', '### Representative evaluated phrase','']
|
||||
phrase=next(p for p in ps if p['draft']['notes']);evaluation=phrase['evaluation'];assert abs(phrase['score']-sum(evaluation['contributions'].values()))<1e-10
|
||||
text += [f"Selected `{phrase['id']}`; score {phrase['score']:.9f}. Identity is a hard feature contract.",'','| Contribution | Raw | Weight | Weighted |','|---|---:|---:|---:|']
|
||||
for k,value in evaluation['raw'].items():text.append(f"| {k} | {value:.9f} | {evaluation['weights'][k]} | {evaluation['contributions'][k]:.9f} |")
|
||||
pool=d['selection_trace'].get('source_candidate_pools',{}).get(phrase['passage'],[])
|
||||
alternatives=sorted((c for c in pool if c['id']!=phrase['draft']['id']),key=lambda c:(c['score'],c['id']))
|
||||
text += ['', 'Other retained alternatives for this source window (possibly different incoming histories):']
|
||||
for c in alternatives[:3]:text.append(f"- `{c['id']}`: {c['score']:.9f}, {c['attacks']} attacks; selected ending {c['ending']}.")
|
||||
text += ['', 'These are local alternatives, not a proof of a global optimum. The chorus frontier compares complete histories using duration-weighted phrase scores. The JSON retains every reported contribution, operation cutoff counts and source input trace.','']
|
||||
evidence[key]['winner_evaluation']=evaluation;evidence[key]['local_alternatives']=alternatives[:3]
|
||||
(args.output/'evidence.json').write_text(json.dumps(evidence,indent=2))
|
||||
(args.output/'phrase_traces.md').write_text('\n'.join(text)+'\n')
|
||||
if __name__=='__main__':main()
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Optional evaluation of independent source excerpts; never a production import."""
|
||||
from __future__ import annotations
|
||||
import argparse,json,time
|
||||
from pathlib import Path
|
||||
from generator import musicxml,parts,production,vocabulary,score,musicxml_writer
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--sources',type=Path,default=Path('tests/evaluation_sources'))
|
||||
p.add_argument('--data-dir',type=Path,default=Path('data'))
|
||||
p.add_argument('--output',type=Path,required=True)
|
||||
args=p.parse_args();args.output.mkdir(parents=True,exist_ok=True)
|
||||
report={'scope':'Four-bar source excerpts, not complete standards; read adjacent provenance. No target solo used.', 'cases':[]}
|
||||
for path in sorted(args.sources.glob('*.xml')):
|
||||
source=production.SourceAnalysis.read(parts.prepare_document(musicxml.load_musicxml(path)))
|
||||
roles={};case={'source':str(path),'canonical_source':source.musical_key,'roles':{}}
|
||||
for role in production.ROLES:
|
||||
start=time.monotonic()
|
||||
try:
|
||||
value=production.generate_role(source,role,args.data_dir)
|
||||
roles[role]=value;case['roles'][role]={'valid':True,'seconds':time.monotonic()-start,'hash':vocabulary.fingerprint(value['document'])}
|
||||
except ValueError as error:
|
||||
case['roles'][role]={'valid':False,'seconds':time.monotonic()-start,'diagnostic':str(error)}
|
||||
print(path.stem,role,case['roles'][role],flush=True)
|
||||
if len(roles)==len(production.ROLES):
|
||||
for part,document in production.assemble(source,roles).items():
|
||||
score.write_json(args.output/(path.stem+'_'+part+'.json'),document)
|
||||
musicxml_writer.write_musicxml(args.output/(path.stem+'_'+part+'.xml'),document)
|
||||
report['cases'].append(case)
|
||||
(args.output/'report.json').write_text(json.dumps(report,indent=2))
|
||||
return int(any(not r['valid'] for c in report['cases'] for r in c['roles'].values()))
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
Reference in New Issue
Block a user