OSCR

Shedding light on neural learning to rank models for anticancer drug prioritization.

Code ↔ Paper

6 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 6 matches
  1. [1] § 2 Materials and methods › 2.2 Loss functions › 2.2.6 NeuralNDCG. ↔ src/models/loss.py, lines 191–272 · score 0.68 · permutation matrix, NeuralNDCG, ground truth, discounted
  2. [2] § 2 Materials and methods › 2.2 Loss functions › 2.2.6 NeuralNDCG. ↔ src/models/loss.py, lines 191–272 · score 0.67 · Sinkhorn scaling, permutation matrix, NeuralNDCG, temperature
  3. [3] § 3 Experiments › 3.2 Explainability setting ↔ src/shapley_listwise.py, lines 35–116 · score 0.62 · background samples, selected genes, SHAP, Kernel, ranking, drugs
  4. [4] § 3 Experiments › 3.1 Experiment setting › 3.1.2 Train-validation setups. ↔ src/cross_validate.py, lines 295–424 · score 0.61 · cross validation, Adam, Python, PyTorch, fold, optimizer
  5. [5] § 2 Materials and methods › 2.2 Loss functions › 2.2.6 NeuralNDCG. ↔ src/models/loss.py, lines 131–189 · score 0.59 · ideal DCG, ground truth, descending
  6. [6] § 3 Experiments › 3.4 Hyperparameter sensitivity and optimization analysis ↔ src/cross_validate.py, lines 295–424 · score 0.56 · NeuralNDCG, LambdaLoss, threshold, optimization, LambdaRank, metrics

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 411 lines · 17 KB · no license · 3 matches

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as f
  4. import numpy as np
  5. # import logging
  6. from pathlib import Path
  7. class PairPushLoss(nn.Module):
  8. def __init__(self, alpha=0.5, beta=0.1):
  9. super(PairPushLoss, self).__init__()
  10. self.loss = nn.Softplus(beta=1, threshold=50)
  11. self.relu = nn.ReLU()
  12. self.alpha = alpha
  13. self.beta = beta
  14. #self.gamma = gamma
  15. def forward(self, diff, labels1, labels2, sign):
  16. y = np.array(labels1) == np.array(labels2)
  17. pn_pairs = (y == 0) # for (sens, insens) or (insens, sens) pairs
  18. pp_pairs = (y == 1) & (np.array(labels1) == 1) # for (sens,sens) pairs
  19. nn_pairs = (y == 1) & (np.array(labels1) == 0) # for (insens,insens) pairs
  20. # sign should be 1 if (+,-) pair and -1 if (-,+) pair
  21. bloss = 0
  22. if sum(pp_pairs):
  23. bloss = self.alpha*torch.mean(self.loss(-sign[pp_pairs]*diff[pp_pairs]), dim=0)
  24. if sum(pn_pairs):
  25. bloss += (1-self.alpha-self.beta)*torch.mean(self.loss(-sign[pn_pairs]*diff[pn_pairs]), dim=0)
  26. if sum(nn_pairs):
  27. bloss += self.beta*torch.mean(self.loss(-sign[nn_pairs]*diff[nn_pairs]), dim=0)
  28. return bloss
  29. class ListOneLoss(nn.Module):
  30. def __init__(self, M=1):
  31. super(ListOneLoss, self).__init__()
  32. self.M = M
  33. def forward(self, y_pred, y_true):
  34. pred_max = f.softmax(y_pred/self.M, dim=0) + 1e-9
  35. true_max = f.softmax(-y_true/self.M, dim=0) # need to reverse the sign
  36. pred_log = torch.log(pred_max)
  37. return torch.mean(-torch.sum(true_max*pred_log))
  38. class ListAllLoss(nn.Module):
  39. def __init__(self, M=0.5):
  40. super(ListAllLoss, self).__init__()
  41. self.M = M
  42. def forward(self, y_pred, y_label):
  43. pred_max = f.softmax(y_pred/self.M, dim=1) + 1e-9
  44. pred_log = torch.log(pred_max)
  45. return torch.mean(-torch.sum(y_label*pred_log))
  46. class LambdaLoss(nn.Module):
  47. def __init__(self, eps=1e-10, padded_value_indicator=-1, weighing_scheme=None, k=None, sigma=1.0, mu=10.0,
  48. reduction="mean", reduction_log="binary"):
  49. super(LambdaLoss, self).__init__()
  50. self.eps = eps
  51. self.padded_value_indicator = padded_value_indicator
  52. self.weighing_scheme = weighing_scheme
  53. self.k = k
  54. self.sigma = sigma
  55. self.mu = mu
  56. self.reduction = reduction
  57. self.reduction_log = reduction_log
  58. def forward(self, y_pred, y_true):
  59. device = y_pred.device
  60. y_pred = y_pred.clone()
  61. y_true = y_true.clone()
  62. k = self.k or y_true.shape[1]
  63. y_true = y_true.float()
  64. y_true = (y_true.max()-y_true) + y_true.min()
  65. padded_mask = y_true == self.padded_value_indicator
  66. y_pred[padded_mask] = float("-inf")
  67. y_true[padded_mask] = float("-inf")
  68. y_pred_sorted, indices_pred = y_pred.sort(descending=True, dim=-1)
  69. y_true_sorted, _ = y_true.sort(descending=True, dim=-1)
  70. true_sorted_by_preds = torch.gather(y_true, dim=1, index=indices_pred)
  71. true_diffs = true_sorted_by_preds[:, :, None] - true_sorted_by_preds[:, None, :]
  72. padded_pairs_mask = torch.isfinite(true_diffs)
  73. padded_pairs_mask = padded_pairs_mask & (true_diffs >= 0)
  74. ndcg_at_k_mask = torch.zeros((y_pred.shape[1], y_pred.shape[1]), dtype=torch.bool, device=device)
  75. if k is not None:
  76. ndcg_at_k_mask[:k, :k] = 1
  77. true_sorted_by_preds.clamp_(min=0.)
  78. y_true_sorted.clamp_(min=0.)
  79. pos_idxs = torch.arange(1, y_pred.shape[1] + 1).to(device)
  80. D = torch.log2(1. + pos_idxs.float())[None, :]
  81. maxDCGs = torch.sum(((torch.pow(2, y_true_sorted) - 1) / D)[:, :k], dim=-1).clamp(min=self.eps)
  82. G = (torch.pow(2, true_sorted_by_preds) - 1) / maxDCGs[:, None]
  83. if self.weighing_scheme is None:
  84. weights = 1.0
  85. else:
  86. weights = self.weighing_scheme(G, D, self.mu, true_sorted_by_preds)
  87. scores_diffs = (y_pred_sorted[:, :, None] - y_pred_sorted[:, None, :]).clamp(min=-1e8, max=1e8)
  88. scores_diffs.masked_fill(torch.isnan(scores_diffs), 0.0)
  89. weighted_probas = (torch.sigmoid(self.sigma * scores_diffs).clamp(min=self.eps) ** weights).clamp(min=self.eps)
  90. if self.reduction_log == "natural":
  91. losses = torch.log(weighted_probas)
  92. elif self.reduction_log == "binary":
  93. losses = torch.log2(weighted_probas)
  94. else:
  95. raise ValueError("Reduction logarithm base can be either natural or binary")
  96. if self.reduction == "sum":
  97. loss = -torch.sum(losses[padded_pairs_mask & ndcg_at_k_mask])
  98. elif self.reduction == "mean":
  99. loss = -torch.mean(losses[padded_pairs_mask & ndcg_at_k_mask])
  100. else:
  101. raise ValueError("Reduction method can be either sum or mean")
  102. return loss
  103. def lambdaRank_scheme(G, D, *args):
  104. return torch.abs(torch.pow(D[:, :, None], -1.) - torch.pow(D[:, None, :], -1.)) * torch.abs(G[:, :, None] - G[:, None, :])
  105. class ApproxNDCGLoss(nn.Module):
  106. def __init__(self, eps=1e-10, padded_value_indicator=-1, alpha=1.):
  107. """
  108. :param eps: epsilon value, used for numerical stability
  109. :param padded_value_indicator: an indicator of the y_true index containing a padded item, e.g. -1
  110. :param alpha: score difference weight used in the sigmoid function
  111. """
  112. super(ApproxNDCGLoss, self).__init__()
  113. self.eps = eps
  114. self.padded_value_indicator = padded_value_indicator
  115. self.alpha = alpha
  116. def forward(self, y_pred, y_true):
  117. """
  118. Loss based on approximate NDCG introduced in "A General Approximation Framework for Direct Optimization of
  119. Information Retrieval Measures". Please note that this method does not implement any kind of truncation.
  120. :param y_pred: predictions from the model, shape [batch_size, slate_length]
  121. :param y_true: ground truth labels, shape [batch_size, slate_length]
  122. :return: loss value, a torch.Tensor
  123. """
  124. device = y_pred.device
  125. y_pred = y_pred.clone()
  126. y_true = y_true.clone()
  127. y_true = y_true.float()
  128. y_true = (y_true.max()-y_true) + y_true.min()
  129. padded_mask = y_true == self.padded_value_indicator
  130. y_pred[padded_mask] = float("-inf")
  131. y_true[padded_mask] = float("-inf")
  132. # Here we sort the true and predicted relevancy scores.
  133. y_pred_sorted, indices_pred = y_pred.sort(descending=True, dim=-1)
  134. y_true_sorted, _ = y_true.sort(descending=True, dim=-1)
  135. # After sorting, we can mask out the pairs of indices (i, j) containing index of a padded element.
  136. true_sorted_by_preds = torch.gather(y_true, dim=1, index=indices_pred)
  137. true_diffs = true_sorted_by_preds[:, :, None] - true_sorted_by_preds[:, None, :]
  138. padded_pairs_mask = torch.isfinite(true_diffs)
  139. padded_pairs_mask.diagonal(dim1=-2, dim2=-1).zero_()
  140. # Here we clamp the -infs to get correct gains and ideal DCGs (maxDCGs)
  141. true_sorted_by_preds.clamp_(min=0.)
  142. y_true_sorted.clamp_(min=0.)
  143. # Here we find the gains, discounts and ideal DCGs per slate.
  144. pos_idxs = torch.arange(1, y_pred.shape[1] + 1).to(device)
  145. D = torch.log2(1. + pos_idxs.float())[None, :]
  146. maxDCGs = torch.sum((torch.pow(2, y_true_sorted) - 1) / D, dim=-1).clamp(min=self.eps)
  147. G = (torch.pow(2, true_sorted_by_preds) - 1) / maxDCGs[:, None]
  148. # Here we approximate the ranking positions according to Eqs 19-20 and later approximate NDCG (Eq 21)
  149. scores_diffs = (y_pred_sorted[:, :, None] - y_pred_sorted[:, None, :])
  150. scores_diffs[~padded_pairs_mask] = 0.
  151. approx_pos = 1. + torch.sum(padded_pairs_mask.float() * (torch.sigmoid(-self.alpha * scores_diffs).clamp(min=self.eps)), dim=-1)
  152. approx_D = torch.log2(1. + approx_pos)
  153. approx_NDCG = torch.sum((G / approx_D), dim=-1)
  154. return -torch.mean(approx_NDCG)
  155. class NeuralNDCG(nn.Module):
  156. def __init__(self, padded_value_indicator=-1, temperature=22.0, powered_relevancies=True, k=None,
  157. stochastic=False, n_samples=32, beta=0.1, log_scores=True):
  158. super(NeuralNDCG, self).__init__()
  159. self.padded_value_indicator = padded_value_indicator
  160. self.temperature = temperature
  161. self.powered_relevancies = powered_relevancies
  162. self.k = k
  163. self.stochastic = stochastic
  164. self.n_samples = n_samples
  165. self.beta = beta
  166. self.log_scores = log_scores
  167. def forward(self, y_pred, y_true):
  168. dev = y_pred.device
  169. y_true = y_true.float()
  170. y_true = (y_true.max()-y_true) + y_true.min()
  171. #scalling
  172. y_true = (y_true-y_true.min())/(y_true.max()-y_true.min())
  173. k = self.k or y_true.shape[1]
  174. mask = (y_true == self.padded_value_indicator)
  175. if self.stochastic:
  176. P_hat = stochastic_neural_sort(
  177. y_pred.unsqueeze(-1),
  178. n_samples=self.n_samples,
  179. tau=self.temperature,
  180. mask=mask,
  181. beta=self.beta,
  182. log_scores=self.log_scores,
  183. device=dev,
  184. )
  185. else:
  186. P_hat = deterministic_neural_sort(
  187. y_pred.unsqueeze(-1),
  188. tau=self.temperature,
  189. mask=mask,
  190. device=dev,
  191. ).unsqueeze(0)
  192. # Perform Sinkhorn scaling to obtain doubly stochastic permutation matrices
  193. P_hat = sinkhorn_scaling(
  194. P_hat.view(P_hat.shape[0] * P_hat.shape[1], P_hat.shape[2], P_hat.shape[3]),
  195. mask.repeat_interleave(P_hat.shape[0], dim=0),
  196. tol=1e-6, max_iter=50
  197. )
  198. P_hat = P_hat.view(
  199. int(P_hat.shape[0] / y_pred.shape[0]),
  200. y_pred.shape[0],
  201. P_hat.shape[1],
  202. P_hat.shape[2]
  203. )
  204. P_hat = P_hat.masked_fill(mask[None, :, :, None] | mask[None, :, None, :], 0.0)
  205. y_true_masked = y_true.masked_fill(mask, 0.0).unsqueeze(-1).unsqueeze(0)
  206. if self.powered_relevancies:
  207. y_true_masked = torch.pow(2.0, y_true_masked) - 1.0
  208. ground_truth = torch.matmul(P_hat, y_true_masked).squeeze(-1)
  209. discounts = (torch.tensor(1.0) / torch.log2(torch.arange(y_true.shape[-1], dtype=torch.float) + 2.0)).to(dev)
  210. discounted_gains = ground_truth * discounts
  211. if self.powered_relevancies:
  212. idcg = dcg(y_true, y_true, ats=[k]).permute(1, 0)
  213. else:
  214. idcg = dcg(y_true, y_true, ats=[k], gain_function=lambda x: x).permute(1, 0)
  215. discounted_gains = discounted_gains[:, :, :k]
  216. ndcg = discounted_gains.sum(dim=-1) / (idcg + 1e-10)
  217. idcg_mask = idcg == 0.0
  218. ndcg = ndcg.masked_fill(idcg_mask.repeat(ndcg.shape[0], 1), 0.0)
  219. assert (ndcg < 0.0).sum() == 0, "Every NDCG should be non-negative"
  220. if idcg_mask.all():
  221. return torch.tensor(0.0, device=dev)
  222. mean_ndcg = ndcg.sum() / ((~idcg_mask).sum() * ndcg.shape[0])
  223. return -1.0 * mean_ndcg
  224. def sinkhorn_scaling(mat, mask=None, tol=1e-6, max_iter=50):
  225. """
  226. Sinkhorn scaling procedure.
  227. :param mat: a tensor of square matrices of shape N x M x M, where N is batch size
  228. :param mask: a tensor of masks of shape N x M
  229. :param tol: Sinkhorn scaling tolerance
  230. :param max_iter: maximum number of iterations of the Sinkhorn scaling
  231. :return: a tensor of (approximately) doubly stochastic matrices
  232. """
  233. if mask is not None:
  234. mat = mat.masked_fill(mask[:, None, :] | mask[:, :, None], 0.0)
  235. mat = mat.masked_fill(mask[:, None, :] & mask[:, :, None], 1.0)
  236. for _ in range(max_iter):
  237. mat = mat / mat.sum(dim=1, keepdim=True).clamp(min=1e-10)
  238. mat = mat / mat.sum(dim=2, keepdim=True).clamp(min=1e-10)
  239. if torch.max(torch.abs(mat.sum(dim=2) - 1.)) < tol and torch.max(torch.abs(mat.sum(dim=1) - 1.)) < tol:
  240. break
  241. if mask is not None:
  242. mat = mat.masked_fill(mask[:, None, :] | mask[:, :, None], 0.0)
  243. return mat
  244. def deterministic_neural_sort(s, tau, mask, device="cuda"):
  245. """
  246. Deterministic neural sort.
  247. Code taken from "Stochastic Optimization of Sorting Networks via Continuous Relaxations", ICLR 2019.
  248. Minor modifications applied to the original code (masking).
  249. :param s: values to sort, shape [batch_size, slate_length]
  250. :param tau: temperature for the final softmax function
  251. :param mask: mask indicating padded elements
  252. :return: approximate permutation matrices of shape [batch_size, slate_length, slate_length]
  253. """
  254. n = s.size()[1]
  255. one = torch.ones((n, 1), dtype=torch.float32, device=device)
  256. s = s.masked_fill(mask[:, :, None], -1e8)
  257. A_s = torch.abs(s - s.permute(0, 2, 1))
  258. A_s = A_s.masked_fill(mask[:, :, None] | mask[:, None, :], 0.0)
  259. B = torch.matmul(A_s, torch.matmul(one, torch.transpose(one, 0, 1)))
  260. temp = [n - m + 1 - 2 * (torch.arange(n - m, device=device) + 1) for m in mask.squeeze(-1).sum(dim=1)]
  261. temp = [t.type(torch.float32) for t in temp]
  262. temp = [torch.cat((t, torch.zeros(n - len(t), device=device))) for t in temp]
  263. scaling = torch.stack(temp).type(torch.float32).to(device) # type: ignore
  264. s = s.masked_fill(mask[:, :, None], 0.0)
  265. C = torch.matmul(s, scaling.unsqueeze(-2))
  266. P_max = (C - B).permute(0, 2, 1)
  267. P_max = P_max.masked_fill(mask[:, :, None] | mask[:, None, :], -np.inf)
  268. P_max = P_max.masked_fill(mask[:, :, None] & mask[:, None, :], 1.0)
  269. sm = torch.nn.Softmax(-1)
  270. P_hat = sm(P_max / tau)
  271. return P_hat
  272. def stochastic_neural_sort(s, n_samples, tau, mask, beta=1.0, log_scores=True, eps=1e-10, device="cuda"):
  273. """
  274. Stochastic neural sort. Please note that memory complexity grows by factor n_samples.
  275. Code taken from "Stochastic Optimization of Sorting Networks via Continuous Relaxations", ICLR 2019.
  276. Minor modifications applied to the original code (masking).
  277. :param s: values to sort, shape [batch_size, slate_length]
  278. :param n_samples: number of samples (approximations) for each permutation matrix
  279. :param tau: temperature for the final softmax function
  280. :param mask: mask indicating padded elements
  281. :param beta: scale parameter for the Gumbel distribution
  282. :param log_scores: whether to apply the logarithm function to scores prior to Gumbel perturbation
  283. :param eps: epsilon for the logarithm function
  284. :return: approximate permutation matrices of shape [n_samples, batch_size, slate_length, slate_length]
  285. """
  286. batch_size = s.size()[0]
  287. n = s.size()[1]
  288. s_positive = s + torch.abs(s.min())
  289. samples = beta * sample_gumbel([n_samples, batch_size, n, 1], device=device)
  290. if log_scores:
  291. s_positive = torch.log(s_positive + eps)
  292. s_perturb = (s_positive + samples).view(n_samples * batch_size, n, 1)
  293. mask_repeated = mask.repeat_interleave(n_samples, dim=0)
  294. P_hat = deterministic_neural_sort(s_perturb, tau, mask_repeated)
  295. P_hat = P_hat.view(n_samples, batch_size, n, n)
  296. return P_hat
  297. def dcg(y_pred, y_true, ats=None, gain_function=lambda x: torch.pow(2, x) - 1, padding_indicator=-1):
  298. """
  299. Discounted Cumulative Gain at k.
  300. Compute DCG at ranks given by ats or at the maximum rank if ats is None.
  301. :param y_pred: predictions from the model, shape [batch_size, slate_length]
  302. :param y_true: ground truth labels, shape [batch_size, slate_length]
  303. :param ats: optional list of ranks for DCG evaluation, if None, maximum rank is used
  304. :param gain_function: callable, gain function for the ground truth labels, e.g. torch.pow(2, x) - 1
  305. :param padding_indicator: an indicator of the y_true index containing a padded item, e.g. -1
  306. :return: DCG values for each slate and evaluation position, shape [batch_size, len(ats)]
  307. """
  308. y_true = y_true.clone()
  309. y_pred = y_pred.clone()
  310. actual_length = y_true.shape[1]
  311. if ats is None:
  312. ats = [actual_length]
  313. ats = [min(at, actual_length) for at in ats]
  314. true_sorted_by_preds = __apply_mask_and_get_true_sorted_by_preds(y_pred, y_true, padding_indicator)
  315. discounts = (torch.tensor(1) / torch.log2(torch.arange(true_sorted_by_preds.shape[1], dtype=torch.float) + 2.0)).to(
  316. device=true_sorted_by_preds.device)
  317. gains = gain_function(true_sorted_by_preds)
  318. discounted_gains = (gains * discounts)[:, :np.max(ats)]
  319. cum_dcg = torch.cumsum(discounted_gains, dim=1)
  320. ats_tensor = torch.tensor(ats, dtype=torch.long) - torch.tensor(1)
  321. dcg = cum_dcg[:, ats_tensor]
  322. return dcg
  323. def __apply_mask_and_get_true_sorted_by_preds(y_pred, y_true, padding_indicator=-1):
  324. mask = y_true == padding_indicator
  325. y_pred[mask] = float('-inf')
  326. y_true[mask] = 0.0
  327. _, indices = y_pred.sort(descending=True, dim=-1)
  328. return torch.gather(y_true, dim=1, index=indices)

loss.py at commit 35b0c4b, no license · at the source

Overview

Authors: Faraz Sarmeili1,2, Benyamin Ghahremani-Nezhad3, Mohammad Khalilpour3, Karim Abbasi4, Rassoul Dinarvand1,5, Hamid R Rabiee2
  1. Department of Pharmaceutics, Faculty of Pharmacy, Tehran University of Medical Sciences, Tehran, Iran
  2. Department of Computer Engineering, Sharif University of Technology, Tehran, Iran
  3. Department of Computer Engineering, Amirkabir University of Technology, Tehran, Iran
  4. Mosaheb Institute for Mathematical Research, Kharazmi University, Tehran, Iran
  5. Nanotechnology Research Centre, Faculty of Pharmacy, Tehran University of Medical Sciences, Tehran, Iran
Journal: PloS one, volume 21, issue 8, article e0345854
Dates: received 2 December 2025; accepted 11 March 2026; published online 13 August 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1371/journal.pone.0345854 · PMID 42594139 · PMCID PMC13472410 · OpenAlex W7202350312
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), other condition (population)
Methods: Statistics, Machine learning
MeSH: Antineoplastic Agents*, Neural Networks, Computer*, Breast Neoplasms, Cell Line, Tumor, Female, Humans (* major topic)
Topic: Computational Drug Discovery Methods (Computational Theory and Mathematics, Computer Science), according to OpenAlex
Citations: not cited yet (Europe PMC); 63 references in the paper

Abstract

Learning to Rank (LeToR) methods have gained increasing attention in drug response prediction, offering a direct way to prioritize effective treatments for cancer cell lines. In this study, we systematically benchmark six ranking loss functions, including state-of-the-art listwise methods, and five types of molecular representations across two large-scale drug screening datasets, CTRP and PRISM. Using high-dimensional gene expression profiles and various drug fingerprints and descriptors, we evaluated models under multiple validation setups and ranking metrics. Our results demonstrate that listwise loss functions such as LambdaLoss and LambdaRank consistently excel in both early and overall ranking quality. Additionally, combining molecular fingerprints with physicochemical descriptors yielded improved performance. A novel attention-based mechanism and a modified version of RankingSHAP were integrated to enhance interpretability, uncovering key genes and substructures aligned with known biological insights. The explainability pipeline successfully distinguished estrogen receptor-positive (ER⁺) and estrogen receptor-negative (ER−) breast cancer subtypes. The model successfully identified critical substructures in docetaxel, an FDA-approved therapy, and triptolide, which is currently undergoing clinical evaluation for breast cancer. These findings are consistent with established structure-activity relationship (SAR) data. Overall, this study presents a comprehensive evaluation framework and underscores the importance of carefully selecting loss functions and feature representations when developing robust and interpretable drug-ranking systems.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.

Sarmeili/NeuralDrugRanker

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 35b0c4b4df6d2a2148f35ac9ce6797300549de26, 8 April 2025
Languages: Python (35), Shell (12), Jupyter (1)
Size: 54 files, 48 scripts
Software Heritage: not archived
Found in: “Data Availability”
Holds: README, 1 notebook
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (32 files), PyTorch (17 files), pandas (12 files), Matplotlib (6 files), RDKit (6 files), scikit-learn (6 files), SciPy (5 files), NetworkX (2 files), PyTorch Geometric (2 files), seaborn (2 files), SHAP (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
49 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 48 scripts, each with its path and the digest of its content;
  • 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data Availability

https://github.com/Sarmeili/NeuralDrugRanker.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 6 MeSH terms, 53 references.

Cite

This paper

Sarmeili, F., Ghahremani-Nezhad, B., Khalilpour, M., Abbasi, K., Dinarvand, R., & Rabiee, H. R. (2026). Shedding light on neural learning to rank models for anticancer drug prioritization. PloS one, 21(8), e0345854. https://doi.org/10.1371/journal.pone.0345854

BibTeX

@article{sarmeili2026shedding,
author = {Sarmeili, Faraz and Ghahremani-Nezhad, Benyamin and Khalilpour, Mohammad and Abbasi, Karim and Dinarvand, Rassoul and Rabiee, Hamid R},
title = {{Shedding light on neural learning to rank models for anticancer drug prioritization}},
journal = {PloS one},
year = {2026},
month = aug,
volume = {21},
number = {8},
pages = {e0345854},
publisher = {PLOS},
issn = {1932-6203},
doi = {10.1371/journal.pone.0345854},
url = {https://doi.org/10.1371/journal.pone.0345854},
pmid = {42594139},
pmcid = {PMC13472410}
}

RIS

TY - JOUR
AU - Sarmeili, Faraz
AU - Ghahremani-Nezhad, Benyamin
AU - Khalilpour, Mohammad
AU - Abbasi, Karim
AU - Dinarvand, Rassoul
AU - Rabiee, Hamid R
TI - Shedding light on neural learning to rank models for anticancer drug prioritization
T2 - PloS one
J2 - PLoS One
PY - 2026
DA - 2026/08/13
VL - 21
IS - 8
SP - e0345854
SN - 1932-6203
PB - PLOS
DO - 10.1371/journal.pone.0345854
UR - https://doi.org/10.1371/journal.pone.0345854
LA - en
ER -

CSL-JSON

{
"id": "10.1371/journal.pone.0345854",
"type": "article-journal",
"title": "Shedding light on neural learning to rank models for anticancer drug prioritization",
"container-title": "PloS one",
"author": [
{
"family": "Sarmeili",
"given": "Faraz"
},
{
"family": "Ghahremani-Nezhad",
"given": "Benyamin"
},
{
"family": "Khalilpour",
"given": "Mohammad"
},
{
"family": "Abbasi",
"given": "Karim"
},
{
"family": "Dinarvand",
"given": "Rassoul"
},
{
"family": "Rabiee",
"given": "Hamid R"
}
],
"container-title-short": "PLoS One",
"volume": "21",
"issue": "8",
"page": "e0345854",
"DOI": "10.1371/journal.pone.0345854",
"PMID": "42594139",
"PMCID": "PMC13472410",
"ISSN": "1932-6203",
"publisher": "PLOS",
"URL": "https://doi.org/10.1371/journal.pone.0345854",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
13
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.34133/csbj.0184 [code]
Cross-Species Multitask Learning with Molecular and ADME Descriptors for Liver Microsomal Metabolic Stability.
Journal: Computational and structural biotechnology journal
In common: RDKit, SHAP, PyTorch Geometric, 5 other tools, 1 reference
[2] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: RDKit, SHAP, NetworkX, 7 other tools
[3] doi:10.1093/nar/gkag706 [code]
scDifformer: diffusion-based post-training for virtual cell modeling across large-scale single-cell data.
Journal: Nucleic acids research
In common: RDKit, PyTorch Geometric, NetworkX, 7 other tools
[4] doi:10.1093/bib/bbag118 [code]
Drug screening for α-synuclein aggregation inhibitors via multimodal graph neural network.
Journal: Briefings in bioinformatics
In common: RDKit, PyTorch Geometric, NetworkX, 7 other tools
[5] doi:10.1021/acsomega.5c09368 [code]
Structure-Based and AI-Assisted Identification of AGPS Inhibitors for Glioma via Integrated Docking, Molecular Dynamics, and Binding Affinity Screening.
Journal: ACS omega
In common: RDKit, PyTorch Geometric, PyTorch, 6 other tools, other condition
[6] doi:10.1038/s41586-026-10670-w [code]
Zero-shot design of drug-binding proteins via neural iterative selection-expansion.
Journal: Nature
In common: RDKit, PyTorch Geometric, PyTorch, 6 other tools
[7] doi:10.3390/ph19081319 [code]
DeepBBB: A Data-Composition-Aware Graph Screening Workflow for BBB-Focused CNS Library Construction and Prospective PAMPA-BBB Evaluation.
Journal: Pharmaceuticals (Basel, Switzerland)
In common: RDKit, PyTorch Geometric, NetworkX, 5 other tools
[8] doi:10.1038/s41398-026-04101-7 [code]
Using deep learning to identify brain networks mediating cognitive and motor impairments in alcohol use disorder.
Journal: Translational psychiatry
In common: PyTorch Geometric, NetworkX, PyTorch, 6 other tools, other condition
[9] doi:10.1038/s41467-026-71759-4 [code]
CellNiche represents cellular microenvironments in atlas-scale spatial omics data with contrastive learning.
Journal: Nature communications
In common: PyTorch Geometric, NetworkX, PyTorch, 6 other tools, other condition
[10] doi:10.1523/eneuro.0362-25.2026 [code]
Similarities between &lt;i&gt;Ciona&lt;/i&gt; Dorsal Motor Ganglion and Vertebrate Cerebellum: Did a Chordate Ancestor Already Show D/V Subdivision within a Hindbrain Precursor?
Journal: eNeuro
In common: SHAP, PyTorch Geometric, PyTorch, 6 other tools

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.