Source code for rnaglib.transforms.represent.sequence
import torch
import networkx as nx
from rnaglib.algorithms import get_sequences
from .representation import Representation
[docs]
class SequenceRepresentation(Representation):
"""
Represents RNA as a linear sequence following the 5'to 3' order of backbone edges. Note that this only works on single-chain. If you have a multi-chain RNA make sure to first apply the ``ChainSplitTransform``.
RNAs. When using a graph-based framework (e.g. pyg or dgl) the RNA is stored as a linear graph with edges going in 5' to 3' as well as 3' to 3'. This can be controlled using the `backbone` argument.
:param framework: which learning framework to store representation.
:param backbone: if 'both' graph will have 5' -> 3' edges and 3' -> 5', if '5p3p' will only have the former and if '3p5p' only the latter.
"""
[docs]
def __init__(
self,
framework: str = "pyg",
backbone: str = "both",
**kwargs,
):
authorized_frameworks = {"pyg", "torch"}
assert framework in authorized_frameworks, (
f"Framework {framework} not supported for this representation. " f"Choose one of {authorized_frameworks}."
)
self.framework = framework
super().__init__(**kwargs)
pass
def __call__(self, rna_graph, features_dict):
sequence_dict = get_sequences(rna_graph)
full_seq = ""
full_nid = []
chain_index = []
for i, (chain_id, (seq, nids)) in enumerate(sequence_dict.items()):
full_seq += seq
full_nid.extend(nids)
chain_index.extend([i] * len(seq))
if self.framework == "torch":
data = self.to_torch(full_nid, features_dict, chain_index)
return data
if self.framework == "pyg":
data = self.to_pyg(full_nid, features_dict, chain_index)
return data
def to_torch(self, node_ids, features_dict, chain_index):
x, y = None, None
if "nt_features" in features_dict:
x = (
torch.stack([features_dict["nt_features"][n] for n in node_ids])
if "nt_features" in features_dict
else None
)
if "nt_targets" in features_dict:
list_y = [features_dict["nt_targets"][n] for n in node_ids]
# In the case of single target, pytorch CE loss expects shape (n,) and not (n,1)
# For multi-target cases, we stack to get (n,d)
if len(list_y[0]) == 1:
y = torch.cat(list_y)
else:
y = torch.stack(list_y)
if "rna_targets" in features_dict:
y = torch.tensor(features_dict["rna_targets"])
chain_index = torch.tensor(chain_index, dtype=torch.long)
return x, chain_index
def to_pyg(self, node_ids, features_dict, chain_index):
try:
from torch_geometric.data import Data
except ImportError:
raise ImportError("torch_geometric is required for sequence representation")
# for some reason from_networkx is not working so doing by hand
# not super efficient at the moment
x, y = None, None
if "nt_features" in features_dict:
x = (
torch.stack([features_dict["nt_features"][n] for n in node_ids])
if "nt_features" in features_dict
else None
)
if "nt_targets" in features_dict:
list_y = [features_dict["nt_targets"][n] for n in node_ids]
# In the case of single target, pytorch CE loss expects shape (n,) and not (n,1)
# For multi-target cases, we stack to get (n,d)
if len(list_y[0]) == 1:
y = torch.cat(list_y)
else:
y = torch.stack(list_y)
if "rna_targets" in features_dict:
y = torch.tensor(features_dict["rna_targets"])
chain_index = torch.tensor(chain_index, dtype=torch.long)
return Data(x=x, y=y, chain_index=chain_index)
@property
def name(self):
return "sequence"
def batch(self, samples):
"""
Batch a list of graph samples
:param samples: A list of the output from this representation
:return: a batched version of it.
"""
if self.framework == "pyg":
try:
from torch_geometric.data import Batch
except ImportError:
raise ImportError("torch_geometric is required for sequence representation")
batch = Batch.from_data_list(samples)
# sometimes batching changes dtype from int to float32?
return batch