"""Development-only shared integer ranking from COMPLETE ordinary rollouts. The returned coefficients depend on the training examples. Target event arrays, source identifiers and diagnostic oracle paths are never production artifacts. """ from collections import Counter from fractions import Fraction as F import itertools import math from generator import vocabulary from generator.solo import selection def contextual_candidates(selector,notes): """Induce only whitelisted two/three-condition event contexts. These counts are training-dependent. No title, time index, song key, event array, fingerprint or arbitrary predicate can appear in the rule schema. """ values=Counter() for context,ticks in selector.contexts(notes): terms=sorted((k,v) for k,v in context.items() if v in selection.CONDITIONS[k]) for size in (2,3): for terms_ in itertools.combinations(terms,size): values['joint/'+'&'.join(k+'='+v for k,v in terms_)]+=ticks return values def vector(builder,actions,home=()): e=builder.selector.complete(actions,home,start=builder.start,end=builder.end) # Equal spans: clear normalization without rounding any musical time. span=builder.end-builder.start features={k:F(v)*span for k,v in e.raw.items() if not k.startswith('prior/') and v} prior=sum(F(v)*span for k,v in e.contributions.items() if k.startswith('prior/')) return features,prior def constraint(role,gold,other,case): left,lp=gold;right,rp=other delta={k:left.get(k,0)-right.get(k,0) for k in left.keys()|right.keys()} delta={k:v for k,v in delta.items() if v} selection.validate_weights(dict.fromkeys(delta,0)) return {'role':role,'delta':delta,'prior':lp-rp,'case':case} def solve(rows,bound=1024): """One shared coefficient table per role; deterministic pinned integer solver.""" import z3 if z3.get_version_string()!='4.13.3':raise ValueError('Use pinned z3-solver==4.13.3.0') z3.set_param('smt.random_seed',0) # Exact zero/one-coefficient cases need no combinatorial cardinality search. # Checking every feature's integer interval proves the minimum support and # L1 objective when all independent roles have such a solution. This does # not claim the resulting sparse model reproduces any complete melody. simple={} for role in sorted({r['role'] for r in rows}): subset=[r for r in rows if r['role']==role] if all(r['prior']<=-1 for r in subset):simple[role]={};continue choices=[] for key in sorted({k for r in subset for k in r['delta']}): low,high=-bound,bound for r in subset: slope=F(r['delta'].get(key,0));limit=-1-F(r['prior']) if slope>0:high=min(high,math.floor(limit/slope)) elif slope<0:low=max(low,math.ceil(limit/slope)) elif limit<0:low,high=1,0;break if low<=high: value=low if low>0 else high if high<0 else 0 choices.append((abs(value),key,value)) if not choices:break _,key,value=min(choices);simple[role]={key:value} if set(simple)=={r['role'] for r in rows}: for weights in simple.values():selection.validate_weights(weights) return {'status':'solved','weights':simple,'bound':bound, 'nonzero':sum(len(w) for w in simple.values()),'l1':sum(abs(v) for w in simple.values() for v in w.values()), 'method':'exhaustive zero/one-coefficient integer intervals; minimum support and L1 within that support proven'} names=sorted({(r['role'],k) for r in rows for k in r['delta']}) variables={n:z3.Int('w_'+str(i)) for i,n in enumerate(names)} limits=[v>=-bound for v in variables.values()]+[v<=bound for v in variables.values()] rational=lambda v:z3.RealVal(str(F(v))) constraints=[z3.Sum([variables[(r['role'],k)]*rational(v) for k,v in r['delta'].items()])+rational(r['prior'])<=-1 for r in rows] check=z3.Solver();check.set(rlimit=2000000);check.add(*limits) for i,c in enumerate(constraints):check.assert_and_track(c,'comparison_'+str(i)) status=check.check() if status==z3.unknown:return {'status':'solver_unknown','stage':'feasibility','reason':check.reason_unknown()} if status==z3.unsat: return {'status':'incompatible_with_current_features_and_bounds','bound':bound, 'conflicts':[str(x) for x in check.unsat_core()], 'identical_feature_pairs':[i for i,r in enumerate(rows) if not r['delta'] and r['prior']>=0]} opt=z3.Optimize();opt.add(*limits,*constraints) # Deterministic solver work budget. Exhaustion yields no proposed model; # elapsed wall time never chooses a musical winner. opt.set(rlimit=2000000) opt.minimize(z3.Sum([z3.If(v==0,0,1) for v in variables.values()])) opt.minimize(z3.Sum([z3.If(v<0,-v,v) for v in variables.values()])) if opt.check()!=z3.sat:return {'status':'solver_unknown','reason':opt.reason_unknown()} model=opt.model();weights={r:{} for r,_ in names} for (role,key),v in variables.items(): value=model.eval(v,model_completion=True).as_long() if value:weights[role][key]=value for w in weights.values():selection.validate_weights(w) return {'status':'solved','weights':weights,'bound':bound, 'nonzero':sum(len(w) for w in weights.values()),'l1':sum(abs(v) for w in weights.values() for v in w.values())} def sounded(actions): from generator.solo.types import sounding_notes return tuple((n.start,n.end,n.pitch.number()) for n in sounding_notes(tuple(n for a in actions for n in a.notes))) class CoverageTrace: """Read-only observer. Targets diagnose cuts, never supply search decisions.""" def __init__(self,gold): self.gold=sounded(gold);self.records=[] def matches(self,notes,end): # These are committed whole actions, not a truncated playback preview. # A quarter-note attack cannot reproduce a half-note target merely because # its first quarter sounds the same. Future actions do not reopen it. from generator.solo.types import sounding_notes actual=tuple((n.start,n.end,n.pitch.number()) for n in sounding_notes(notes)) expected=tuple((a,b,p) for a,b,p in self.gold if a