OSCR

High-fidelity bidirectional translation between single-cell transcriptomes and DNA methylomes with scBOND.

Code ↔ Paper

9 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 9 matches
  1. [1] § Methods › Basic architecture of scBOND › The encoders in scBOND ↔ sccross/models/layers.py, lines 24–142 · score 0.77 · neural network, negative slope, LeakyReLU, dropout rate, layers, module
  2. [2] § Methods › Basic architecture of scBOND › The translator in scBOND ↔ scBond/model_component.py, lines 525–641 · score 0.75 · ReLU, gating network, expert networks, Softmax, probability, activation
  3. [3] § Results › scBOND preserves cellular heterogeneity and improves the discrimination of similar cell types in early embryonic development ↔ scBond/calculate_cluster.py, lines 6–42 · score 0.73 · adjusted mutual information, normalized mutual information, adjusted Rand, homogeneity, AMI, NMI
  4. [4] § Results › scBOND preserves cellular heterogeneity and improves the discrimination of similar cell types in early embryonic development ↔ scBond/calculate_cluster.py, lines 6–42 · score 0.73 · adjusted mutual information, normalized mutual information, adjusted Rand, homogeneity, AMI, NMI
  5. [5] § Methods › The training procedure of scBOND ↔ scBond/bond.py, lines 428–577 · score 0.63 · KL divergence, loss function, lr, patience, discriminators, reconstruction
  6. [6] § Methods › Data collection and preprocessing ↔ scBond/bond.py, lines 131–214 · score 0.58 · highly variable genes, HVGs, imputation, median, min, preprocessing
  7. [7] § Methods › Basic architecture of scBOND › The translator in scBOND ↔ scBond/model_component.py, lines 525–641 · score 0.55 · gating network, expert networks, space, block, weights, latent
  8. [8] § Methods › Data collection and preprocessing ↔ scBond/data_processing.py, lines 205–297 · score 0.53 · min max, imputation, median, preprocessing, methylation, cell
  9. [9] § Methods › The training procedure of scBOND ↔ scBond/train_model.py, lines 607–666 · score 0.51 · reconstruction loss, Adam, KL, patience, optimize, discriminators

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 · 830 lines · 31 KB · MIT · 2 matches

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. class NetBlock(nn.Module):
  5. def __init__(
  6. self,
  7. nlayer: int,
  8. dim_list: list,
  9. act_list: list,
  10. dropout_rate: float,
  11. noise_rate: float
  12. ):
  13. """
  14. multiple layers netblock with specific layer counts, dimension, activations and dropout.
  15. Parameters
  16. ----------
  17. nlayer
  18. layer counts.
  19. dim_list
  20. dimension list, length equal to nlayer + 1.
  21. act_list
  22. activation list, length equal to nlayer + 1.
  23. dropout_rate
  24. rate of dropout.
  25. noise_rate
  26. rate of set part of input data to 0.
  27. """
  28. super(NetBlock, self).__init__()
  29. self.nlayer = nlayer
  30. self.noise_dropout = nn.Dropout(noise_rate)
  31. self.linear_list = nn.ModuleList()
  32. self.bn_list = nn.ModuleList()
  33. self.activation_list = nn.ModuleList()
  34. self.dropout_list = nn.ModuleList()
  35. for i in range(nlayer):
  36. self.linear_list.append(nn.Linear(dim_list[i], dim_list[i + 1]))
  37. nn.init.xavier_uniform_(self.linear_list[i].weight)
  38. self.bn_list.append(nn.BatchNorm1d(dim_list[i + 1]))
  39. self.activation_list.append(act_list[i])
  40. if not i == nlayer -1:
  41. self.dropout_list.append(nn.Dropout(dropout_rate))
  42. def forward(self, x):
  43. x = self.noise_dropout(x)
  44. for i in range(self.nlayer):
  45. x = self.linear_list[i](x)
  46. x = self.bn_list[i](x)
  47. x = self.activation_list[i](x)
  48. if not i == self.nlayer -1:
  49. """ don't use dropout for output to avoid loss calculate break down """
  50. x = self.dropout_list[i](x)
  51. return x
  52. class Split_Chrom_Encoder_block(nn.Module):
  53. def __init__(
  54. self,
  55. nlayer: int,
  56. dim_list: list,
  57. act_list: list,
  58. chrom_list: list,
  59. dropout_rate: float,
  60. noise_rate: float
  61. ):
  62. """
  63. MET encoder netblock with specific layer counts, dimension, activations and dropout.
  64. Parameters
  65. ----------
  66. nlayer
  67. layer counts.
  68. dim_list
  69. dimension list, length equal to nlayer + 1.
  70. act_list
  71. activation list, length equal to nlayer + 1.
  72. chrom_list
  73. list record the peaks count for each chrom, assert that sum of chrom list equal to dim_list[0].
  74. dropout_rate
  75. rate of dropout.
  76. noise_rate
  77. rate of set part of input data to 0.
  78. """
  79. super(Split_Chrom_Encoder_block, self).__init__()
  80. self.nlayer = nlayer
  81. self.chrom_list = chrom_list
  82. self.noise_dropout = nn.Dropout(noise_rate)
  83. self.linear_list = nn.ModuleList()
  84. self.bn_list = nn.ModuleList()
  85. self.activation_list = nn.ModuleList()
  86. self.dropout_list = nn.ModuleList()
  87. for i in range(nlayer):
  88. if i == 0:
  89. """first layer seperately forward for each chrom"""
  90. self.linear_list.append(nn.ModuleList())
  91. self.bn_list.append(nn.ModuleList())
  92. self.activation_list.append(nn.ModuleList())
  93. self.dropout_list.append(nn.ModuleList())
  94. for j in range(len(chrom_list)):
  95. self.linear_list[i].append(nn.Linear(chrom_list[j], dim_list[i + 1] // len(chrom_list)))
  96. nn.init.xavier_uniform_(self.linear_list[i][j].weight)
  97. self.bn_list[i].append(nn.BatchNorm1d(dim_list[i + 1] // len(chrom_list)))
  98. self.activation_list[i].append(act_list[i])
  99. self.dropout_list[i].append(nn.Dropout(dropout_rate))
  100. else:
  101. self.linear_list.append(nn.Linear(dim_list[i], dim_list[i + 1]))
  102. nn.init.xavier_uniform_(self.linear_list[i].weight)
  103. self.bn_list.append(nn.BatchNorm1d(dim_list[i + 1]))
  104. self.activation_list.append(act_list[i])
  105. if not i == nlayer -1:
  106. self.dropout_list.append(nn.Dropout(dropout_rate))
  107. def forward(self, x):
  108. x = self.noise_dropout(x)
  109. for i in range(self.nlayer):
  110. if i == 0:
  111. x = torch.split(x, self.chrom_list, dim = 1)
  112. temp = []
  113. for j in range(len(self.chrom_list)):
  114. temp.append(self.dropout_list[0][j](self.activation_list[0][j](self.bn_list[0][j](self.linear_list[0][j](x[j])))))
  115. x = torch.concat(temp, dim = 1)
  116. else:
  117. x = self.linear_list[i](x)
  118. x = self.bn_list[i](x)
  119. x = self.activation_list[i](x)
  120. if not i == self.nlayer -1:
  121. """ don't use dropout for output to avoid loss calculate break down """
  122. x = self.dropout_list[i](x)
  123. return x
  124. class Split_Chrom_Decoder_block(nn.Module):
  125. def __init__(
  126. self,
  127. nlayer: int,
  128. dim_list: list,
  129. act_list: list,
  130. chrom_list: list,
  131. dropout_rate: float,
  132. noise_rate: float
  133. ):
  134. """
  135. MET decoder netblock with specific layer counts, dimension, activations and dropout.
  136. Parameters
  137. ----------
  138. nlayer
  139. layer counts.
  140. dim_list
  141. dimension list, length equal to nlayer + 1.
  142. act_list
  143. activation list, length equal to nlayer + 1.
  144. chrom_list
  145. list record the peaks count for each chrom, assert that sum of chrom list equal to dim_list[end].
  146. dropout_rate
  147. rate of dropout.
  148. noise_rate
  149. rate of set part of input data to 0.
  150. """
  151. super(Split_Chrom_Decoder_block, self).__init__()
  152. self.nlayer = nlayer
  153. self.noise_dropout = nn.Dropout(noise_rate)
  154. self.chrom_list = chrom_list
  155. self.linear_list = nn.ModuleList()
  156. self.bn_list = nn.ModuleList()
  157. self.activation_list = nn.ModuleList()
  158. self.dropout_list = nn.ModuleList()
  159. for i in range(nlayer):
  160. if not i == nlayer -1:
  161. self.linear_list.append(nn.Linear(dim_list[i], dim_list[i + 1]))
  162. nn.init.xavier_uniform_(self.linear_list[i].weight)
  163. self.bn_list.append(nn.BatchNorm1d(dim_list[i + 1]))
  164. self.activation_list.append(act_list[i])
  165. self.dropout_list.append(nn.Dropout(dropout_rate))
  166. else:
  167. """last layer seperately forward for each chrom"""
  168. self.linear_list.append(nn.ModuleList())
  169. self.bn_list.append(nn.ModuleList())
  170. self.activation_list.append(nn.ModuleList())
  171. self.dropout_list.append(nn.ModuleList())
  172. for j in range(len(chrom_list)):
  173. self.linear_list[i].append(nn.Linear(dim_list[i] // len(chrom_list), chrom_list[j]))
  174. nn.init.xavier_uniform_(self.linear_list[i][j].weight)
  175. self.bn_list[i].append(nn.BatchNorm1d(chrom_list[j]))
  176. self.activation_list[i].append(act_list[i])
  177. def forward(self, x):
  178. x = self.noise_dropout(x)
  179. for i in range(self.nlayer):
  180. if not i == self.nlayer -1:
  181. x = self.linear_list[i](x)
  182. x = self.bn_list[i](x)
  183. x = self.activation_list[i](x)
  184. x = self.dropout_list[i](x)
  185. else:
  186. x = torch.chunk(x, len(self.chrom_list), dim = 1)
  187. temp = []
  188. for j in range(len(self.chrom_list)):
  189. temp.append(self.activation_list[i][j](self.bn_list[i][j](self.linear_list[i][j](x[j]))))
  190. x = torch.concat(temp, dim = 1)
  191. return x
  192. class SELayer(nn.Module):
  193. def __init__(self, channel, reduction=16):
  194. super(SELayer, self).__init__()
  195. self.fc = nn.Sequential(
  196. nn.Linear(channel, channel // reduction, bias=False),
  197. nn.ReLU(inplace=True),
  198. nn.Linear(channel // reduction, channel, bias=False),
  199. nn.Sigmoid()
  200. )
  201. def forward(self, x):
  202. b, c = x.size()
  203. y = self.fc(x)
  204. return x * y.view(b, c)
  205. class SelfAttention(nn.Module):
  206. def __init__(
  207. self,
  208. dim,
  209. num_heads=8,
  210. qkv_bias=False,
  211. attn_drop=0.,
  212. proj_drop=0.
  213. ):
  214. """
  215. Multi-head self-attention module that computes attention between input and context tensors.
  216. Parameters
  217. ----------
  218. dim: int
  219. Dimension of the input features and embedding.
  220. num_heads: int
  221. Number of parallel attention heads, default 8.
  222. qkv_bias: bool
  223. Whether to include bias in the query, key, and value linear projections, default False.
  224. attn_drop: float
  225. Dropout probability applied to attention weights, default 0.0.
  226. proj_drop: float
  227. Dropout probability applied to the output projection, default 0.0.
  228. """
  229. super().__init__()
  230. self.num_heads = num_heads
  231. self.scale = (dim // num_heads) ** -0.5
  232. self.q = nn.Linear(dim, dim, bias=qkv_bias)
  233. self.k = nn.Linear(dim, dim, bias=qkv_bias)
  234. self.v = nn.Linear(dim, dim, bias=qkv_bias)
  235. self.attn_drop = nn.Dropout(attn_drop)
  236. self.proj = nn.Linear(dim, dim)
  237. self.proj_drop = nn.Dropout(proj_drop)
  238. def forward(self, x, context):
  239. B, N, C = x.shape
  240. q = self.q(x).reshape(B, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
  241. k = self.k(context).reshape(B, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
  242. v = self.v(context).reshape(B, N, self.num_heads, C // self.num_heads).permute(0, 2, 1, 3)
  243. attn = (q @ k.transpose(-2, -1)) * self.scale
  244. attn = attn.softmax(dim=-1)
  245. attn = self.attn_drop(attn)
  246. x = (attn @ v).transpose(1, 2).reshape(B, N, C)
  247. x = self.proj(x)
  248. x = self.proj_drop(x)
  249. return x
  250. class Translator(nn.Module):
  251. def __init__(
  252. self,
  253. translator_input_dim_r: int,
  254. translator_input_dim_m: int,
  255. translator_embed_dim: int,
  256. translator_embed_act_list: list,
  257. num_experts: int = 4,
  258. reduction_ratio: int = 64,
  259. num_heads=8,
  260. attn_drop=0.1,
  261. proj_drop=0.1
  262. ):
  263. """
  264. Translator block with MoE mechanism used for translation between different omics.
  265. Parameters
  266. ----------
  267. translator_input_dim_r
  268. dimension of input from RNA encoder for translator.
  269. translator_input_dim_a
  270. dimension of input from MET encoder for translator.
  271. translator_embed_dim
  272. dimension of embedding space for translator.
  273. translator_embed_act_list
  274. activation list for translator, involving [mean_activation, log_var_activation, decoder_activation].
  275. num_experts
  276. number of expert networks.
  277. reduction_ratio
  278. The reduction ratio for the SE layer.
  279. num_heads: int
  280. Number of parallel attention heads, default 8.
  281. attn_drop: float
  282. Dropout probability applied to attention weights, default 0.0.
  283. proj_drop: float
  284. Dropout probability applied to the output projection, default 0.0.
  285. """
  286. super(Translator, self).__init__()
  287. mean_activation, log_var_activation, decoder_activation = translator_embed_act_list
  288. self.expert_networks_r_mu = nn.ModuleList([
  289. nn.Sequential(
  290. nn.Linear(translator_input_dim_r, translator_embed_dim),
  291. nn.BatchNorm1d(translator_embed_dim),
  292. mean_activation
  293. ) for _ in range(num_experts)
  294. ])
  295. self.expert_networks_m_mu = nn.ModuleList([
  296. nn.Sequential(
  297. nn.Linear(translator_input_dim_m, translator_embed_dim),
  298. nn.BatchNorm1d(translator_embed_dim),
  299. mean_activation
  300. ) for _ in range(num_experts)
  301. ])
  302. self.expert_networks_r_d = nn.ModuleList([
  303. nn.Sequential(
  304. nn.Linear(translator_input_dim_r, translator_embed_dim),
  305. nn.BatchNorm1d(translator_embed_dim),
  306. log_var_activation
  307. ) for _ in range(num_experts)
  308. ])
  309. self.expert_networks_m_d = nn.ModuleList([
  310. nn.Sequential(
  311. nn.Linear(translator_input_dim_m, translator_embed_dim),
  312. nn.BatchNorm1d(translator_embed_dim),
  313. log_var_activation
  314. ) for _ in range(num_experts)
  315. ])
  316. self.gating_network_r_mu = nn.Sequential(
  317. nn.Linear(translator_input_dim_r, 64),
  318. nn.ReLU(),
  319. nn.Linear(64, num_experts),
  320. nn.Softmax(dim=1)
  321. )
  322. self.gating_network_m_mu = nn.Sequential(
  323. nn.Linear(translator_input_dim_m, 64),
  324. nn.ReLU(),
  325. nn.Linear(64, num_experts),
  326. nn.Softmax(dim=1)
  327. )
  328. self.gating_network_r_d = nn.Sequential(
  329. nn.Linear(translator_input_dim_r, 64),
  330. nn.ReLU(),
  331. nn.Linear(64, num_experts),
  332. nn.Softmax(dim=1)
  333. )
  334. self.gating_network_m_d = nn.Sequential(
  335. nn.Linear(translator_input_dim_m, 64),
  336. nn.ReLU(),
  337. nn.Linear(64, num_experts),
  338. nn.Softmax(dim=1)
  339. )
  340. self.RNA_decoder_l = nn.Linear(translator_embed_dim, translator_input_dim_r)
  341. nn.init.xavier_uniform_(self.RNA_decoder_l.weight)
  342. self.RNA_decoder_bn = nn.BatchNorm1d(translator_input_dim_r)
  343. self.RNA_decoder_act = decoder_activation
  344. self.MET_decoder_l = nn.Linear(translator_embed_dim, translator_input_dim_m)
  345. nn.init.xavier_uniform_(self.MET_decoder_l.weight)
  346. self.MET_decoder_bn = nn.BatchNorm1d(translator_input_dim_m)
  347. self.MET_decoder_act = decoder_activation
  348. self.se_rna = SELayer(translator_embed_dim, reduction_ratio)
  349. self.se_met = SELayer(translator_embed_dim, reduction_ratio)
  350. self.self_attn_r2m = SelfAttention(
  351. dim=translator_embed_dim,
  352. num_heads=num_heads,
  353. attn_drop=attn_drop,
  354. proj_drop=proj_drop
  355. )
  356. self.self_attn_m2r = SelfAttention(
  357. dim=translator_embed_dim,
  358. num_heads=num_heads,
  359. attn_drop=attn_drop,
  360. proj_drop=proj_drop
  361. )
  362. def reparameterize(self, mu, sigma):
  363. sigma = torch.exp(sigma / 2)
  364. eps = torch.randn_like(sigma)
  365. return mu + eps * sigma
  366. def forward_with_RNA(self, x, forward_type):
  367. expert_outputs_r_mu = [expert_network_mu(x) for expert_network_mu in self.expert_networks_r_mu]
  368. gating_weights_r_mu = self.gating_network_r_mu(x)
  369. gating_weights_r_mu = gating_weights_r_mu.unsqueeze(2)
  370. expert_outputs_r_mu = torch.stack(expert_outputs_r_mu, dim=1)
  371. weighted_output_r_mu = (expert_outputs_r_mu * gating_weights_r_mu).sum(dim=1)
  372. expert_outputs_r_d = [expert_network_d(x) for expert_network_d in self.expert_networks_r_d]
  373. gating_weights_r_d = self.gating_network_r_d(x)
  374. gating_weights_r_d = gating_weights_r_d.unsqueeze(2)
  375. expert_outputs_r_d = torch.stack(expert_outputs_r_d, dim=1)
  376. weighted_output_r_d = (expert_outputs_r_d * gating_weights_r_d).sum(dim=1)
  377. if forward_type == 'test':
  378. latent_layer = weighted_output_r_mu
  379. elif forward_type == 'train':
  380. latent_layer = self.reparameterize(weighted_output_r_mu, weighted_output_r_d)
  381. latent_layer = self.se_rna(latent_layer)
  382. latent_layer_R_out = self.RNA_decoder_act(self.RNA_decoder_bn(self.RNA_decoder_l(latent_layer)))
  383. latent_layer_M_out = self.MET_decoder_act(self.MET_decoder_bn(self.MET_decoder_l(latent_layer)))
  384. return latent_layer_R_out, latent_layer_M_out, weighted_output_r_mu, weighted_output_r_d
  385. def forward_with_MET(self, x, forward_type):
  386. expert_outputs_m_mu = [expert_network_mu(x) for expert_network_mu in self.expert_networks_m_mu]
  387. gating_weights_m_mu = self.gating_network_m_mu(x)
  388. gating_weights_m_mu = gating_weights_m_mu.unsqueeze(2)
  389. expert_outputs_m_mu = torch.stack(expert_outputs_m_mu, dim=1)
  390. weighted_output_m_mu = (expert_outputs_m_mu * gating_weights_m_mu).sum(dim=1)
  391. expert_outputs_m_d = [expert_network_d(x) for expert_network_d in self.expert_networks_m_d]
  392. gating_weights_m_d = self.gating_network_m_d(x)
  393. gating_weights_m_d = gating_weights_m_d.unsqueeze(2)
  394. expert_outputs_m_d = torch.stack(expert_outputs_m_d, dim=1)
  395. weighted_output_m_d = (expert_outputs_m_d * gating_weights_m_d).sum(dim=1)
  396. x = x.unsqueeze(1)
  397. weighted_output_m_mu = weighted_output_m_mu.unsqueeze(1)
  398. self_attn_output = self.self_attn_m2r(x, x)
  399. weighted_output_m_mu = weighted_output_m_mu + self_attn_output # 残差连接
  400. weighted_output_m_mu = weighted_output_m_mu.squeeze(1)
  401. weighted_output_m_d = weighted_output_m_d.squeeze(1)
  402. if forward_type == 'test':
  403. latent_layer = weighted_output_m_mu
  404. elif forward_type == 'train':
  405. latent_layer = self.reparameterize(weighted_output_m_mu, weighted_output_m_d)
  406. latent_layer = self.se_met(latent_layer)
  407. latent_layer_R_out = self.RNA_decoder_act(self.RNA_decoder_bn(self.RNA_decoder_l(latent_layer)))
  408. latent_layer_M_out = self.MET_decoder_act(self.MET_decoder_bn(self.MET_decoder_l(latent_layer)))
  409. return latent_layer_R_out, latent_layer_M_out, weighted_output_m_mu, weighted_output_m_d
  410. def train_model(self, x, input_type):
  411. if input_type == 'RNA':
  412. return self.forward_with_RNA(x, 'train')
  413. elif input_type == 'MET':
  414. return self.forward_with_MET(x, 'train')
  415. def test_model(self, x, input_type):
  416. if input_type == 'RNA':
  417. return self.forward_with_RNA(x, 'test')
  418. elif input_type == 'MET':
  419. return self.forward_with_MET(x, 'test')
  420. class Single_Translator(nn.Module):
  421. def __init__(
  422. self,
  423. translator_input_dim: int,
  424. translator_embed_dim: int,
  425. translator_embed_act_list: list,
  426. num_experts_single: int = 6,
  427. reduction_ratio: int = 64,
  428. num_heads: int = 8,
  429. attn_drop: float = 0.1,
  430. proj_drop: float = 0.1
  431. ):
  432. """
  433. Single translator block with MoE mechanism used only for pretraining.
  434. Parameters
  435. ----------
  436. translator_input_dim
  437. dimension of input from encoder for translator.
  438. translator_embed_dim
  439. dimension of embedding space for translator.
  440. translator_embed_act_list
  441. activation list for translator, involving [mean_activation, log_var_activation, decoder_activation].
  442. num_experts_single
  443. number of expert networks, default 6.
  444. reduction_ratio
  445. The reduction ratio for the SE layer, default 64.
  446. num_heads: int
  447. Number of parallel attention heads, default 8.
  448. attn_drop: float
  449. Dropout probability applied to attention weights, default 0.0.
  450. proj_drop: float
  451. Dropout probability applied to the output projection, default 0.0.
  452. """
  453. super(Single_Translator, self).__init__()
  454. mean_activation, log_var_activation, decoder_activation = translator_embed_act_list
  455. self.expert_networks_mu = nn.ModuleList([
  456. nn.Sequential(
  457. nn.Linear(translator_input_dim, translator_embed_dim),
  458. nn.BatchNorm1d(translator_embed_dim),
  459. mean_activation
  460. ) for _ in range(num_experts_single)
  461. ])
  462. self.expert_networks_d = nn.ModuleList([
  463. nn.Sequential(
  464. nn.Linear(translator_input_dim, translator_embed_dim),
  465. nn.BatchNorm1d(translator_embed_dim),
  466. log_var_activation
  467. ) for _ in range(num_experts_single)
  468. ])
  469. self.gating_network = nn.Sequential(
  470. nn.Linear(translator_input_dim, 64),
  471. nn.ReLU(),
  472. nn.Linear(64, num_experts_single),
  473. nn.Softmax(dim=1)
  474. )
  475. self.decoder_l = nn.Linear(translator_embed_dim, translator_input_dim)
  476. nn.init.xavier_uniform_(self.decoder_l.weight)
  477. self.decoder_bn = nn.BatchNorm1d(translator_input_dim)
  478. self.decoder_act = decoder_activation
  479. self.se = SELayer(translator_embed_dim, reduction_ratio)
  480. self.self_attn = SelfAttention(
  481. dim=translator_embed_dim,
  482. num_heads=8,
  483. attn_drop=0.1,
  484. proj_drop=0.1
  485. )
  486. def reparameterize(self, mu, sigma):
  487. sigma = torch.exp(sigma / 2)
  488. eps = torch.randn_like(sigma)
  489. return mu + eps * sigma
  490. def forward(self, x, forward_type):
  491. expert_outputs_mu = [expert_network(x) for expert_network in self.expert_networks_mu]
  492. expert_outputs_d = [expert_network(x) for expert_network in self.expert_networks_d]
  493. gating_weights = self.gating_network(x)
  494. gating_weights = gating_weights.unsqueeze(2)
  495. expert_outputs_mu = torch.stack(expert_outputs_mu, dim=1)
  496. expert_outputs_d = torch.stack(expert_outputs_d, dim=1)
  497. weighted_output_mu = (expert_outputs_mu * gating_weights).sum(dim=1)
  498. weighted_output_d = (expert_outputs_d * gating_weights).sum(dim=1)
  499. weighted_output_mu = weighted_output_mu.unsqueeze(1)
  500. attn_output = self.self_attn(weighted_output_mu, weighted_output_mu)
  501. weighted_output_mu = weighted_output_mu + attn_output
  502. weighted_output_mu = weighted_output_mu.squeeze(1)
  503. if forward_type == 'test':
  504. latent_layer = weighted_output_mu
  505. elif forward_type == 'train':
  506. latent_layer = self.reparameterize(weighted_output_mu, weighted_output_d)
  507. latent_layer = self.se(latent_layer)
  508. latent_layer_out = self.decoder_act(self.decoder_bn(self.decoder_l(latent_layer)))
  509. return latent_layer_out, weighted_output_mu, weighted_output_d
  510. class Background_Translator(nn.Module):
  511. def __init__(
  512. self,
  513. translator_input_dim_r: int,
  514. translator_input_dim_m: int,
  515. translator_embed_dim: int,
  516. translator_embed_act_list: list,
  517. reduction_ratio: int = 64
  518. ):
  519. """
  520. Background Translator block with SE layer.
  521. Parameters
  522. ----------
  523. translator_input_dim_r
  524. dimension of input from RNA encoder for translator.
  525. translator_input_dim_m
  526. dimension of input from MET encoder for translator.
  527. translator_embed_dim
  528. dimension of embedding space for translator.
  529. translator_embed_act_list
  530. activation list for translator, involving [mean_activation, log_var_activation, decoder_activation].
  531. reduction_ratio
  532. The reduction ratio for the SE layer, default 64.
  533. """
  534. super(Background_Translator, self).__init__()
  535. mean_activation, log_var_activation, decoder_activation = translator_embed_act_list
  536. self.RNA_encoder_l_mu = nn.Linear(translator_input_dim_r, translator_embed_dim)
  537. nn.init.xavier_uniform_(self.RNA_encoder_l_mu.weight)
  538. self.RNA_encoder_bn_mu = nn.BatchNorm1d(translator_embed_dim)
  539. self.RNA_encoder_act_mu = mean_activation
  540. self.MET_encoder_l_mu = nn.Linear(translator_input_dim_m, translator_embed_dim)
  541. nn.init.xavier_uniform_(self.MET_encoder_l_mu.weight)
  542. self.MET_encoder_bn_mu = nn.BatchNorm1d(translator_embed_dim)
  543. self.MET_encoder_act_mu = mean_activation
  544. self.RNA_encoder_l_d = nn.Linear(translator_input_dim_r, translator_embed_dim)
  545. nn.init.xavier_uniform_(self.RNA_encoder_l_d.weight)
  546. self.RNA_encoder_bn_d = nn.BatchNorm1d(translator_embed_dim)
  547. self.RNA_encoder_act_d = log_var_activation
  548. self.MET_encoder_l_d = nn.Linear(translator_input_dim_m, translator_embed_dim)
  549. nn.init.xavier_uniform_(self.MET_encoder_l_d.weight)
  550. self.MET_encoder_bn_d = nn.BatchNorm1d(translator_embed_dim)
  551. self.MET_encoder_act_d = log_var_activation
  552. self.RNA_decoder_l = nn.Linear(translator_embed_dim, translator_input_dim_r)
  553. nn.init.xavier_uniform_(self.RNA_decoder_l.weight)
  554. self.RNA_decoder_bn = nn.BatchNorm1d(translator_input_dim_r)
  555. self.RNA_decoder_act = decoder_activation
  556. self.MET_decoder_l = nn.Linear(translator_embed_dim, translator_input_dim_m)
  557. nn.init.xavier_uniform_(self.MET_decoder_l.weight)
  558. self.MET_decoder_bn = nn.BatchNorm1d(translator_input_dim_m)
  559. self.MET_decoder_act = decoder_activation
  560. self.background_pro_alpha = nn.Parameter(torch.randn(1, translator_input_dim_m))
  561. self.background_pro_log_beta = nn.Parameter(torch.clamp(torch.randn(1, translator_input_dim_m), -10, 1))
  562. self.scale_parameters_l = nn.Linear(translator_embed_dim, translator_embed_dim)
  563. self.scale_parameters_bn = nn.BatchNorm1d(translator_embed_dim)
  564. self.scale_parameters_act = mean_activation
  565. self.pi_l = nn.Linear(translator_embed_dim, 1)
  566. self.pi_bn = nn.BatchNorm1d(1)
  567. self.pi_act = nn.Sigmoid()
  568. self.se_rna = SELayer(translator_embed_dim, reduction_ratio)
  569. self.se_met = SELayer(translator_embed_dim, reduction_ratio)
  570. def reparameterize(self, mu, sigma):
  571. sigma = torch.exp(sigma / 2)
  572. eps = torch.randn_like(sigma)
  573. return mu + eps * sigma
  574. def forward_with_RNA(self, x, forward_type):
  575. latent_layer_mu = self.RNA_encoder_act_mu(self.RNA_encoder_bn_mu(self.RNA_encoder_l_mu(x)))
  576. latent_layer_d = self.RNA_encoder_act_d(self.RNA_encoder_bn_d(self.RNA_encoder_l_d(x)))
  577. if forward_type == 'test':
  578. latent_layer = latent_layer_mu
  579. elif forward_type == 'train':
  580. latent_layer = self.reparameterize(latent_layer_mu, latent_layer_d)
  581. latent_layer = self.se_rna(latent_layer)
  582. latent_layer_R_out = self.RNA_decoder_act(self.RNA_decoder_bn(self.RNA_decoder_l(latent_layer)))
  583. latent_layer_M_out = self.MET_decoder_act(self.MET_decoder_bn(self.MET_decoder_l(latent_layer)))
  584. return latent_layer_R_out, latent_layer_M_out, latent_layer_mu, latent_layer_d
  585. def forward_with_MET(self, x, forward_type):
  586. latent_layer = self.forward_adt(x, forward_type)
  587. latent_layer = self.se_met(latent_layer)
  588. latent_layer_R_out = self.RNA_decoder_act(self.RNA_decoder_bn(self.RNA_decoder_l(latent_layer)))
  589. latent_layer_M_out = self.MET_decoder_act(self.MET_decoder_bn(self.MET_decoder_l(latent_layer)))
  590. return latent_layer_R_out, latent_layer_M_out, latent_layer, latent_layer
  591. def train_model(self, x, input_type):
  592. if input_type == 'RNA':
  593. return self.forward_with_RNA(x, 'train')
  594. elif input_type == 'MET':
  595. return self.forward_with_MET(x, 'train')
  596. def test_model(self, x, input_type):
  597. if input_type == 'RNA':
  598. return self.forward_with_RNA(x, 'test')
  599. elif input_type == 'MET':
  600. return self.forward_with_MET(x, 'test')
  601. class Background_Single_Translator(nn.Module):
  602. def __init__(
  603. self,
  604. translator_input_dim: int,
  605. translator_embed_dim: int,
  606. translator_embed_act_list: list,
  607. reduction_ratio: int = 64
  608. ):
  609. """
  610. Background Single Translator block with SE layer.
  611. Parameters
  612. ----------
  613. translator_input_dim
  614. dimension of input from encoder for translator.
  615. translator_embed_dim
  616. dimension of embedding space for translator.
  617. translator_embed_act_list
  618. activation list for translator, involving [mean_activation, log_var_activation, decoder_activation].
  619. reduction_ratio
  620. The reduction ratio for the SE layer, default 64.
  621. """
  622. super(Background_Single_Translator, self).__init__()
  623. mean_activation, log_var_activation, decoder_activation = translator_embed_act_list
  624. self.encoder_l_mu = nn.Linear(translator_input_dim, translator_embed_dim)
  625. nn.init.xavier_uniform_(self.encoder_l_mu.weight)
  626. self.encoder_bn_mu = nn.BatchNorm1d(translator_embed_dim)
  627. self.encoder_act_mu = mean_activation
  628. self.encoder_l_d = nn.Linear(translator_input_dim, translator_embed_dim)
  629. nn.init.xavier_uniform_(self.encoder_l_d.weight)
  630. self.encoder_bn_d = nn.BatchNorm1d(translator_embed_dim)
  631. self.encoder_act_d = log_var_activation
  632. self.decoder_l = nn.Linear(translator_embed_dim, translator_input_dim)
  633. nn.init.xavier_uniform_(self.decoder_l.weight)
  634. self.decoder_bn = nn.BatchNorm1d(translator_input_dim)
  635. self.decoder_act = decoder_activation
  636. self.background_pro_alpha = nn.Parameter(torch.randn(1, translator_input_dim))
  637. self.background_pro_log_beta = nn.Parameter(torch.clamp(torch.randn(1, translator_input_dim), -10, 1))
  638. self.scale_parameters_l = nn.Linear(translator_embed_dim, translator_embed_dim)
  639. self.scale_parameters_bn = nn.BatchNorm1d(translator_embed_dim)
  640. self.scale_parameters_act = mean_activation
  641. self.pi_l = nn.Linear(translator_embed_dim, 1)
  642. self.pi_bn = nn.BatchNorm1d(1)
  643. self.pi_act = nn.Sigmoid()
  644. self.se = SELayer(translator_embed_dim, reduction_ratio)
  645. def reparameterize(self, mu, sigma):
  646. sigma = torch.exp(sigma / 2)
  647. eps = torch.randn_like(sigma)
  648. return mu + eps * sigma
  649. def forward(self, x, forward_type):
  650. latent_layer = self.forward_adt(x, forward_type)
  651. latent_layer = self.se(latent_layer)
  652. latent_layer_out = self.decoder_act(self.decoder_bn(self.decoder_l(latent_layer)))
  653. return latent_layer_out, latent_layer, latent_layer

model_component.py at commit 22a2192, under MIT · at the source

Overview

Authors: Kehan Lang1, Chenyang Jia1, Siyu Li1, Yi Guo2, Dingjun Hu1, Shengquan Chen1,3
  1. School of Mathematical Sciences and LPMC, Nankai University, Tianjin 300071, China
  2. College of Electronic Information and Optical Engineering, Nankai University, Tianjin 300350, China
  3. Academy for Advanced Interdisciplinary Studies, Nankai University, Tianjin 300071, China
Institutions: Nankai University (China)
Journal: Genome research, volume 36, issue 4, pages 769-784
Dates: received 24 August 2025; accepted 2 March 2026; published online 7 April 2026; in print April 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1101/gr.281350.125 · PMID 41887797 · PMCID PMC13138010 · OpenAlex W7140373506
Open access: green, a free copy (OpenAlex)
Status: code verified
Categories: genetics / omics (modality), human (organism), mouse (organism), cellular / molecular (subfield)
Methods: Statistics, Smoothing, state filtering, decompositions, Connectivity, Machine learning
MeSH: DNA Methylation*, Single-Cell Analysis*, Software*, Transcriptome*, Animals, Epigenesis, Genetic, Humans, Mice, Single-Cell Gene Expression Analysis (* major topic)
Topic: Single-cell and spatial transcriptomics (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: National Natural Science Foundation of China (62473212); Young Elite Scientists Sponsorship Program (2023QNRC001); Fundamental Research Funds for the Central Universities (050-63253077); National Undergraduate Training Program for Innovation and Entrepreneurship
Citations: cited by 1 paper (Europe PMC); 53 references in the paper

Abstract

Single-cell multiomic sequencing technologies have offered unprecedented insights into cellular heterogeneity by jointly profiling gene expression and epigenetic landscapes at single-cell resolution. However, the application of these technologies remains limited owing to technical challenges and high costs. Computational approaches for cross-modality translation provide a promising solution to these limitations by enabling the inference of one modality from another. However, existing methods for cross-modality translation between single-cell RNA sequencing (scRNA-seq) and single-cell DNA methylation (scDNAm) data face limitations, including unidirectionality, inadequate modeling of context-specific DNA methylation–expression associations, neglect of biological relevance in evaluation, and poor performance in limited paired training data. To fill these gaps, we introduce scBOND, a bidirectional cross-modal translation framework tailored for scRNA-seq and scDNAm profiles. scBOND leverages a mixture-of-experts block to capture context-dependent regulatory patterns, while implementing self-attention mechanism and a feature recalibration module to enhance biological signal fidelity. Extensive experiments demonstrate scBOND consistently outperforms baseline methods in both translation directions, yielding high-accuracy translation while preserving cellular structure. In mouse embryonic data, scBOND preserves subtle, functionally significant differences between closely related cell types, which are undetected in the original data. Downstream analyses confirm that scBOND effectively recovers tissue-specific signals in human brain neurons. Moreover, using RNA-only data, we reconstruct scDNAm profiles and identify cell-type- and stage-specific regulatory mechanisms in oligodendrocyte lineage. To further improve model generalization in paired data-scarce scenarios, we propose scBOND-Aug, a variant of scBOND equipped with a biologically informed data augmentation strategy, which demonstrates superior results with limited paired data.

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

Repositories

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

mcgilldinglab/scCross

License: MIT
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 586d415ae67c89225d12312c84588d02434caa0c, 9 August 2025
Languages: Python (17), Jupyter (12)
Size: 53 files, 29 scripts
Software Heritage: not archived
Found in: the text, “Baseline methods”
Holds: README, license file, environment (env.yaml, pyproject.toml, setup.py), documentation, 12 notebooks
Not found: CITATION.cff, tests, continuous integration
Tools: anndata (22 files), Scanpy (21 files), pandas (17 files), scikit-learn (15 files), Matplotlib (13 files), NumPy (12 files), PyTorch (5 files), SciPy (5 files), seaborn (3 files), h5py (2 files), NetworkX (2 files), BEDTools (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
31 files

tanlabcode/MAPLE.1.0

License: none: the authors keep all their rights
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: f707eaf880b5c373b81b559c4494a5502f4b7005, 20 September 2021
Languages: R (6)
Size: 11 files, 6 scripts
Software Heritage: not archived
Found in: the text, “Baseline methods”
Holds: README, environment (DESCRIPTION), documentation
Not found: license file, CITATION.cff, tests, continuous integration
Tools: data.table (3 files), reshape2 (3 files), glmnet (1 file), randomForest (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
7 files, not copied: shown from their source

OSCR keeps no copy of these files: this repository has no license that allows it. The reader above shows each one from its source, fetched by your browser at commit f707eaf, when its fingerprint is the one OSCR verified. How this works.

BioX-NKU/scBOND

License: MIT
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 22a21923897186ce3ca55e893a9693ae05583d1d, 22 April 2026
Languages: Python (13), Jupyter (9)
Size: 60 files, 22 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file, environment (requirements.txt, setup.py, docs/requirements.txt), documentation, 6 notebooks
Not found: CITATION.cff, tests, continuous integration
Tools: Scanpy (17 files), PyTorch (7 files), anndata (5 files), NumPy (5 files), pandas (5 files), SciPy (4 files), Matplotlib (3 files), scikit-learn (2 files), seaborn (2 files), BEDTools (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
24 files

Zenodo 17699419

License: CC-BY-4.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Scanpy (11 files), PyTorch (7 files), anndata (5 files), NumPy (5 files), pandas (5 files), SciPy (4 files), Matplotlib (3 files), scikit-learn (2 files), seaborn (2 files), BEDTools (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers (HTTP 200)
  • 29 September 2026: the link answers (HTTP 200)
17 files

Code availability

The MIT-licensed scBOND software, including detailed documents and tutorials, is freely available at GitHub (https://github.com/BioX-NKU/scBOND) and as Supplemental Code. Comprehensive and automatically generated API documentation can be accessed via the read the docs site at https://scbond.readthedocs.io/en/latest/. Additionally, a stable, versioned release of the software is archived on Zenodo (https://zenodo.org/records/17699419).

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

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:

  • 4 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 72 scripts, each with its path and the digest of its content;
  • 9 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

Data links

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, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 9 MeSH terms, 4 funders, 48 references.

Cite

This paper

Lang, K., Jia, C., Li, S., Guo, Y., Hu, D., & Chen, S. (2026). High-fidelity bidirectional translation between single-cell transcriptomes and DNA methylomes with scBOND. Genome research, 36(4), 769-784. https://doi.org/10.1101/gr.281350.125

BibTeX

@article{lang2026high,
author = {Lang, Kehan and Jia, Chenyang and Li, Siyu and Guo, Yi and Hu, Dingjun and Chen, Shengquan},
title = {{High-fidelity bidirectional translation between single-cell transcriptomes and DNA methylomes with scBOND}},
journal = {Genome research},
year = {2026},
month = apr,
volume = {36},
number = {4},
pages = {769--784},
publisher = {Cold Spring Harbor Laboratory Press},
issn = {1088-9051},
doi = {10.1101/gr.281350.125},
url = {https://doi.org/10.1101/gr.281350.125},
pmid = {41887797},
pmcid = {PMC13138010}
}

RIS

TY - JOUR
AU - Lang, Kehan
AU - Jia, Chenyang
AU - Li, Siyu
AU - Guo, Yi
AU - Hu, Dingjun
AU - Chen, Shengquan
TI - High-fidelity bidirectional translation between single-cell transcriptomes and DNA methylomes with scBOND
T2 - Genome research
J2 - Genome Res
PY - 2026
DA - 2026/04/07
VL - 36
IS - 4
SP - 769
EP - 784
SN - 1088-9051
PB - Cold Spring Harbor Laboratory Press
DO - 10.1101/gr.281350.125
UR - https://doi.org/10.1101/gr.281350.125
LA - en
ER -

CSL-JSON

{
"id": "10.1101/gr.281350.125",
"type": "article-journal",
"title": "High-fidelity bidirectional translation between single-cell transcriptomes and DNA methylomes with scBOND",
"container-title": "Genome research",
"author": [
{
"family": "Lang",
"given": "Kehan"
},
{
"family": "Jia",
"given": "Chenyang"
},
{
"family": "Li",
"given": "Siyu"
},
{
"family": "Guo",
"given": "Yi"
},
{
"family": "Hu",
"given": "Dingjun"
},
{
"family": "Chen",
"given": "Shengquan"
}
],
"container-title-short": "Genome Res",
"volume": "36",
"issue": "4",
"page": "769-784",
"DOI": "10.1101/gr.281350.125",
"PMID": "41887797",
"PMCID": "PMC13138010",
"ISSN": "1088-9051",
"publisher": "Cold Spring Harbor Laboratory Press",
"URL": "https://doi.org/10.1101/gr.281350.125",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
7
]
]
}
}

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.1038/s41467-026-73171-4 [code]
Dissecting epigenetic heterogeneity in single-cell DNA methylomes with a unified framework.
Journal: Nature communications
In common: BEDTools, anndata, Scanpy, 8 other tools, genetics / omics, cellular / molecular, 7 references
[2] doi:10.1038/s41467-026-71803-3 [code]
Charting the transition from in vitro gliogenesis to the in vivo maturation of human glial progenitor cells transplanted into the hypomyelinated mouse brain.
Journal: Nature communications
In common: BEDTools, anndata, Scanpy, 9 other tools, genetics / omics, mouse, cellular / molecular, 4 references
[3] doi:10.1002/advs.77524 [code]
MethyAnno: An Interpretable Automated Annotation Method Leveraging Multi-Scale Information and Metric Learning Framework for scDNAm Data.
Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)
In common: anndata, Scanpy, PyTorch, 6 other tools, genetics / omics, 6 references
[4] doi:10.1038/s41586-026-10629-x [code]
Whole-genome duplication shaped cell-type evolution in the vertebrate brain.
Journal: Nature
In common: BEDTools, anndata, Scanpy, 8 other tools, genetics / omics, mouse, cellular / molecular, 2 references
[5] doi:10.1093/bioinformatics/btag652 [code]
mmVelo: a deep generative model for estimating cell state-dependent dynamics across multiple modalities.
Journal: Bioinformatics (Oxford, England)
In common: BEDTools, anndata, Scanpy, 7 other tools, genetics / omics, mouse, 4 references
[6] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: anndata, Scanpy, NetworkX, 9 other tools, genetics / omics, cellular / molecular, 2 references
[7] doi:10.1016/j.isci.2026.116055 [code]
Mapping the transcriptional diversity of calcium signaling in the mouse and human brain.
Journal: iScience
In common: anndata, Scanpy, NetworkX, 9 other tools, genetics / omics, mouse, 2 references
[8] doi:10.1186/s13059-026-04177-w [code]
Genomic sequence evolution underlying human neocortical interareal diversification.
Journal: Genome biology
In common: BEDTools, anndata, Scanpy, 8 other tools, genetics / omics, mouse, cellular / molecular, 2 references
[9] doi:10.1038/s42003-026-10462-y [code]
SpaDC enables sequence-based integrative analysis and regulatory inference of spatial chromatin accessibility data.
Journal: Communications biology
In common: BEDTools, anndata, Scanpy, 7 other tools, genetics / omics, mouse, 3 references
[10] doi:10.1038/s41514-026-00391-9 [code]
Region-specific transcriptional signatures of brain aging in the absence of neuropathology at the single-cell level.
Journal: npj aging
In common: anndata, Scanpy, reshape2, 8 other tools, genetics / omics, cellular / molecular, 3 references

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.