anzhi2710gmailcom's picture
Upload folder using huggingface_hub
d766458 verified
Raw
History Blame Contribute Delete
8.23 kB
import jax
import jax.numpy as jnp
import numpy as np
import optax
from colabdesign.shared.utils import copy_dict, update_dict, softmax, Key
from colabdesign.shared.prep import rewire
from colabdesign.af.alphafold.common import residue_constants
aa_order = residue_constants.restype_order
order_aa = {b:a for a,b in aa_order.items()}
class design_model:
def set_weights(self, *args, **kwargs):
'''
set weights
-------------------
note: model.restart() resets the weights to their defaults
use model.set_weights(..., set_defaults=True) to avoid this
-------------------
model.set_weights(rmsd=1)
'''
if kwargs.pop("set_defaults", False):
update_dict(self._opt["weights"], *args, **kwargs)
update_dict(self.opt["weights"], *args, **kwargs)
def set_seq(self, seq=None, mode=None, bias=None, rm_aa=None, set_state=True, **kwargs):
'''
set sequence params and bias
-----------------------------------
-seq=str or seq=[str,str] or seq=array(shape=(L,20) or shape=(?,L,20))
-mode=
-"wildtype"/"wt" = initialize sequence with sequence saved from input PDB
-"gumbel" = initial sequence with gumbel distribution
-"soft_???" = apply softmax-activation to initailized sequence (eg. "soft_gumbel")
-bias=array(shape=(20,) or shape=(L,20)) - bias the sequence
-rm_aa="C,W" = specify which amino acids to remove (aka. add a negative-infinity bias to these aa)
-----------------------------------
'''
# backward compatibility
seq_init = kwargs.pop("seq_init",None)
if seq_init is not None:
modes = ["soft","gumbel","wildtype","wt"]
if isinstance(seq_init,str):
seq_init = seq_init.split("_")
if isinstance(seq_init,list) and seq_init[0] in modes:
mode = seq_init
else:
seq = seq_init
if mode is None: mode = []
# decide on shape
shape = (self._num, self._len, self._args.get("alphabet_size",20))
# initialize bias
if bias is None:
b = np.zeros(shape[1:])
else:
b = np.array(np.broadcast_to(bias, shape[1:]))
# disable certain amino acids
if rm_aa is not None:
for aa in rm_aa.split(","):
b[...,aa_order[aa]] -= 1e6
# use wildtype sequence
if ("wildtype" in mode or "wt" in mode) and hasattr(self,"_wt_aatype"):
wt_seq = np.eye(shape[-1])[self._wt_aatype]
wt_seq[self._wt_aatype == -1] = 0
if "pos" in self.opt and self.opt["pos"].shape[0] == wt_seq.shape[0]:
seq = np.zeros(shape)
seq[:,self.opt["pos"],:] = wt_seq
else:
seq = wt_seq
# initialize sequence
if seq is None:
if hasattr(self,"key"):
x = 0.01 * np.random.normal(size=shape)
else:
x = np.zeros(shape)
else:
if isinstance(seq, str):
seq = [seq]
if isinstance(seq, list):
if isinstance(seq[0], str):
aa_dict = copy_dict(aa_order)
if shape[-1] > 21:
aa_dict["-"] = 21 # add gap character
seq = np.asarray([[aa_dict.get(aa,-1) for aa in s] for s in seq])
else:
seq = np.asarray(seq)
else:
seq = np.asarray(seq)
if np.issubdtype(seq.dtype, np.integer):
seq_ = np.eye(shape[-1])[seq]
seq_[seq == -1] = 0
seq = seq_
if kwargs.pop("add_seq",False):
b = b + seq * 1e7
if seq.ndim == 2:
x = np.pad(seq[None],[[0,shape[0]-1],[0,0],[0,0]])
elif shape[0] > seq.shape[0]:
x = np.pad(seq,[[0,shape[0]-seq.shape[0]],[0,0],[0,0]])
else:
x = seq
if "gumbel" in mode:
y_gumbel = jax.random.gumbel(self.key(),shape)
if "soft" in mode:
y = softmax(x + b + y_gumbel)
elif "alpha" in self.opt:
y = x + y_gumbel / self.opt["alpha"]
else:
y = x + y_gumbel
x = np.where(x.sum(-1,keepdims=True) == 1, x, y)
# set seq/bias/state
self._params["seq"] = x
self._inputs["bias"] = b
def _norm_seq_grad(self):
g = self.aux["grad"]["seq"]
eff_L = (np.square(g).sum(-1,keepdims=True) > 0).sum(-2,keepdims=True)
gn = np.linalg.norm(g,axis=(-1,-2),keepdims=True)
self.aux["grad"]["seq"] = g * np.sqrt(eff_L) / (gn + 1e-7)
def set_optimizer(self, optimizer=None, learning_rate=None, norm_seq_grad=None, **kwargs):
'''
set/reset optimizer
----------------------------------
supported optimizers include: [adabelief, adafactor, adagrad, adam, adamw,
fromage, lamb, lars, noisy_sgd, dpsgd, radam, rmsprop, sgd, sm3, yogi]
'''
optimizers = {'adabelief':optax.adabelief,'adafactor':optax.adafactor,
'adagrad':optax.adagrad,'adam':optax.adam,
'adamw':optax.adamw,'fromage':optax.fromage,
'lamb':optax.lamb,'lars':optax.lars,
'noisy_sgd':optax.noisy_sgd,'dpsgd':optax.dpsgd,
'radam':optax.radam,'rmsprop':optax.rmsprop,
'sgd':optax.sgd,'sm3':optax.sm3,'yogi':optax.yogi}
if optimizer is None: optimizer = self._args["optimizer"]
if learning_rate is not None: self.opt["learning_rate"] = learning_rate
if norm_seq_grad is not None: self.opt["norm_seq_grad"] = norm_seq_grad
o = optimizers[optimizer](1.0, **kwargs)
self._state = o.init(self._params)
def update_grad(state, grad, params):
updates, state = o.update(grad, state, params)
grad = jax.tree_util.tree_map(lambda x:-x, updates)
return state, grad
self._optimizer = jax.jit(update_grad)
def set_seed(self, seed=None):
np.random.seed(seed=seed)
self.key = Key(seed=seed).get
def get_seq(self, get_best=True):
'''
get sequences as strings
- set get_best=False, to get the last sampled sequence
'''
aux = self._tmp["best"]["aux"] if (get_best and "aux" in self._tmp["best"]) else self.aux
x = aux["seq"]["hard"].argmax(-1)
return ["".join([order_aa[a] for a in s]) for s in x]
def get_seqs(self, get_best=True):
return self.get_seq(get_best)
def rewire(self, order=None, offset=0, loops=0):
'''
helper function for "partial" protocol
-----------------------------------------
-order=[0,1,2] - change order of specified segments
-offset=0 - specify start position of the first segment
-loops=[3,2] - specified loop lengths between segments
-----------------------------------------
'''
self.opt["pos"] = rewire(length=self._pos_info["length"], order=order,
offset=offset, loops=loops)
# make default
if hasattr(self,"_opt"): self._opt["pos"] = self.opt["pos"]
def soft_seq(x, bias, opt, key=None, num_seq=None, shuffle_first=True):
seq = {"input":x}
# shuffle msa
if x.ndim == 3 and x.shape[0] > 1 and key is not None:
key, sub_key = jax.random.split(key)
if num_seq is None or x.shape[0] == num_seq:
# randomly pick which sequence is query
if shuffle_first:
n = jax.random.randint(sub_key,[],0,x.shape[0])
seq["input"] = seq["input"].at[0].set(seq["input"][n]).at[n].set(seq["input"][0])
else:
n = jnp.arange(x.shape[0])
if shuffle_first:
n = jax.random.permutation(sub_key,n)
else:
n = jnp.append(0,jax.random.permutation(sub_key,n[1:]))
seq["input"] = seq["input"][n[:num_seq]]
# straight-through/reparameterization
seq["logits"] = seq["input"] * opt["alpha"]
if bias is not None: seq["logits"] = seq["logits"] + bias
seq["pssm"] = jax.nn.softmax(seq["logits"])
seq["soft"] = jax.nn.softmax(seq["logits"] / opt["temp"])
seq["hard"] = jax.nn.one_hot(seq["soft"].argmax(-1), seq["soft"].shape[-1])
seq["hard"] = jax.lax.stop_gradient(seq["hard"] - seq["soft"]) + seq["soft"]
# create pseudo sequence
seq["pseudo"] = opt["soft"] * seq["soft"] + (1-opt["soft"]) * seq["input"]
seq["pseudo"] = opt["hard"] * seq["hard"] + (1-opt["hard"]) * seq["pseudo"]
return seq