"""R628:有限网格方法与解析不可行证书;fallback必须重新验约束。""" from itertools import product from math import isfinite,isclose ASSETS=['A','B','C'];SECTOR={'A':'Tech','B':'Tech','C':'Health'} RET=[.08,.06,.03];VAR=[.04,.01,.0025] def valid(weights,caps,required=1.,asset_cap=.6,min_return=.055): if set(caps)-set(SECTOR.values()) or any(not isfinite(cap) or cap<0 for cap in caps.values()):return False if not isfinite(required) or required<0 or not isfinite(asset_cap) or asset_cap<0 or not isfinite(min_return):return False if len(weights)!=3 or any(not isfinite(x) or x<0 for x in weights):return False if abs(sum(weights)-required)>1e-10:return False if any(x>asset_cap+1e-10 for x in weights):return False if any(sum(w for s,w in zip(ASSETS,weights) if SECTOR[s]==sector)>cap+1e-10 for sector,cap in caps.items()):return False return sum(w*r for w,r in zip(weights,RET))>=min_return-1e-10 def solve(caps,solver_available=True,data_ok=True): if not data_ok:return {'status':'insufficient_data','weights':None} if set(caps)-set(SECTOR.values()):raise ValueError('unknown constrained sector') if any(not isfinite(v) or v<0 for v in caps.values()):raise ValueError('invalid cap') if set(caps)=={'Tech','Health'} and sum(caps.values())<1-1e-10: return {'status':'proven_infeasible','weights':None,'conflict':['full_investment','Tech_cap','Health_cap']} if not solver_available: fallback=[1/3]*3 return {'status':'solver_unavailable_fallback' if valid(fallback,caps) else 'fallback_rejected', 'weights':fallback if valid(fallback,caps) else None,'optimized':False} candidates=[list(w) for w in product([i/10 for i in range(11)],repeat=3) if valid(w,caps)] if not candidates:return {'status':'no_grid_candidate','weights':None} chosen=min(candidates,key=lambda w:(sum(v*x*x for v,x in zip(VAR,w)),w)) assert valid(chosen,caps) return {'status':'grid_optimum','weights':chosen,'optimized':True} caps={'Tech':.7,'Health':.6};answer=solve(caps) assert answer['weights']==[.2,.5,.3] assert isclose(sum(w*r for w,r in zip(answer['weights'],RET)),.055,rel_tol=0,abs_tol=1e-12) assert isclose(sum(v*w*w for v,w in zip(VAR,answer['weights'])),.004325,rel_tol=0,abs_tol=1e-12) assert solve({'Tech':.4,'Health':.4})['status']=='proven_infeasible' # 该证书是极小冲突:取消任一行业帽或全投资要求,都有构造性可行点。 assert valid([.3,.3,.4],{'Health':.4},min_return=0) assert valid([.2,.2,.6],{'Tech':.4},min_return=0) assert valid([0,0,0],{'Tech':.4,'Health':.4},required=0,min_return=0) assert solve(caps,False)['status']=='solver_unavailable_fallback' assert solve({'Tech':.6,'Health':.6},False)['status']=='fallback_rejected' assert solve(caps,data_ok=False)['status']=='insufficient_data' assert solve({'Tech':.65,'Health':.35})['status']=='no_grid_candidate' assert valid([.4,.25,.35],{'Tech':.65,'Health':.35}) assert not valid([float('nan'),.5,.5],caps) assert not valid([float('inf'),0,0],caps) invalid_constraints=[] for name,changes in [ ('NaN_sector_cap',{'caps':{'Tech':float('nan'),'Health':.6}}), ('NaN_required',{'required':float('nan')}), ('NaN_asset_cap',{'asset_cap':float('nan')}), ('infinite_sector_cap',{'caps':{'Tech':float('inf'),'Health':.6}}), ('infinite_required',{'required':float('inf')}), ('infinite_asset_cap',{'asset_cap':float('inf')}), ('unknown_sector',{'caps':{'Unknown':.7}}), ('negative_sector_cap',{'caps':{'Tech':-.1}}), ('NaN_min_return',{'min_return':float('nan')}), ]: options={'caps':caps};options.update(changes) if valid([.2,.5,.3],**options):invalid_constraints.append(name) assert not invalid_constraints,invalid_constraints print('grid_weights='+str(answer['weights'])+' expected_return=5.500000% variance=0.004325') print('sector_caps_0.4_0.4=proven_infeasible conflict=full_investment,Tech_cap,Health_cap') print('solver_missing=explicit_fallback fallback_constraints_rechecked=True invalid_fallback=rejected data_missing=distinct NaN=rejected') print('no_grid_candidate=not_a_continuous_infeasibility_certificate')