176 lines
5.6 KiB
Python
176 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Protein Folding Fractal Echo - compares lattice coherence to Ramachandran landscape"""
|
|
import sys,csv,math
|
|
from collections import defaultdict
|
|
|
|
def load(fn):
|
|
data=[]
|
|
with open(fn,'r') as f:
|
|
for row in csv.DictReader(f):
|
|
d={}
|
|
for k,v in row.items():
|
|
k=k.strip().lower()
|
|
try: d[k]=float(v)
|
|
except: d[k]=v
|
|
if d.get('omega',0)>0: data.append(d)
|
|
return data
|
|
|
|
def main():
|
|
fn=sys.argv[1] if len(sys.argv)>1 else None
|
|
if not fn:
|
|
print("Usage: python protein_fold_echo.py sweep.csv")
|
|
return
|
|
data=load(fn)
|
|
n=len(data)
|
|
cohs=[d['coherence'] for d in data]
|
|
mn,mx=min(cohs),max(cohs)
|
|
mean=sum(cohs)/n
|
|
std=(sum((c-mean)**2 for c in cohs)/n)**0.5
|
|
med=sorted(cohs)[n//2]
|
|
skew=sum((c-mean)**3 for c in cohs)/(n*std**3) if std>0 else 0
|
|
|
|
print("="*70)
|
|
print(" PROTEIN FOLDING FRACTAL ECHO ANALYZER")
|
|
print("="*70)
|
|
print(f" Source: {fn}")
|
|
print(f" Points: {n}")
|
|
print(f" Coherence: {mn:.6f} to {mx:.6f}")
|
|
print(f" Mean={mean:.6f} Std={std:.6f}")
|
|
|
|
by_om=defaultdict(list)
|
|
for d in data:
|
|
by_om[round(d['omega'],2)].append(d)
|
|
|
|
results={}
|
|
|
|
# TEST 1: Basin Counting (Ramachandran has 4-5 basins)
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 1: BASIN COUNTING (Ramachandran has 4-5 basins)")
|
|
print("="*70)
|
|
basin_matches=0
|
|
for om in sorted(by_om.keys()):
|
|
pts=by_om[om]
|
|
cs=sorted(set(round(p['coherence'],4) for p in pts))
|
|
if len(cs)<2:
|
|
basins=1
|
|
else:
|
|
gaps=[cs[i+1]-cs[i] for i in range(len(cs)-1)]
|
|
mg=sum(gaps)/len(gaps) if gaps else 0
|
|
basins=sum(1 for g in gaps if g>mg*2)+1
|
|
match="<<<" if 3<=basins<=6 else ""
|
|
if 3<=basins<=6:
|
|
basin_matches+=1
|
|
print(f" om={om:.1f}: {len(cs):>3} distinct, {basins:>2} basins {match}")
|
|
print(f"\n Slices with 3-6 basins: {basin_matches}/{len(by_om)}")
|
|
results['basin']=basin_matches>=3
|
|
|
|
# TEST 2: Forbidden Fraction (Ramachandran ~35% allowed)
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 2: FORBIDDEN FRACTION (Ramachandran ~35% allowed)")
|
|
print("="*70)
|
|
top35=sorted(cohs)[int(n*0.65)]
|
|
allowed=sum(1 for c in cohs if c>=top35)/n
|
|
diff=abs(allowed-0.35)
|
|
print(f" Top 35% threshold: {top35:.6f}")
|
|
print(f" Allowed fraction: {allowed:.1%}")
|
|
print(f" Ramachandran target: 35%")
|
|
print(f" Difference: {diff:.1%}")
|
|
print(f" {'PASS' if diff<0.15 else 'FAIL'}")
|
|
results['forbidden']=diff<0.15
|
|
|
|
# TEST 3: Funnel Topology (proteins have positive skewness)
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 3: FUNNEL TOPOLOGY (proteins have positive skewness)")
|
|
print("="*70)
|
|
cr=mx-mn
|
|
if cr>0:
|
|
nb=10
|
|
bw=cr/nb
|
|
bins=[0]*nb
|
|
for c in cohs:
|
|
b=min(int((c-mn)/bw),nb-1)
|
|
bins[b]+=1
|
|
for i in range(nb):
|
|
lo=mn+i*bw
|
|
hi=lo+bw
|
|
bar='#'*(bins[i]*40//max(max(bins),1))
|
|
print(f" {lo:.4f}-{hi:.4f}: {bins[i]:>5} {bar}")
|
|
print(f"\n Skewness: {skew:+.4f}")
|
|
if skew>0.3:
|
|
print(" >>> FUNNEL DETECTED")
|
|
elif skew<-0.3:
|
|
print(" >>> INVERTED FUNNEL")
|
|
else:
|
|
print(" >>> FLAT LANDSCAPE")
|
|
results['funnel']=abs(skew)>0.3
|
|
|
|
# TEST 4: Amino Acid Classes (5 Ramachandran classes)
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 4: AMINO ACID CLASS MAPPING (5 classes expected)")
|
|
print("="*70)
|
|
classes=set()
|
|
for om in sorted(by_om.keys()):
|
|
pts=by_om[om]
|
|
cr2=max(p['coherence'] for p in pts)-min(p['coherence'] for p in pts)
|
|
if cr2<0.0005:
|
|
cls='proline'
|
|
elif cr2<0.002:
|
|
cls='pre_proline'
|
|
elif cr2<0.005:
|
|
cls='beta_branched'
|
|
elif cr2<0.02:
|
|
cls='general'
|
|
else:
|
|
cls='glycine'
|
|
classes.add(cls)
|
|
print(f" om={om:.1f}: range={cr2:.6f} -> {cls}")
|
|
print(f"\n Classes found: {len(classes)}/5 = {sorted(classes)}")
|
|
results['classes']=len(classes)>=3
|
|
|
|
# TEST 5: Levinthal Compression
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 5: LEVINTHAL COMPRESSION")
|
|
print("="*70)
|
|
distinct=len(set(round(c,4) for c in cohs))
|
|
comp=n/max(1,distinct)
|
|
print(f" Combinations: {n}")
|
|
print(f" Distinct modes: {distinct}")
|
|
print(f" Compression: {comp:.1f}:1")
|
|
results['levinthal']=comp>2
|
|
|
|
# TEST 6: Hierarchy
|
|
print(f"\n{'='*70}")
|
|
print(" TEST 6: HIERARCHICAL STRUCTURE")
|
|
print("="*70)
|
|
n_class=len(by_om)
|
|
n_topo=distinct
|
|
print(f" CATH: 4 classes -> 41 arch -> 1393 topo")
|
|
print(f" Lattice: {n_class} classes -> {n_topo} topo")
|
|
results['hierarchy']=n_topo>10
|
|
|
|
# VERDICT
|
|
print(f"\n{'='*70}")
|
|
print(" VERDICT")
|
|
print("="*70)
|
|
tests=[
|
|
('Basin count (3-6)',results.get('basin',False)),
|
|
('Forbidden fraction (25-45%)',results.get('forbidden',False)),
|
|
('Funnel topology',results.get('funnel',False)),
|
|
('Amino acid classes (3+/5)',results.get('classes',False)),
|
|
('Levinthal compression (>2:1)',results.get('levinthal',False)),
|
|
('Hierarchical structure',results.get('hierarchy',False))
|
|
]
|
|
passed=sum(1 for _,v in tests if v)
|
|
for name,v in tests:
|
|
print(f" {name:<35} {'PASS' if v else 'FAIL':>6}")
|
|
print(f"\n PASSED: {passed}/6")
|
|
if passed>=4:
|
|
print(" STRONG EVIDENCE: Fractal echo extends to protein folding")
|
|
elif passed>=3:
|
|
print(" MODERATE EVIDENCE: Partial structural similarity")
|
|
else:
|
|
print(" WEAK EVIDENCE: Limited similarity")
|
|
|
|
if __name__=='__main__':
|
|
main()
|