Shedding light on neural learning to rank models for anticancer drug prioritization.
The 6 matches
- [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 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 Experiments › 3.2 Explainability setting ↔ src/shapley_listwise.py, lines 35–116 · score 0.62 · background samples, selected genes, SHAP, Kernel, ranking, drugs
- [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] § 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] § 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
- import torch
- import torch.nn as nn
- import torch.nn.functional as f
- import numpy as np
- # import logging
- from pathlib import Path
- class PairPushLoss(nn.Module):
- def __init__(self, alpha=0.5, beta=0.1):
- super(PairPushLoss, self).__init__()
- self.loss = nn.Softplus(beta=1, threshold=50)
- self.relu = nn.ReLU()
- self.alpha = alpha
- self.beta = beta
- #self.gamma = gamma
- def forward(self, diff, labels1, labels2, sign):
- y = np.array(labels1) == np.array(labels2)
- pn_pairs = (y == 0) # for (sens, insens) or (insens, sens) pairs
- pp_pairs = (y == 1) & (np.array(labels1) == 1) # for (sens,sens) pairs
- nn_pairs = (y == 1) & (np.array(labels1) == 0) # for (insens,insens) pairs
- # sign should be 1 if (+,-) pair and -1 if (-,+) pair
- bloss = 0
- if sum(pp_pairs):
- bloss = self.alpha*torch.mean(self.loss(-sign[pp_pairs]*diff[pp_pairs]), dim=0)
- if sum(pn_pairs):
- bloss += (1-self.alpha-self.beta)*torch.mean(self.loss(-sign[pn_pairs]*diff[pn_pairs]), dim=0)
- if sum(nn_pairs):
- bloss += self.beta*torch.mean(self.loss(-sign[nn_pairs]*diff[nn_pairs]), dim=0)
- return bloss
- class ListOneLoss(nn.Module):
- def __init__(self, M=1):
- super(ListOneLoss, self).__init__()
- self.M = M
- def forward(self, y_pred, y_true):
- pred_max = f.softmax(y_pred/self.M, dim=0) + 1e-9
- true_max = f.softmax(-y_true/self.M, dim=0) # need to reverse the sign
- pred_log = torch.log(pred_max)
- return torch.mean(-torch.sum(true_max*pred_log))
- class ListAllLoss(nn.Module):
- def __init__(self, M=0.5):
- super(ListAllLoss, self).__init__()
- self.M = M
- def forward(self, y_pred, y_label):
- pred_max = f.softmax(y_pred/self.M, dim=1) + 1e-9
- pred_log = torch.log(pred_max)
- return torch.mean(-torch.sum(y_label*pred_log))
- class LambdaLoss(nn.Module):
- def __init__(self, eps=1e-10, padded_value_indicator=-1, weighing_scheme=None, k=None, sigma=1.0, mu=10.0,
- reduction="mean", reduction_log="binary"):
- super(LambdaLoss, self).__init__()
- self.eps = eps
- self.padded_value_indicator = padded_value_indicator
- self.weighing_scheme = weighing_scheme
- self.k = k
- self.sigma = sigma
- self.mu = mu
- self.reduction = reduction
- self.reduction_log = reduction_log
- def forward(self, y_pred, y_true):
- device = y_pred.device
- y_pred = y_pred.clone()
- y_true = y_true.clone()
- k = self.k or y_true.shape[1]
- y_true = y_true.float()
- y_true = (y_true.max()-y_true) + y_true.min()
- padded_mask = y_true == self.padded_value_indicator
- y_pred[padded_mask] = float("-inf")
- y_true[padded_mask] = float("-inf")
- y_pred_sorted, indices_pred = y_pred.sort(descending=True, dim=-1)
- y_true_sorted, _ = y_true.sort(descending=True, dim=-1)
- true_sorted_by_preds = torch.gather(y_true, dim=1, index=indices_pred)
- true_diffs = true_sorted_by_preds[:, :, None] - true_sorted_by_preds[:, None, :]
- padded_pairs_mask = torch.isfinite(true_diffs)
- padded_pairs_mask = padded_pairs_mask & (true_diffs >= 0)
- ndcg_at_k_mask = torch.zeros((y_pred.shape[1], y_pred.shape[1]), dtype=torch.bool, device=device)
- if k is not None:
- ndcg_at_k_mask[:k, :k] = 1
- true_sorted_by_preds.clamp_(min=0.)
- y_true_sorted.clamp_(min=0.)
- pos_idxs = torch.arange(1, y_pred.shape[1] + 1).to(device)
- D = torch.log2(1. + pos_idxs.float())[None, :]
- maxDCGs = torch.sum(((torch.pow(2, y_true_sorted) - 1) / D)[:, :k], dim=-1).clamp(min=self.eps)
- G = (torch.pow(2, true_sorted_by_preds) - 1) / maxDCGs[:, None]
- if self.weighing_scheme is None:
- weights = 1.0
- else:
- weights = self.weighing_scheme(G, D, self.mu, true_sorted_by_preds)
- scores_diffs = (y_pred_sorted[:, :, None] - y_pred_sorted[:, None, :]).clamp(min=-1e8, max=1e8)
- scores_diffs.masked_fill(torch.isnan(scores_diffs), 0.0)
- weighted_probas = (torch.sigmoid(self.sigma * scores_diffs).clamp(min=self.eps) ** weights).clamp(min=self.eps)
- if self.reduction_log == "natural":
- losses = torch.log(weighted_probas)
- elif self.reduction_log == "binary":
- losses = torch.log2(weighted_probas)
- else:
- raise ValueError("Reduction logarithm base can be either natural or binary")
- if self.reduction == "sum":
- loss = -torch.sum(losses[padded_pairs_mask & ndcg_at_k_mask])
- elif self.reduction == "mean":
- loss = -torch.mean(losses[padded_pairs_mask & ndcg_at_k_mask])
- else:
- raise ValueError("Reduction method can be either sum or mean")
- return loss
- def lambdaRank_scheme(G, D, *args):
- return torch.abs(torch.pow(D[:, :, None], -1.) - torch.pow(D[:, None, :], -1.)) * torch.abs(G[:, :, None] - G[:, None, :])
- class ApproxNDCGLoss(nn.Module):
- def __init__(self, eps=1e-10, padded_value_indicator=-1, alpha=1.):
- """
- :param eps: epsilon value, used for numerical stability
- :param padded_value_indicator: an indicator of the y_true index containing a padded item, e.g. -1
- :param alpha: score difference weight used in the sigmoid function
- """
- super(ApproxNDCGLoss, self).__init__()
- self.eps = eps
- self.padded_value_indicator = padded_value_indicator
- self.alpha = alpha
- def forward(self, y_pred, y_true):
- """
- Loss based on approximate NDCG introduced in "A General Approximation Framework for Direct Optimization of
- Information Retrieval Measures". Please note that this method does not implement any kind of truncation.
- :param y_pred: predictions from the model, shape [batch_size, slate_length]
- :param y_true: ground truth labels, shape [batch_size, slate_length]
- :return: loss value, a torch.Tensor
- """
- device = y_pred.device
- y_pred = y_pred.clone()
- y_true = y_true.clone()
- y_true = y_true.float()
- y_true = (y_true.max()-y_true) + y_true.min()
- padded_mask = y_true == self.padded_value_indicator
- y_pred[padded_mask] = float("-inf")
- y_true[padded_mask] = float("-inf")
- # Here we sort the true and predicted relevancy scores.
- y_pred_sorted, indices_pred = y_pred.sort(descending=True, dim=-1)
- y_true_sorted, _ = y_true.sort(descending=True, dim=-1)
- # After sorting, we can mask out the pairs of indices (i, j) containing index of a padded element.
- true_sorted_by_preds = torch.gather(y_true, dim=1, index=indices_pred)
- true_diffs = true_sorted_by_preds[:, :, None] - true_sorted_by_preds[:, None, :]
- padded_pairs_mask = torch.isfinite(true_diffs)
- padded_pairs_mask.diagonal(dim1=-2, dim2=-1).zero_()
- # Here we clamp the -infs to get correct gains and ideal DCGs (maxDCGs)
- true_sorted_by_preds.clamp_(min=0.)
- y_true_sorted.clamp_(min=0.)
- # Here we find the gains, discounts and ideal DCGs per slate.
- pos_idxs = torch.arange(1, y_pred.shape[1] + 1).to(device)
- D = torch.log2(1. + pos_idxs.float())[None, :]
- maxDCGs = torch.sum((torch.pow(2, y_true_sorted) - 1) / D, dim=-1).clamp(min=self.eps)
- G = (torch.pow(2, true_sorted_by_preds) - 1) / maxDCGs[:, None]
- # Here we approximate the ranking positions according to Eqs 19-20 and later approximate NDCG (Eq 21)
- scores_diffs = (y_pred_sorted[:, :, None] - y_pred_sorted[:, None, :])
- scores_diffs[~padded_pairs_mask] = 0.
- approx_pos = 1. + torch.sum(padded_pairs_mask.float() * (torch.sigmoid(-self.alpha * scores_diffs).clamp(min=self.eps)), dim=-1)
- approx_D = torch.log2(1. + approx_pos)
- approx_NDCG = torch.sum((G / approx_D), dim=-1)
- return -torch.mean(approx_NDCG)
- class NeuralNDCG(nn.Module):
- def __init__(self, padded_value_indicator=-1, temperature=22.0, powered_relevancies=True, k=None,
- stochastic=False, n_samples=32, beta=0.1, log_scores=True):
- super(NeuralNDCG, self).__init__()
- self.padded_value_indicator = padded_value_indicator
- self.temperature = temperature
- self.powered_relevancies = powered_relevancies
- self.k = k
- self.stochastic = stochastic
- self.n_samples = n_samples
- self.beta = beta
- self.log_scores = log_scores
- def forward(self, y_pred, y_true):
- dev = y_pred.device
- y_true = y_true.float()
- y_true = (y_true.max()-y_true) + y_true.min()
- #scalling
- y_true = (y_true-y_true.min())/(y_true.max()-y_true.min())
- k = self.k or y_true.shape[1]
- mask = (y_true == self.padded_value_indicator)
- if self.stochastic:
- P_hat = stochastic_neural_sort(
- y_pred.unsqueeze(-1),
- n_samples=self.n_samples,
- tau=self.temperature,
- mask=mask,
- beta=self.beta,
- log_scores=self.log_scores,
- device=dev,
- )
- else:
- P_hat = deterministic_neural_sort(
- y_pred.unsqueeze(-1),
- tau=self.temperature,
- mask=mask,
- device=dev,
- ).unsqueeze(0)
- # Perform Sinkhorn scaling to obtain doubly stochastic permutation matrices
- P_hat = sinkhorn_scaling(
- P_hat.view(P_hat.shape[0] * P_hat.shape[1], P_hat.shape[2], P_hat.shape[3]),
- mask.repeat_interleave(P_hat.shape[0], dim=0),
- tol=1e-6, max_iter=50
- )
- P_hat = P_hat.view(
- int(P_hat.shape[0] / y_pred.shape[0]),
- y_pred.shape[0],
- P_hat.shape[1],
- P_hat.shape[2]
- )
- P_hat = P_hat.masked_fill(mask[None, :, :, None] | mask[None, :, None, :], 0.0)
- y_true_masked = y_true.masked_fill(mask, 0.0).unsqueeze(-1).unsqueeze(0)
- if self.powered_relevancies:
- y_true_masked = torch.pow(2.0, y_true_masked) - 1.0
- ground_truth = torch.matmul(P_hat, y_true_masked).squeeze(-1)
- discounts = (torch.tensor(1.0) / torch.log2(torch.arange(y_true.shape[-1], dtype=torch.float) + 2.0)).to(dev)
- discounted_gains = ground_truth * discounts
- if self.powered_relevancies:
- idcg = dcg(y_true, y_true, ats=[k]).permute(1, 0)
- else:
- idcg = dcg(y_true, y_true, ats=[k], gain_function=lambda x: x).permute(1, 0)
- discounted_gains = discounted_gains[:, :, :k]
- ndcg = discounted_gains.sum(dim=-1) / (idcg + 1e-10)
- idcg_mask = idcg == 0.0
- ndcg = ndcg.masked_fill(idcg_mask.repeat(ndcg.shape[0], 1), 0.0)
- assert (ndcg < 0.0).sum() == 0, "Every NDCG should be non-negative"
- if idcg_mask.all():
- return torch.tensor(0.0, device=dev)
- mean_ndcg = ndcg.sum() / ((~idcg_mask).sum() * ndcg.shape[0])
- return -1.0 * mean_ndcg
- def sinkhorn_scaling(mat, mask=None, tol=1e-6, max_iter=50):
- """
- Sinkhorn scaling procedure.
- :param mat: a tensor of square matrices of shape N x M x M, where N is batch size
- :param mask: a tensor of masks of shape N x M
- :param tol: Sinkhorn scaling tolerance
- :param max_iter: maximum number of iterations of the Sinkhorn scaling
- :return: a tensor of (approximately) doubly stochastic matrices
- """
- if mask is not None:
- mat = mat.masked_fill(mask[:, None, :] | mask[:, :, None], 0.0)
- mat = mat.masked_fill(mask[:, None, :] & mask[:, :, None], 1.0)
- for _ in range(max_iter):
- mat = mat / mat.sum(dim=1, keepdim=True).clamp(min=1e-10)
- mat = mat / mat.sum(dim=2, keepdim=True).clamp(min=1e-10)
- if torch.max(torch.abs(mat.sum(dim=2) - 1.)) < tol and torch.max(torch.abs(mat.sum(dim=1) - 1.)) < tol:
- break
- if mask is not None:
- mat = mat.masked_fill(mask[:, None, :] | mask[:, :, None], 0.0)
- return mat
- def deterministic_neural_sort(s, tau, mask, device="cuda"):
- """
- Deterministic neural sort.
- Code taken from "Stochastic Optimization of Sorting Networks via Continuous Relaxations", ICLR 2019.
- Minor modifications applied to the original code (masking).
- :param s: values to sort, shape [batch_size, slate_length]
- :param tau: temperature for the final softmax function
- :param mask: mask indicating padded elements
- :return: approximate permutation matrices of shape [batch_size, slate_length, slate_length]
- """
- n = s.size()[1]
- one = torch.ones((n, 1), dtype=torch.float32, device=device)
- s = s.masked_fill(mask[:, :, None], -1e8)
- A_s = torch.abs(s - s.permute(0, 2, 1))
- A_s = A_s.masked_fill(mask[:, :, None] | mask[:, None, :], 0.0)
- B = torch.matmul(A_s, torch.matmul(one, torch.transpose(one, 0, 1)))
- temp = [n - m + 1 - 2 * (torch.arange(n - m, device=device) + 1) for m in mask.squeeze(-1).sum(dim=1)]
- temp = [t.type(torch.float32) for t in temp]
- temp = [torch.cat((t, torch.zeros(n - len(t), device=device))) for t in temp]
- scaling = torch.stack(temp).type(torch.float32).to(device) # type: ignore
- s = s.masked_fill(mask[:, :, None], 0.0)
- C = torch.matmul(s, scaling.unsqueeze(-2))
- P_max = (C - B).permute(0, 2, 1)
- P_max = P_max.masked_fill(mask[:, :, None] | mask[:, None, :], -np.inf)
- P_max = P_max.masked_fill(mask[:, :, None] & mask[:, None, :], 1.0)
- sm = torch.nn.Softmax(-1)
- P_hat = sm(P_max / tau)
- return P_hat
- def stochastic_neural_sort(s, n_samples, tau, mask, beta=1.0, log_scores=True, eps=1e-10, device="cuda"):
- """
- Stochastic neural sort. Please note that memory complexity grows by factor n_samples.
- Code taken from "Stochastic Optimization of Sorting Networks via Continuous Relaxations", ICLR 2019.
- Minor modifications applied to the original code (masking).
- :param s: values to sort, shape [batch_size, slate_length]
- :param n_samples: number of samples (approximations) for each permutation matrix
- :param tau: temperature for the final softmax function
- :param mask: mask indicating padded elements
- :param beta: scale parameter for the Gumbel distribution
- :param log_scores: whether to apply the logarithm function to scores prior to Gumbel perturbation
- :param eps: epsilon for the logarithm function
- :return: approximate permutation matrices of shape [n_samples, batch_size, slate_length, slate_length]
- """
- batch_size = s.size()[0]
- n = s.size()[1]
- s_positive = s + torch.abs(s.min())
- samples = beta * sample_gumbel([n_samples, batch_size, n, 1], device=device)
- if log_scores:
- s_positive = torch.log(s_positive + eps)
- s_perturb = (s_positive + samples).view(n_samples * batch_size, n, 1)
- mask_repeated = mask.repeat_interleave(n_samples, dim=0)
- P_hat = deterministic_neural_sort(s_perturb, tau, mask_repeated)
- P_hat = P_hat.view(n_samples, batch_size, n, n)
- return P_hat
- def dcg(y_pred, y_true, ats=None, gain_function=lambda x: torch.pow(2, x) - 1, padding_indicator=-1):
- """
- Discounted Cumulative Gain at k.
- Compute DCG at ranks given by ats or at the maximum rank if ats is None.
- :param y_pred: predictions from the model, shape [batch_size, slate_length]
- :param y_true: ground truth labels, shape [batch_size, slate_length]
- :param ats: optional list of ranks for DCG evaluation, if None, maximum rank is used
- :param gain_function: callable, gain function for the ground truth labels, e.g. torch.pow(2, x) - 1
- :param padding_indicator: an indicator of the y_true index containing a padded item, e.g. -1
- :return: DCG values for each slate and evaluation position, shape [batch_size, len(ats)]
- """
- y_true = y_true.clone()
- y_pred = y_pred.clone()
- actual_length = y_true.shape[1]
- if ats is None:
- ats = [actual_length]
- ats = [min(at, actual_length) for at in ats]
- true_sorted_by_preds = __apply_mask_and_get_true_sorted_by_preds(y_pred, y_true, padding_indicator)
- discounts = (torch.tensor(1) / torch.log2(torch.arange(true_sorted_by_preds.shape[1], dtype=torch.float) + 2.0)).to(
- device=true_sorted_by_preds.device)
- gains = gain_function(true_sorted_by_preds)
- discounted_gains = (gains * discounts)[:, :np.max(ats)]
- cum_dcg = torch.cumsum(discounted_gains, dim=1)
- ats_tensor = torch.tensor(ats, dtype=torch.long) - torch.tensor(1)
- dcg = cum_dcg[:, ats_tensor]
- return dcg
- def __apply_mask_and_get_true_sorted_by_preds(y_pred, y_true, padding_indicator=-1):
- mask = y_true == padding_indicator
- y_pred[mask] = float('-inf')
- y_true[mask] = 0.0
- _, indices = y_pred.sort(descending=True, dim=-1)
- return torch.gather(y_true, dim=1, index=indices)
loss.py at commit 35b0c4b, no license · at the source
Overview
- Department of Pharmaceutics, Faculty of Pharmacy, Tehran University of Medical Sciences, Tehran, Iran
- Department of Computer Engineering, Sharif University of Technology, Tehran, Iran
- Department of Computer Engineering, Amirkabir University of Technology, Tehran, Iran
- Mosaheb Institute for Mathematical Research, Kharazmi University, Tehran, Iran
- Nanotechnology Research Centre, Faculty of Pharmacy, Tehran University of Medical Sciences, Tehran, Iran
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
35b0c4b4df6d2a2148f35ac9ce6797300549de26, 8 April 2025Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
49 files
- data/
scripts/ , Python, 59 linescell_similarity.py - data/
scripts/ , Python, 121 linescmp_similarity.py - data/
scripts/ , Shell, 22 linescommands_ctrpv2.sh - data/
scripts/ , Shell, 20 linescommands_prism.sh - data/
scripts/ , Python, 118 linescreate_cv.py - data/
scripts/ , Python, 83 linescreate_filtered_ccle_dru g.py - data/
scripts/ , Python, 99 linescreate_setup_LCO.py - data/
scripts/ , Python, 121 linescreate_setup_LRO.py - data/
scripts/ , Python, 52 lineslasso.py - data/
scripts/ , Python, 34 linesoptimal_perf.py - netprop/
netprop_ctrp.py , Python, 110 lines - netprop/
netprop_prism.py , Python, 113 lines - scripts/
get_results.sh , Shell, 51 lines - scripts/
multi-processing-run-all , Shell, 56 lines.sh - scripts/
run_ae.sh , Shell, 34 lines - scripts/
run_all.sh , Shell, 64 lines - scripts/
run_all_copy.sh , Shell, 63 lines - scripts/
run_inference.sh , Shell, 67 lines - scripts/
run_listall.sh , Shell, 29 lines - scripts/
run_listone.sh , Shell, 26 lines - scripts/
run_pairpushc.sh , Shell, 29 lines - scripts/
run_shap_listwise_explai , Shell, 49 linesner.sh - shapley/
replace_names.py , Python, 14 lines - shapley/
shapley_plot.ipynb , Jupyter, 92 lines - src/
cross_validate.py , Python, 453 lines, 2 matches - src/
dataloader/ , Python, 2 lines__init__.py - src/
dataloader/ , Python, 164 linesloader.py - src/
dataloader/ , Python, 254 linesutils.py - src/
eval_ae.py , Python, 120 lines - src/
eval_fenc.py , Python, 83 lines - src/
features/ , Python, 247 linesfeatures_generators.py - src/
features/ , Python, 346 linesfeaturization.py - src/
gene_enrichement_analysi , Python, 9 liness.py - src/
get_results.py , Python, 104 lines - src/
inference.py , Python, 327 lines - src/
log_parser.py , Python, 59 lines - src/
models/ , Python, 44 linesae.py - src/
models/ , Python, 411 lines, 3 matchesloss.py - src/
models/ , Python, 229 linesmpn.py - src/
models/ , Python, 450 linesranknet.py - src/
shapley_listwise.py , Python, 183 lines, 1 match - src/
train_ae.py , Python, 174 lines - src/
training/ , Python, 59 lineseval.py - src/
training/ , Python, 184 linestrain.py - src/
utils/ , Python, 141 linesargs.py - src/
utils/ , Python, 299 linescommon.py - src/
utils/ , Python, 271 linesmetrics.py - src/
utils/ , Python, 186 linesnn_utils.py - README.md, Text, 65 lines
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://
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://
BibTeX
@article{sarmeili2026she
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/
url = {https://
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/
VL - 21
IS - 8
SP - e0345854
SN - 1932-6203
PB - PLOS
DO - 10.1371/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1371/
"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":
"volume": "21",
"issue": "8",
"page": "e0345854",
"DOI": "10.1371/
"PMID": "42594139",
"PMCID": "PMC13472410",
"ISSN": "1932-6203",
"publisher": "PLOS",
"URL": "https://
"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 journalIn common: RDKit, PyTorch Geometric, SHAP, 5 other tools, 1 reference
- [2] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn 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 researchIn common: RDKit, PyTorch Geometric, NetworkX, 7 other tools
- [4] doi:10.1038/s41586-026-10670-w [code]
- Zero-shot design of drug-binding proteins via neural iterative selection-expansion.Journal: NatureIn common: RDKit, PyTorch Geometric, PyTorch, 6 other tools
- [5] 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
- [6] 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 psychiatryIn common: PyTorch Geometric, NetworkX, PyTorch, 6 other tools, other condition
- [7] doi:10.1038/s41467-026-71759-4 [code]
- CellNiche represents cellular microenvironments in atlas-scale spatial omics data with contrastive learning.Journal: Nature communicationsIn common: PyTorch Geometric, NetworkX, PyTorch, 6 other tools, other condition
- [8] 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: eNeuroIn common: PyTorch Geometric, SHAP, PyTorch, 6 other tools - [9] doi:10.1038/s41598-026-53415-5 [code]
- Computational design and immunoinformatics validation of a T cell multi-epitope vaccine targeting glioblastoma stem cells.Journal: Scientific reportsIn common: RDKit, PyTorch Geometric, PyTorch, 5 other tools, other condition
- [10] doi:10.1186/s13321-026-01177-7 [code]
- A pipeline for developing AI-driven models to predict molecular initiating events: a case study on neural tube defects.Journal: Journal of cheminformaticsIn common: RDKit, NetworkX, 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 48 scripts, and 6 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:9764010d59843553…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
