LncPNdeep: A long non-coding RNA classifier based on large language model with peptide and nucleotide embedding.
The 4 matches
- [1] § Materials and methods › Concatenated deep learning ↔ simulation_and_pretrain_code/utils.py, lines 38–80 · score 0.58 · cross entropy, binary classification, dense, loss
- [2] § Materials and methods › Nucleotide embedding ↔ simulation_and_pretrain_code/pre_dataset.py, lines 12–109 · score 0.55 · mask token, special token, pre, CLS, sequences, RNA
- [3] § Materials and methods › Nucleotide embedding ↔ predict_lncrna.py, lines 195–285 · score 0.52 · bigbird768, bigbird256, nucleotide embeddings, Longformer256, prediction, sequence
- [4] § Materials and methods › Concatenated deep learning ↔ predict_lncrna.py, lines 195–285 · score 0.51 · bigbird768, bigbird256, Longformer256, Fake, Max, nucleotide
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 · 359 lines · 12 KB · no license · 2 matches
- # -*- coding: utf-8 -*-
- """
- Full lncRNA / coding RNA prediction pipeline:
- 1. Input FASTA
- 2. Extract nucleotide embeddings (Longformer256, Bigbird256, Bigbird768)
- 3. Extract peptide embeddings (Average / Fake / Max)
- 4. Load h5 classifier and predict
- 5. Write results txt: sequence name + lncRNA probability + label
- Example:
- py predict_lncrna.py --input_fasta input.fasta --output_dir work/ --result_txt results.txt
- """
- from __future__ import annotations
- import argparse
- import os
- from pathlib import Path
- from typing import List, Tuple
- import numpy as np
- import pandas as pd
- import tensorflow as tf
- import torch
- from Bio import SeqIO
- from Bio.Seq import Seq
- from tqdm import tqdm
- from transformers import BertModel, BertTokenizer
- from extract_nucleotide_embeddings import (
- MODEL_CONFIGS,
- extract_embeddings_from_fasta,
- parse_fasta,
- )
- from download_weights import (
- get_nucleotide_embedding_dir,
- get_weights_pretrain_dir,
- missing_weight_files,
- weights_ready,
- )
- SCRIPT_DIR = Path(__file__).resolve().parent
- DEFAULT_MODEL_H5 = SCRIPT_DIR / "ProteinTransAllfeature_ResCNN2_07_08.h5"
- DEFAULT_WEIGHTS_DIR = get_weights_pretrain_dir(SCRIPT_DIR)
- _PROT_BERT_TOKENIZER = None
- _PROT_BERT_MODEL = None
- def _get_protein_bert(device: torch.device):
- global _PROT_BERT_TOKENIZER, _PROT_BERT_MODEL
- if _PROT_BERT_TOKENIZER is None or _PROT_BERT_MODEL is None:
- _PROT_BERT_TOKENIZER = BertTokenizer.from_pretrained(
- "Rostlab/prot_bert", do_lower_case=False
- )
- _PROT_BERT_MODEL = BertModel.from_pretrained("Rostlab/prot_bert").to(device)
- _PROT_BERT_MODEL.eval()
- return _PROT_BERT_TOKENIZER, _PROT_BERT_MODEL
- def get_bert_embedding(sequence: str, len_seq_limit: int, device: torch.device) -> np.ndarray:
- tokenizer, model = _get_protein_bert(device)
- sequence_w_spaces = " ".join(list(sequence))
- encoded_input = tokenizer(
- sequence_w_spaces,
- truncation=True,
- max_length=len_seq_limit,
- padding="max_length",
- return_tensors="pt",
- ).to(device)
- with torch.no_grad():
- output = model(**encoded_input)
- output_hidden = output["last_hidden_state"][:, 0][0].detach().cpu().numpy()
- if len(output_hidden) != 1024:
- raise ValueError(f"ProtBERT embedding dimension must be 1024, got {len(output_hidden)}")
- return output_hidden
- def extract_protein_embeddings_from_fasta(
- fasta_path: str,
- output_dir: str,
- prefix: str = "protein",
- device: str | None = None,
- ) -> Tuple[str, str, str]:
- """
- Extract three protein embedding variants from FASTA and save as npy files.
- Returns: (average_path, fake_path, max_path)
- """
- resolved_device = torch.device(
- device if device else ("cuda" if torch.cuda.is_available() else "cpu")
- )
- os.makedirs(output_dir, exist_ok=True)
- embed_average: List[np.ndarray] = []
- embed_fake: List[np.ndarray] = []
- embed_max: List[np.ndarray] = []
- for item in tqdm(SeqIO.parse(fasta_path, "fasta"), desc="Extracting protein embeddings"):
- reading_frames = [
- Seq(item.seq).translate(table="Standard", stop_symbol="*", to_stop=False, cds=False)
- for _ in range(3)
- ]
- peptides: List[str] = []
- lengths: List[int] = []
- frame_embeddings: List[np.ndarray] = []
- for frame in reading_frames:
- for peptide in frame.split("*"):
- if len(peptide) > 100:
- peptides.append(peptide)
- lengths.append(len(peptide))
- frame_embeddings.append(
- get_bert_embedding(
- sequence=str(peptide),
- len_seq_limit=1200,
- device=resolved_device,
- )
- )
- if len(peptides) == 0:
- fallback = get_bert_embedding(
- sequence=str(Seq(item.seq).translate()),
- len_seq_limit=1200,
- device=resolved_device,
- )
- embed_average.append(fallback)
- embed_fake.append(fallback)
- embed_max.append(fallback)
- else:
- embed_max.append(frame_embeddings[int(np.argmax(lengths))])
- embed_fake.append(
- get_bert_embedding(
- sequence=str(Seq(item.seq).translate()),
- len_seq_limit=1200,
- device=resolved_device,
- )
- )
- embed_average.append(np.sum(frame_embeddings, axis=0))
- average_path = os.path.join(output_dir, f"{prefix}_Average_Protein.npy")
- fake_path = os.path.join(output_dir, f"{prefix}_Fake_Protein.npy")
- max_path = os.path.join(output_dir, f"{prefix}_Max_Protein.npy")
- np.save(average_path, np.array(embed_average))
- np.save(fake_path, np.array(embed_fake))
- np.save(max_path, np.array(embed_max))
- return average_path, fake_path, max_path
- def load_embedding_txt(path: str) -> np.ndarray:
- data = pd.read_table(path, sep=" ", header=None)
- array = np.array(data, dtype=np.float32)
- return array.reshape(array.shape[0], 1, array.shape[1])
- def load_protein_npy(path: str) -> np.ndarray:
- array = np.load(path)
- return array.reshape(array.shape[0], 1, array.shape[1])
- def predict_lncrna_probability(
- model_h5: str,
- average_protein: np.ndarray,
- fake_protein: np.ndarray,
- max_protein: np.ndarray,
- bigbird256: np.ndarray,
- bigbird768: np.ndarray,
- longformer256: np.ndarray,
- ) -> np.ndarray:
- """Load h5 model and return softmax probabilities with shape (N, 2): col 0 = lncRNA, col 1 = coding RNA."""
- model = tf.keras.models.load_model(model_h5, compile=False)
- predictions = model.predict(
- [
- average_protein,
- fake_protein,
- max_protein,
- bigbird256,
- bigbird768,
- longformer256,
- ],
- verbose=0,
- )
- predictions = np.asarray(predictions, dtype=np.float32)
- if predictions.ndim == 2 and predictions.shape[1] == 2:
- return predictions
- raise ValueError(
- f"Expected model output shape (N, 2) softmax probabilities, got {predictions.shape}"
- )
- def run_pipeline(
- input_fasta: str,
- output_dir: str,
- result_txt: str,
- model_h5: str,
- weights_dir: str,
- vocab_path: str | None = None,
- device: str | None = None,
- attention_mode: str = "sliding_chunks",
- skip_nucleotide: bool = False,
- skip_protein: bool = False,
- ) -> None:
- if not weights_ready(SCRIPT_DIR):
- missing = "\n".join(f" - {path}" for path in missing_weight_files(SCRIPT_DIR))
- raise FileNotFoundError(
- "Required model weights are missing. Run this first:\n"
- " python download_weights.py\n\n"
- f"Missing files:\n{missing}"
- )
- os.makedirs(output_dir, exist_ok=True)
- nucleotide_dir = os.path.join(output_dir, "nucleotide")
- protein_dir = os.path.join(output_dir, "protein")
- records = parse_fasta(input_fasta)
- sequence_names = [name for name, _ in records]
- if not skip_nucleotide:
- nucleotide_paths = extract_embeddings_from_fasta(
- fasta_path=input_fasta,
- output_dir=nucleotide_dir,
- models=list(MODEL_CONFIGS.keys()),
- weights_dir=weights_dir,
- vocab_path=vocab_path,
- device=device,
- attention_mode=attention_mode,
- )
- else:
- nucleotide_paths = {
- name: os.path.join(nucleotide_dir, cfg["output_file"])
- for name, cfg in MODEL_CONFIGS.items()
- }
- for path in nucleotide_paths.values():
- if not os.path.isfile(path):
- raise FileNotFoundError(f"Missing nucleotide embedding file: {path}")
- if not skip_protein:
- average_path, fake_path, max_path = extract_protein_embeddings_from_fasta(
- fasta_path=input_fasta,
- output_dir=protein_dir,
- prefix="query",
- device=device,
- )
- else:
- average_path = os.path.join(protein_dir, "query_Average_Protein.npy")
- fake_path = os.path.join(protein_dir, "query_Fake_Protein.npy")
- max_path = os.path.join(protein_dir, "query_Max_Protein.npy")
- for path in (average_path, fake_path, max_path):
- if not os.path.isfile(path):
- raise FileNotFoundError(f"Missing protein embedding file: {path}")
- probabilities = predict_lncrna_probability(
- model_h5=model_h5,
- average_protein=load_protein_npy(average_path),
- fake_protein=load_protein_npy(fake_path),
- max_protein=load_protein_npy(max_path),
- bigbird256=load_embedding_txt(nucleotide_paths["Bigbird256"]),
- bigbird768=load_embedding_txt(nucleotide_paths["Bigbird768"]),
- longformer256=load_embedding_txt(nucleotide_paths["Longformer256"]),
- )
- if len(probabilities) != len(sequence_names):
- raise ValueError(
- f"Prediction count ({len(probabilities)}) does not match sequence count ({len(sequence_names)})"
- )
- os.makedirs(os.path.dirname(result_txt) or ".", exist_ok=True)
- with open(result_txt, "w", encoding="utf-8") as handle:
- handle.write(
- "sequence_name\tlncRNA_probability\tcodingRNA_probability\tis_lncRNA\n"
- )
- for name, prob_pair in zip(sequence_names, probabilities):
- lnc_prob = float(prob_pair[0])
- coding_prob = float(prob_pair[1])
- is_lnc = "lncRNA" if lnc_prob >= coding_prob else "codingRNA"
- handle.write(
- f"{name}\t{lnc_prob:.6f}\t{coding_prob:.6f}\t{is_lnc}\n"
- )
- print(f"Prediction finished for {len(sequence_names)} sequence(s)")
- print(f"Results written to: {result_txt}")
- def parse_args() -> argparse.Namespace:
- parser = argparse.ArgumentParser(description="Full lncRNA / coding RNA prediction pipeline")
- parser.add_argument("--input_fasta", required=True, help="Path to input FASTA file")
- parser.add_argument("--output_dir", required=True, help="Directory for intermediate embeddings and outputs")
- parser.add_argument(
- "--result_txt",
- default=None,
- help="Path to final results txt (default: output_dir/prediction_results.txt)",
- )
- parser.add_argument(
- "--model_h5",
- default=str(DEFAULT_MODEL_H5),
- help="Path to final classification model h5 file",
- )
- parser.add_argument(
- "--weights_dir",
- default=str(DEFAULT_WEIGHTS_DIR),
- help="Directory containing nucleotide pretrain weight files",
- )
- parser.add_argument(
- "--vocab_path",
- default=None,
- help="Optional path to pre_valid_kmer1.txt for vocabulary",
- )
- parser.add_argument("--device", default=None, choices=["cpu", "cuda"], help="Inference device (default: auto)")
- parser.add_argument(
- "--attention_mode",
- default="sliding_chunks",
- choices=["sliding_chunks", "n2", "tvm"],
- help="Longformer attention mode",
- )
- parser.add_argument(
- "--skip_nucleotide",
- action="store_true",
- help="Skip nucleotide embedding extraction (requires existing files in output_dir/nucleotide)",
- )
- parser.add_argument(
- "--skip_protein",
- action="store_true",
- help="Skip protein embedding extraction (requires existing npy files in output_dir/protein)",
- )
- parser.add_argument(
- "--download_weights",
- action="store_true",
- help="Download missing weights from Hugging Face before running",
- )
- return parser.parse_args()
- def main() -> None:
- args = parse_args()
- if args.download_weights:
- from download_weights import ensure_weights
- ensure_weights(base_dir=SCRIPT_DIR)
- result_txt = args.result_txt or os.path.join(args.output_dir, "prediction_results.txt")
- run_pipeline(
- input_fasta=args.input_fasta,
- output_dir=args.output_dir,
- result_txt=result_txt,
- model_h5=args.model_h5,
- weights_dir=args.weights_dir,
- vocab_path=args.vocab_path,
- device=args.device,
- attention_mode=args.attention_mode,
- skip_nucleotide=args.skip_nucleotide,
- skip_protein=args.skip_protein,
- )
- if __name__ == "__main__":
- main()
predict_lncrna.py at commit 0e20ea3, no license · at the source
Overview
- Department of Biostatistics, University of Michigan, 1415 Washington Heights, Ann Arbor, MI, 48109, USA
- Department of Oral and Maxillofacial Surgery, University of Michigan, 1011 N. University, Ave, Ann Arbor, MI, 48109, USA
- Experimental Rheumatology, Department of Rheumatology, Radboud University Medical Centre, Nijmegen, the Netherlands
Abstract
Accurate classification of long non-coding RNAs (lncRNAs) is essential for transcriptome annotation and understanding gene regulation. Existing computational methods predominantly rely on nucleotide sequence features, frequently overlooking biologically relevant peptide signals encoded within lncRNAs. To overcome this limitation, we developed LncPNdeep, an integrative deep learning framework that combines nucleotide and peptide embeddings extracted via masked language models, specifically utilizing contextual representations from BigBird, Longformer, and ProtTrans. By fusing both features in a concatenated neural architecture, LncPNdeep robustly captures complex sequence relationships and improves discrimination between lncRNAs and coding RNAs. Benchmarking on the human transcriptome achieved state-of-the-art performance with 97.1% accuracy, surpassing established lncRNA classification tools and baseline machine learning models. LncPNdeep also demonstrated superior generalization ability across cross-species datasets, maintaining consistently high accuracy and F1 scores. Permutation analysis highlighted the pivotal role of peptide embeddings, especially Average Peptide Embedding, in model performance, while t-SNE visualizations confirmed that integrating multiple embeddings markedly enhances the separation of lncRNAs from coding RNAs. These results position LncPNdeep as a versatile and powerful tool for transcriptomic research, facilitating lncRNA discovery, biomarker identification, and comparative genomics. The model and instructions are freely available at https://
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 4 matches between paragraphs and lines of code.
yatoka233/LncPNdeep
0e20ea30f6f5d5e97f73823d8abbd5c056d8e574, 25 June 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
21 files
- download_weights.py, Python, 229 lines
- extract_nucleotide_embed
dings.py , Python, 311 lines - predict_lncrna.py, Python, 359 lines, 2 matches
- simulation_and_pretrain_
code/ , Python, 175 linesdataset.py - simulation_and_pretrain_
code/ , Python, 200 linesfeature.py - simulation_and_pretrain_
code/ , Python, 61 linesmodel/ attention.py - simulation_and_pretrain_
code/ , Python, 129 linesmodel/ bert.py - simulation_and_pretrain_
code/ , Python, 78 linesmodel/ mybigbird.py - simulation_and_pretrain_
code/ , Python, 208 linesmulti_feature.py - simulation_and_pretrain_
code/ , Python, 120 linespipeline.py - simulation_and_pretrain_
code/ , Python, 131 lines, 1 matchpre_dataset.py - simulation_and_pretrain_
code/ , Python, 90 linespretrain.py - simulation_and_pretrain_
code/ , Python, 109 linesrun.py - simulation_and_pretrain_
code/ , Python, 7 linesscript.py - simulation_and_pretrain_
code/ , Python, 136 linesscripts/ pre_dataset.py - simulation_and_pretrain_
code/ , Python, 70 linesscripts/ pretrain.py - simulation_and_pretrain_
code/ , Python, 172 linessplit_sequence.py - simulation_and_pretrain_
code/ , Python, 163 linestest.py - simulation_and_pretrain_
code/ , Python, 176 linestrainer.py - simulation_and_pretrain_
code/ , Python, 80 lines, 1 matchutils.py - README.md, Text, 145 lines
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;
- 20 scripts, each with its path and the digest of its content;
- 4 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.
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 3, 28 September 2026
- Authors: added Zongrui Dai (0000-0002-7893-5004); Feiyang Deng (0009-0003-4274-7866); Hsiao H. Sung (0000-0001-6016-3838); removed Zongrui Dai; Feiyang Deng; Hsiao H. Sung
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 3 authors, 4 keywords, 1 funder, 39 references.
Cite
This paper
Dai, Z., Deng, F., & Sung, H. H. (2026). LncPNdeep: A long non-coding RNA classifier based on large language model with peptide and nucleotide embedding. Non-coding RNA research, 20, 104-112. https://
BibTeX
@article{dai2026lncpndee
author = {Dai, Zongrui and Deng, Feiyang and Sung, Hsiao H.},
title = {{LncPNdeep: A long non-coding RNA classifier based on large language model with peptide and nucleotide embedding}},
journal = {Non-coding RNA research},
year = {2026},
month = aug,
volume = {20},
pages = {104--112},
publisher = {KeAi Publishing},
issn = {2468-0540},
doi = {10.1016/
url = {https://
pmid = {42621896},
pmcid = {PMC13488033}
}
RIS
TY - JOUR
AU - Dai, Zongrui
AU - Deng, Feiyang
AU - Sung, Hsiao H.
TI - LncPNdeep: A long non-coding RNA classifier based on large language model with peptide and nucleotide embedding
T2 - Non-coding RNA research
J2 - Noncoding RNA Res
PY - 2026
DA - 2026/
VL - 20
SP - 104
EP - 112
SN - 2468-0540
PB - KeAi Publishing
DO - 10.1016/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1016/
"type": "article-journal",
"title": "LncPNdeep: A long non-coding RNA classifier based on large language model with peptide and nucleotide embedding",
"container-title": "Non-coding RNA research",
"author": [
{
"family": "Dai",
"given": "Zongrui"
},
{
"family": "Deng",
"given": "Feiyang"
},
{
"family": "Sung",
"given": "Hsiao H."
}
],
"container-title-short":
"volume": "20",
"page": "104-112",
"DOI": "10.1016/
"PMID": "42621896",
"PMCID": "PMC13488033",
"ISSN": "2468-0540",
"publisher": "KeAi Publishing",
"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.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: Biopython, Hugging Face Transformers, TensorFlow, 3 other tools
- [2] doi:10.1016/j.xops.2026.101249 [code]
- A Disorder-Aware Computational Framework to Identify Structurally Tractable Targets in Proliferative Vitreoretinopathy.Journal: Ophthalmology scienceIn common: Biopython, Hugging Face Transformers, TensorFlow, 3 other tools
- [3] doi:10.1038/s43587-026-01207-x [code]
- A microprotein atlas of the human frontal cortex in Alzheimer's disease.Journal: Nature agingIn common: Biopython, pandas, NumPy, 2 references
- [4] doi:10.1038/s41592-026-03057-2 [code]
- CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species.Journal: Nature methodsIn common: Biopython, TensorFlow, PyTorch, 2 other tools, methods / tools
- [5] doi:10.1038/s41586-026-10658-6 [code]
- An AI system to help scientists write expert-level empirical software.Journal: NatureIn common: Hugging Face Transformers, TensorFlow, PyTorch, 2 other tools, methods / tools
- [6] doi:10.1371/journal.pone.0346575 [code]
- Statistically valid explainable black-box machine learning: applications in sex classification across species using brain imaging.Journal: PloS oneIn common: Hugging Face Transformers, TensorFlow, PyTorch, 2 other tools, methods / tools
- [7] doi:10.1093/nargab/lqag050 [code]
- TSProm: deep learning framework to predict tissue-specific regulatory logic.Journal: NAR genomics and bioinformaticsIn common: Biopython, Hugging Face Transformers, PyTorch, 2 other tools
- [8] doi:10.1093/bib/bbag339 [code]
- scDeepAPA: a deep learning framework for single-cell alternative polyadenylation identification.Journal: Briefings in bioinformaticsIn common: Biopython, Hugging Face Transformers, PyTorch, 2 other tools
- [9] doi:10.64898/2026.03.30.714220 [code]
- An integrated single cell and spatial omics atlas of human prenatal developmentJournal: bioRxiv (preprint)In common: Biopython, Hugging Face Transformers, PyTorch, 2 other tools
- [10] 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 omegaIn common: Biopython, Hugging Face Transformers, PyTorch, 2 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, 20 scripts, and 4 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:ece4a5e71ec8da4f…
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.
