| """Editable vector workflow and empty result-figure spaces.""" |
| from pathlib import Path |
| import numpy as np |
| import matplotlib |
| matplotlib.use('Agg') |
| import matplotlib.pyplot as plt |
| from matplotlib.font_manager import fontManager |
| from matplotlib.patches import FancyArrowPatch, Circle, FancyBboxPatch, Rectangle |
| from PIL import Image |
| ROOT=Path(__file__).resolve().parents[1] |
| for f in (ROOT/'assets/fonts').glob('*.ttf'):fontManager.addfont(str(f)) |
| plt.rcParams.update({'font.family':'Ubuntu','font.size':13,'mathtext.fontset':'cm','pdf.fonttype':42,'svg.fonttype':'none'}) |
| NAVY='#011F5B';PINK='#FBE8EF';ROSE='#B85C83';BLUE='#7C9DBC';GRAY='#617083' |
| OUT=ROOT/'assets/figures';OUT.mkdir(parents=True,exist_ok=True) |
|
|
| def text(ax,x,y,s,size=13,weight='normal',ha='center',color=NAVY,**kw): |
| return ax.text(x,y,s,fontsize=size,fontweight=weight,ha=ha,va='center',color=color,**kw) |
| def arrow(ax,a,b,rad=0,color=NAVY,lw=1.5): |
| ax.add_patch(FancyArrowPatch(a,b,connectionstyle=f'arc3,rad={rad}',arrowstyle='-|>',mutation_scale=13,lw=lw,color=color)) |
| def peptide(ax,x,y,width=.8,noncanonical=False): |
| t=np.linspace(0,1,9);yy=y+.095*np.sin(t*3*np.pi) |
| ax.plot(x+width*t,yy,c=ROSE,lw=2) |
| for i,(xx,yyy) in enumerate(zip(x+width*t,yy)): |
| ax.scatter([xx],[yyy],s=28,marker='D' if noncanonical and i in [2,6] else 'o',c=NAVY if noncanonical and i in [2,6] else PINK,edgecolors=ROSE,zorder=5) |
| def protein(ax,x,y,scale=.40,phase=0): |
| t=np.linspace(0,2*np.pi,180);r=1+.16*np.sin(5*t+phase)+.1*np.cos(3*t) |
| ax.fill(x+scale*r*np.cos(t),y+scale*.8*r*np.sin(t),color=BLUE,alpha=.18) |
| for j in range(3): |
| q=np.linspace(0,1,100) |
| ax.plot(x+scale*(1.55*q-.78),y+scale*(.45*np.sin(3*np.pi*q+j*.8)+.2*(j-1)),color=BLUE,lw=1.8) |
| def save(fig,name): |
| for ext in ['pdf','svg','png']:fig.savefig(OUT/(name+'.'+ext),dpi=200,bbox_inches='tight',facecolor='white',pad_inches=.07) |
| plt.close(fig) |
|
|
| fig,ax=plt.subplots(figsize=(13.6,9.5));ax.set(xlim=(0,13.6),ylim=(0,9.5));ax.axis('off') |
| def box(x,y,w,h,title,number): |
| ax.add_patch(FancyBboxPatch((x,y),w,h,boxstyle='round,pad=.03,rounding_size=.10',fc='white',ec=BLUE,lw=1)) |
| ax.add_patch(FancyBboxPatch((x,y+h-.48),w,.48,boxstyle='round,pad=.03,rounding_size=.08',fc=PINK,ec='none')) |
| text(ax,x+.17,y+h-.24,str(number),13,'bold',ha='left',color=ROSE) |
| text(ax,x+.51,y+h-.24,title,13,'bold',ha='left') |
| |
| text(ax,.1,9.17,'PepPA',27,'bold',ha='left') |
| for name,label,x,w in [('meta.png','Muse Glimmer-30B',7.0,.40),('ChatGPT-Logo.png','GPT-6 Astra Max',9.63,.43),('Claude_logo.png','Claude Opus Max',12.25,.30)]: |
| im=Image.open(ROOT/'assets/logos'/name).convert('RGBA');h=min(.28,w*im.height/im.width) |
| ax.imshow(im,extent=(x-w/2,x+w/2,9.15,9.15+h),aspect='auto',zorder=8) |
| text(ax,x,8.93,label,10) |
| box(.1,6.54,4.04,1.99,'Define the design goal',1) |
| text(ax,.30,7.73,'Specify target and countertargets',11.8,ha='left') |
| text(ax,.30,7.30,'Set chemistry and property thresholds',11.2,ha='left') |
| text(ax,.30,6.86,'Choose human and preclinical contexts',11.1,ha='left') |
| box(4.76,6.54,4.04,1.99,'Retrieve molecular evidence',2) |
| text(ax,4.96,7.73,'Map sites, states and interactions',11.5,ha='left') |
| text(ax,4.96,7.30,'Link each hypothesis to source passages',10.8,ha='left') |
| text(ax,4.96,6.86,'Specify comparisons that test it',11.5,ha='left') |
| box(9.42,6.54,4.04,1.99,'Fix the computational plan',3) |
| text(ax,9.62,7.73,'Choose models and input requirements',10.9,ha='left') |
| text(ax,9.62,7.30,'Set run order, seeds and compute limits',10.8,ha='left') |
| text(ax,9.62,6.86,'Check that all required scores can be run',10.5,ha='left') |
| arrow(ax,(4.2,7.48),(4.68,7.48));arrow(ax,(8.87,7.48),(9.34,7.48)) |
| |
| box(.1,2.83,6.29,3.13,'Generate and score peptides',4) |
| box(7.13,2.83,6.33,3.13,'Verify the proposed peptides',5) |
| ax.plot([11.42,11.42,3.20],[6.48,6.22,6.22],color=NAVY,lw=1.4) |
| arrow(ax,(3.2,6.22),(3.2,6.00)) |
| text(ax,.34,5.14,'Sequence generation',12.3,'bold',ha='left') |
| text(ax,.34,4.77,'PepDFM with MOG-DFM',11.1,ha='left') |
| text(ax,3.53,5.14,'Chemical generation',12.3,'bold',ha='left') |
| text(ax,3.53,4.77,'PepMDLM with PepTune',11.1,ha='left') |
| peptide(ax,.46,4.31,1.15) |
| peptide(ax,3.74,4.31,1.15,noncanonical=True) |
| text(ax,2.49,4.32,'Sequence',9.8) |
| text(ax,5.54,4.32,'SMILES',9.8) |
| text(ax,.34,3.89,'Guide sampling with functional and property scores',11.4,ha='left') |
| text(ax,.34,3.53,'Combine compatible predictors for each endpoint',11.4,ha='left') |
| text(ax,3.25,3.13,r'Maximize the weakest context margin $\min_z m_{kz}(x)$',12) |
| arrow(ax,(6.45,4.35),(7.06,4.35)) |
| protein(ax,7.75,5.03,.30);peptide(ax,7.68,4.83,.63) |
| text(ax,8.6,5.04,'Check target and countertarget complexes',10.8,ha='left') |
| text(ax,7.38,4.54,'Compare external ADMET predictions',11.3,ha='left') |
| text(ax,7.38,4.08,'Check synthesis and formulation requirements',11.3,ha='left') |
| text(ax,7.38,3.61,'Evaluate every required biological context',11.3,ha='left') |
| text(ax,7.38,3.15,'Save scores, uncertainty and failed checks',11.3,'bold',ha='left') |
| |
| arrow(ax,(9.50,2.78),(3.6,2.78),rad=-.10,color=ROSE) |
| text(ax,6.74,2.33,'Use failed computational checks for one redesign pass',11.2,color=ROSE) |
| arrow(ax,(12.9,2.80),(12.9,1.91)) |
| box(.1,.16,13.36,1.66,'Select final sequences that meet the complete design specification',6) |
| peptide(ax,.41,1.00,1.29,noncanonical=True) |
| text(ax,.35,.50,'Canonical or noncanonical',10.1,ha='left') |
| text(ax,3.08,1.01,'Molecular identity',12.2,'bold',ha='left') |
| text(ax,3.08,.62,'Sequence, monomers and bonds',10.3,ha='left') |
| text(ax,6.89,1.01,'Complete property report',12.2,'bold',ha='left') |
| text(ax,6.89,.62,'PeptiVerse and external scores',10.3,ha='left') |
| text(ax,10.53,1.01,'Reproducible records',12.2,'bold',ha='left') |
| text(ax,10.53,.62,'Sources and execution trace',10.3,ha='left') |
| save(fig,'figure1') |
| |
| for n in range(2,7): |
| fig=plt.figure(figsize=(10,2.25));ax=fig.add_axes([.012,.045,.976,.91]);ax.set(xlim=(0,1),ylim=(0,1));ax.axis('off') |
| ax.add_patch(Rectangle((.002,.002),.996,.996,fill=False,ec='#DDE3EB',lw=.8)) |
| save(fig,f'figure{n}') |
| print(OUT) |
|
|