Endogenous precision of the number sense.
The 2 matches
- [1] § Results › Estimation task ↔ Data Analysis Estimation Task.ipynb, lines 163–244 · score 0.64 · Absolute error, relative error, Standard deviation, coefficients, variations, prior width
- [2] § Results › Estimation task ↔ Data Analysis Estimation Task.ipynb, lines 163–244 · score 0.59 · absolute error, relative error, standard deviation, prior width, square
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
Jupyter notebook · 325 lines · 12 KB · no license · 2 matches
- # %%
- # %matplotlib inline
- from matplotlib.pyplot import *
- import numpy as np
- import pandas as pd
- from scipy import stats, optimize
- import itertools, tabulate
- from datetime import datetime
- # %%
- from scipy import __version__ as scipy_version
- print(np.__version__)
- print(scipy_version)
- print(pd.__version__)
- # %%
- import library_estimation as le
- #import apc_utils as apc
- #import statsmodels.api as sm
- # %%
- np.set_printoptions(linewidth=160)
- # %%
- # number spaces for each width
- XS_DELTA = {}
- for delta in le.DELTAS:
- lo, hi = le.RANGES_DELTA[delta]
- xxx = np.arange( int(lo), int(hi) + 1 )
- XS_DELTA[delta] = xxx
- XS_DELTA
- # %%
- def mean_variance_from_pmf(xhats, p_xhats):
- m = (xhats*p_xhats).sum()
- v = (p_xhats*(xhats-m)**2).sum()
- return m,v
- # %% [markdown]
- # # Data
- # %%
- META = pd.read_csv('data_estimation/subjects_info.csv', index_col=0)
- META
- # %%
- DATA = pd.read_csv('data_estimation/data.csv')
- DATA
- # %%
- len(META), DATA.ID.unique().shape
- # %%
- META.Gender.value_counts()
- # %%
- META.Age.mean(), META.Age.std()
- # %%
- META.Laterality.value_counts()
- # %%
- META.Reward.mean(), META.Reward.std()
- # %%
- # def estimate_mean_responses_ols(data):
- # A = np.vstack([data.Number, np.ones(len(data.Number))]).T
- # m, c = np.linalg.lstsq(A, data.Response, rcond=None)[0]
- # xs = np.arange( data.Number.min(), data.Number.max()+1 )
- # return { x : m*x + c for x in xs}
- # def estimate_mean_responses_gaussian_kernel(data, conv_w, xs=None):
- # if xs is None:
- # xs = np.arange(int(data.Number.min()), int(data.Number.max())+1)
- # return dict( zip( *apc.smooth_ys(conv_w, data.Number, data.Response, xs) ) )
- # %% [markdown]
- # ## Figure 1
- # %%
- def get_mean_responses(data):
- xs = np.arange( data.Number.min(), data.Number.max()+1 )
- return {x:data.Response[data.Number==x].mean() for x in xs}
- # %%
- fig = figure(layout="constrained", figsize=(12,9))
- axd = fig.subplot_mosaic(
- """
- ..BCC
- AAACC
- AAADE
- """, height_ratios=[3,1.8,4.2], width_ratios=[.5,.4,1.3,1,1],
- )
- axPrior = axd['B']
- axSTD = axd['A']
- axLIN = axd['C']
- axCV = axd['D']
- axERR = axd['E']
- axERRT = axERR.twinx()
- all_axs = list( axd.values() )
- # priors
- for delta in le.DELTAS:
- data = DATA[DATA.Delta==delta]
- lo, hi = le.RANGES_DELTA[delta]
- c = le.COLORS_DELTA[delta]
- xxx = np.array([lo, hi])
- yyy = stats.uniform(loc=lo, scale=delta).pdf(xxx)
- lbl = le.LABELS_DELTA[delta]
- axPrior.plot(xxx, yyy, c=c, label=lbl)
- axPrior.vlines(xxx, 0, yyy, color=c, ls=':')
- axPrior.text(x=60, y=1/delta-0.004, s=f'$w={delta}$', ha='center', color=c)
- leg = axPrior.legend(loc=(0.725,.6), facecolor='white', framealpha=1, edgecolor='none')
- leg.set_in_layout(False)
- axPrior.set_ylim(0,None)
- axPrior.set_xticks([30,40,50,60,70,80,90])
- axPrior.set_yticks([])
- axPrior.set_xlabel('Number $x$')
- axPrior.set_ylabel('pdf')
- axPrior.set_title('Priors')
- cc1 = 'purple'
- cc2 = '#606060'
- for binned in [False, True]:
- vars_per_delta = {}
- excursions_per_delta = {}
- xs_per_delta = {}
- vars_per_delta = {}
- errors_per_delta = {}
- for delta in reversed(le.DELTAS):
- data = DATA[DATA.Delta==delta]
- lo, hi = le.RANGES_DELTA[delta]
- c = le.COLORS_DELTA[delta]
- bin_edges = le.BINS_EDGES_DELTA[delta] if binned else np.linspace(*le.RANGES_DELTA[delta], delta+2)
- binned_data = le.split_in_bins(data, bin_edges)
- mean_resps = get_mean_responses(data) # { x : mean_xhat }
- excursions = data.apply(lambda row: row.Response - mean_resps[row.Number], axis='columns') # xhat - mean_xhat
- excursions_per_delta[delta] = excursions
- binned_excursions = le.split_in_bins(excursions, bin_edges, xcol=None, xs=data.Number)
- mean_xhats = np.array([da.Response.mean() for da in binned_data])
- vars = np.array( [ (excs**2).mean() for excs in binned_excursions] ); vars_per_delta[delta] = vars
- stds = np.sqrt(vars)
- sesds = np.array( [ le.sesd(excs) for excs in binned_excursions] )
- sevs = np.array( [ le.sev(excs) for excs in binned_excursions] )
- xs = np.array([da.Number.mean() for da in binned_data])
- xs_per_delta[delta] = xs
- cvs = np.array([ stds[i]/m_xhat for i,m_xhat in enumerate(mean_xhats) ])
- vars_per_delta[delta] = vars
- errors_per_delta[delta] = data.Error
- if not binned:
- axSTD.plot(xs, stds, c=c, alpha=.2, clip_on=False)
- axCV.plot(xs, cvs, c=c, alpha=.2, clip_on=False)
- print( delta, stds[xs==60] )
- else:
- axSTD.errorbar(xs, stds, sesds, c=c, label=f'{le.LABELS_DELTA[delta]} prior $(w={delta})$')
- axCV.errorbar(xs, cvs, sesds/xs, c=c)
- ##### MODEL
- ν_1, σ = 1.22553588, 3.62413533
- β = 1/2
- ν = ν_1*delta**(β-1)
- ps = le.probs_xhats_given_x_for_all_xs_MP(lo, delta, ν=ν, σ=σ)
- xs = le.discrete_xs(lo, delta)
- xhats_means = []
- xhats_vars = []
- for x,p_xhats in zip(xs,ps):
- m,v = mean_variance_from_pmf(xs, p_xhats)
- xhats_means.append(m)
- xhats_vars.append(v)
- axSTD.plot(xs, np.sqrt(xhats_vars), c=c, ls=':', zorder=-1000)
- if binned:
- ys = np.array( [ (excursions_per_delta[delta]**2).mean() for delta in le.DELTAS ] )
- yerrs = np.array( [ le.sev(excursions_per_delta[delta]) for delta in le.DELTAS ] )
- line, caps, bars = axLIN.errorbar(le.DELTAS, ys, 2*yerrs, c=cc1, ecolor=[le.COLORS_DELTA[d] for d in le.DELTAS])
- for bar in bars: bar.set_zorder(2000)
- axT = axLIN.twiny()
- line, caps, bars = axT.errorbar(np.array(le.DELTAS)**2, ys, 2*yerrs, c=cc2, ecolor=[le.COLORS_DELTA[d] for d in le.DELTAS])
- for bar in bars: bar.set_zorder(2000)
- pred_vars = vars_per_delta[20] + (vars_per_delta[40]-vars_per_delta[20])*(60**2-20**2)/(40**2-20**2)
- axSTD.plot(xs_per_delta[60], np.sqrt(pred_vars), 'x', c=le.COLORS_DELTA[60])
- pred_vars = vars_per_delta[20] + (vars_per_delta[40]-vars_per_delta[20])*(60-20)/(40-20)
- axSTD.plot(xs_per_delta[60], np.sqrt(pred_vars), 'o', c=le.COLORS_DELTA[60], mfc='none')
- #
- ### Relative error
- if axERR is not None:
- ys = np.array( [ np.abs(errors_per_delta[delta]).mean() for delta in le.DELTAS ] )
- yerrs = np.array( [ stats.sem(np.abs(errors_per_delta[delta])) for delta in le.DELTAS ] )
- line, caps, bars = axERR.errorbar(le.DELTAS, ys, 2*yerrs, color='grey', ecolor=[le.COLORS_DELTA[d] for d in le.DELTAS])
- for bar in bars: bar.set_zorder(2000)
- ys = np.array( [ np.abs(errors_per_delta[delta]).mean()/delta for delta in le.DELTAS ] )
- yerrs = np.array( [ stats.sem(np.abs(errors_per_delta[delta]/delta)) for delta in le.DELTAS ] )
- line, caps, bars = axERRT.errorbar(le.DELTAS, ys, 2*yerrs, color='grey', ecolor=[le.COLORS_DELTA[d] for d in le.DELTAS], ls='--')
- for bar in bars: bar.set_zorder(2000)
- axSTD.set_ylim(0,10.5)
- axSTD.set_xlim(30,90)
- axCV.set_ylim(0,.18)
- axCV.set_xlim(30,90)
- axCV.set_yticks([0,.05,.1,.15])
- for ax in all_axs:
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- axSTD.set_xlabel('Presented number $x$')
- axSTD.set_ylabel('$\\operatorname{StdDev}[\\hat x]$')
- axSTD.set_title('Standard deviation of responses')
- axCV.set_xlabel('Presented number $x$')
- axCV.set_ylabel('$CV = \\operatorname{StdDev}[\\hat x]/\\operatorname{Mean}[\\hat x]$')
- axCV.set_title('Coefficient of Variation')
- #
- leg = axSTD.legend(frameon=False)
- axSTD.legend([Line2D([],[],c='grey',alpha=.2), Line2D([],[],c='grey'), Line2D([],[],c='grey', ls=':'), ],
- ['Subjects', 'Subjects (binned $x$)', 'Model'],
- frameon=False, loc='lower right')
- axSTD.add_artist(leg)
- #
- some_ticks = np.array([4,5,6,7,8])
- axLIN.set_yticks( some_ticks**2, labels=[f'${x}^2$' for x in some_ticks] )
- axLIN.set_xticks(le.DELTAS)
- axT_ticks = np.array(le.DELTAS)
- axT.set_xticks( axT_ticks**2, labels=[f'${x}^2$' for x in axT_ticks] )
- xlabel_ax = axLIN.set_xlabel('Prior width $w$', color=cc1)
- axT.set_xlabel('Squared prior width $w^2$', color=cc2)
- axT.xaxis.set_label_coords(.5, .15)
- axT.spines['bottom'].set_position(('outward', -30))
- axT.xaxis.set_ticks_position('bottom')
- axT.xaxis.set_label_position('bottom')
- axLIN.tick_params(axis='x', colors=cc1)
- axT.tick_params(axis='x', colors=cc2)
- axLIN.spines['bottom'].set_color(cc1)
- axT.spines['bottom'].set_color(cc2)
- axT.spines['top'].set_visible(False)
- axT.spines['right'].set_visible(False)
- axLIN.set_ylabel('$\\operatorname{Var}[\\hat x]$')
- axLIN.set_ylim(0,8**2)
- axLIN.set_title('Variance vs. $w$ and $w^2$')
- ##
- if axERR is not None:
- axERRT.spines['top'].set_visible(False)
- axERR.spines['right'].set_ls((0,(8,5))); axERRT.spines['right'].set_ls((0,(8,5)))
- axERR.set_xticks(le.DELTAS)
- xlabel_ax = axERR.set_xlabel('Prior width $w$')
- axERR.set_ylabel('Absolute error $|\\hat x - x|$'); axERRT.set_ylabel('Relative error $|\\hat x - x| / w$')
- axERR.set_title('Absolute and relative error')
- axERR.set_ylim(0,None); axERRT.set_ylim(0,None)
- axERRT.yaxis.set_major_formatter(matplotlib.ticker.PercentFormatter(1., decimals=0))
- axERR.legend([Line2D([],[],c='grey'),Line2D([],[],c='grey',ls='--')], ['Absolute error', 'Relative error'], frameon=False)
- #fig.subplots_adjust(hspace=.25)
- fig.savefig('figures/result_estimation_task.pdf', bbox_inches='tight')
- # %%
- # %% [markdown]
- # ## Tests of equality of variances
- # %% [markdown]
- # ### across bins
- # %%
- for delta in le.DELTAS:
- data = DATA[DATA.Delta==delta]
- nbbins = 5
- bin_edges = le.BINS_EDGES_DELTA[delta]
- binned_data = le.split_in_bins(data, bin_edges)
- mean_resps = get_mean_responses(data) # { x : mean_xhat }
- excursions = data.apply(lambda row: row.Response - mean_resps[row.Number], axis='columns')
- binned_excursions = le.split_in_bins(excursions, bin_edges, xs=data.Number, xcol=None)
- print(f'w={delta}')
- print('Bins 3,4', stats.levene(*[binned_excursions[i] for i in [2,3]]).pvalue)
- print( [binned_excursions[i].std() for i in [2,3]] )
- print('Bins 3,5', stats.levene(*[binned_excursions[i] for i in [2,4]]).pvalue)
- print( [binned_excursions[i].std() for i in [2,4]] )
- print()
- # %% [markdown]
- # ### across conditions
- # Narrow-Medium, Narrow-Wide, Medium-Narrow
- # %% [markdown]
- # The Levene's test of equality of variances in the three conditions rejects the null hypothesis.
- # %%
- #### With each number in the narrowest range
- excs_per_x_per_delta = {}
- xs = np.arange(50,71)
- pvs = []
- for x in xs:
- excs_per_x_per_delta[x] = {}
- for delta in reversed(le.DELTAS):
- data = DATA[(DATA.Delta==delta) & (DATA.Number==x)]
- excursions = data.Response - data.Response.mean()
- excs_per_x_per_delta[x][delta] = excursions
- pvs.append(stats.levene(*excs_per_x_per_delta[x].values()).pvalue)
- print(x, pvs[-1] )
- np.max(pvs), np.median(pvs)
- # %% [markdown]
- # # Model
- # %% [markdown]
- # $x \in [a, a+w]$, where $a = 60 - w/2$.
- # $p(r|x) = N(r; \frac{x-a}{w}, \nu_w^2)$, with $\nu_w = \nu_1 w^{\beta-1}$. Note that this is slightly different than in the paper (but it is equivalent).
- # The Fisher information is $I(x) = \left( \frac{1}{w\nu_w} \right)^2 = \left( \frac{1}{\nu_1 w^{\beta} } \right)^2 = \frac{1}{\nu_1^2 w^{2\beta} }.$
- # The Bayesian estimate, given $r$, is
- # $$x_B(r) = a + wr + w\nu\frac{\varphi(\frac{r}{\nu}) - \varphi(\frac{r-1}{\nu})}{\Phi(\frac{r}{\nu}) - \Phi(\frac{r-1}{\nu})}.$$
- # With truncated motor noise:
- # $y = [x_B(r) + \sigma \varepsilon]_{[a,a+w]}$, where $\varepsilon \sim N(0,1)$.
- # Responses are integers. We round:
- # $\hat x = round(y)$.
- #
- # This model has 2 or 3 parameters: $\nu_1$, $\sigma$, and $\beta$ which can be fixed or allowed to take any value.
- # %%
- # This function computes the log-likelihood of `data` for delta, ν_1, σ, and β defined as above.
- le.LL_data_w_params(data=DATA, delta=20, ν_1=.3, σ=3, β=1)
- # %%
- def make_x0s(bounds, n=2):
- spaces = [ [x,] if np.isscalar(x) else np.linspace(x[0],x[1],n) for x in bounds ]
- return list( itertools.product(*spaces) )
- # %%
- # TO BE COMPLETED
- # %%
Data Analysis Estimation Task.ipynb, no license · at the source
Overview
Abstract
The behavioral variability in psychophysical experiments and the stochasticity of sensory neurons have revealed the inherent imprecision in the brain’s representations of environmental variables. Numerosity studies yield similar results, pointing to an imprecise ‘number sense’ in the brain. If the imprecision in representations reflects an optimal allocation of limited cognitive resources, as suggested by efficient-coding models, then it should depend on the context in which representations are elicited. Through an estimation task and a discrimination task, both involving numerosities, we show that the scale of subjects’ imprecision increases, but sublinearly, with the width of the prior distribution from which numbers are sampled. This sublinear relation is notably different in the two tasks. The double dependence of the imprecision — both on the prior and on the task — is consistent with the optimization of a tradeoff between the expected reward, different for each task, and a resource cost of the encoding neurons’ activity. Comparing the two tasks allows us to clarify the form of the resource constraint. Our results suggest that perceptual noise is endogenously determined, and that the precision of percepts varies both with the context in which they are elicited and with the observer’s objective.
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 2 matches between paragraphs and lines of code.
OSF d6k3m
Availability: 1 check, the latest on 29 September 2026: the link answers (HTTP 200)
- 29 September 2026: the link answers (HTTP 200)
4 files
- Data Analysis Discrimination Task.ipynb, Jupyter, 281 lines
- Data Analysis Estimation Task.ipynb, Jupyter, 325 lines, 2 matches
- library_discrimination.p
y , Python, 51 lines - library_estimation.py, Python, 320 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;
- 4 scripts, each with its path and the digest of its content;
- 2 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
Datasets cited
- doi:10.7910/
dvn/ , at the source; found in the referencespyqxad
Data availability
The data and the code are available at https://
The following dataset was generated:
Prat-Carrabin A, Woodford M. 2024. Endogenous Precision of the Number Sense: Data & Code. Open Science Framework. d6k3m
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, 29 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 2 authors, 1 keyword, 3 MeSH terms, 1 funder, 55 references.
Cite
This paper
Prat-Carrabin, A., & Woodford, M. (2026). Endogenous precision of the number sense. eLife, 13, RP101277. https://
BibTeX
@article{pratcarrabin202
author = {Prat-Carrabin, Arthur and Woodford, Michael},
title = {{Endogenous precision of the number sense}},
journal = {eLife},
year = {2026},
month = apr,
volume = {13},
pages = {RP101277},
publisher = {eLife Sciences Publications, Ltd},
issn = {2050-084X},
doi = {10.7554/
url = {https://
pmid = {41983455},
pmcid = {PMC13082791}
}
RIS
TY - JOUR
AU - Prat-Carrabin, Arthur
AU - Woodford, Michael
TI - Endogenous precision of the number sense
T2 - eLife
J2 - eLife
PY - 2026
DA - 2026/
VL - 13
SP - RP101277
SN - 2050-084X
PB - eLife Sciences Publications, Ltd
DO - 10.7554/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.7554/
"type": "article-journal",
"title": "Endogenous precision of the number sense",
"container-title": "eLife",
"author": [
{
"family": "Prat-Carrabin",
"given": "Arthur"
},
{
"family": "Woodford",
"given": "Michael"
}
],
"container-title-short":
"volume": "13",
"page": "RP101277",
"DOI": "10.7554/
"PMID": "41983455",
"PMCID": "PMC13082791",
"ISSN": "2050-084X",
"publisher": "eLife Sciences Publications, Ltd",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
15
]
]
}
}
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-73032-0 [code]
- Fast efficient coding and sensory adaptation in gain-adaptive recurrent networks.Journal: Nature communicationsIn common: SciPy, Matplotlib, NumPy, 11 references, author Arthur Prat-Carrabin
- [2] doi:10.1371/journal.pcbi.1014251 [code]
- Distilling noise characteristics and prior expectations in multisensory causal inference.Journal: PLoS computational biologyIn common: cognitive, 5 references
- [3] doi:10.1162/netn.a.544 [code]
- Brain network reconfiguration during reward prediction error processing.Journal: Network neuroscience (Cambridge, Mass.)In common: statsmodels, pandas, SciPy, 2 other tools, cognitive, 2 references
- [4] doi:10.1038/s41593-026-02255-7 [code]
- Neural circuits encode prior knowledge of temporal statistics.Journal: Nature neuroscienceIn common: pandas, SciPy, Matplotlib, 1 other tool, 3 references
- [5] doi:10.1371/journal.pbio.3003829 [code]
- Identity-specific reward expectations in orbitofrontal cortex guide goal-directed choices.Journal: PLoS biologyIn common: statsmodels, pandas, SciPy, 2 other tools, cognitive, 1 reference
- [6] doi:10.1162/imag.a.1207 [code]
- Investigating the temporal dynamics and modeling of mid-level feature representations in humans.Journal: Imaging neuroscience (Cambridge, Mass.)In common: statsmodels, pandas, SciPy, 2 other tools, cognitive, 1 reference
- [7] doi:10.1111/psyp.70397 [code]
- Cortical Contributions to Attentional Orienting and Response Cancellation in Action Stopping.Journal: PsychophysiologyIn common: statsmodels, pandas, SciPy, 2 other tools, cognitive, 1 reference
- [8] doi:10.1038/s41467-026-71568-9 [code]
- Convergent and selective representations of pain, appetitive processes, aversive processes, and cognitive control in the insula.Journal: Nature communicationsIn common: statsmodels, pandas, SciPy, 2 other tools, cognitive, 1 reference
- [9] doi:10.1016/j.isci.2026.117375 [code]
- Motor priming is associated with widespread recruitment into neural ensembles and more rapid ensemble transitions.Journal: iScienceIn common: statsmodels, pandas, SciPy, 2 other tools, 1 reference
- [10] doi:10.7554/elife.108109 [code]
- Multimodal MRI marker of cognition explains the association between cognition and mental health in the UK Biobank.Journal: eLifeIn common: pandas, SciPy, Matplotlib, 1 other tool, cognitive, 1 reference
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, 4 scripts, and 2 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:db034af780b9198e…
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
[.
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.
