rcicr 1.2.0
Upgrading from the CRAN version? The last release on CRAN was 0.3.4.1, before the package
was archived in 2021. 1.0.1 and 1.1.0 were GitHub-only releases made in the meantime, so the
1.1.0 section below applies to you too — it is where the bulk of the bug fixes are.
Reproducibility impact
-
generateCI(zmap = TRUE)blurred z-maps with the wrong sigma, for stimulus sets
generated with 1.1.0 only.generateCI()reads the stimulus set withload(), which
assigns into the function's own frame, and 1.1.0 began storing the noisesigmathere —
the same name as the z-map blur argument. The saved value replaced the argument, so z-maps
were blurred with 25 rather than the documented 3, and passingsigmadid nothing.In practice this affects nobody. 1.1.0 was a GitHub tag that stood for about a day and
was never on CRAN, so almost no stimulus set carries the field that triggers it. Files from
1.0.1 and earlier have nosigmaat all and were never affected. Only z-maps are involved
— classification images, scaling, InfoVal and every saved number are untouched. It is
recorded here because it did change a number, and because if you are the one person who
generated stimuli that day, regenerating the z-map is a one-line rerun.Every argument is now kept across the
load(), so a field added to the.Rdatalater
cannot quietly capture another one. Found bytools/compare-release-output.R, the release
gate introduced in this version — the first bug it caught.
Behaviour change
-
plotZmap(mask = ...)now actually masks the z-map. The argument has been documented
since 2016 — "if a cell evaluates to TRUE, the corresponding zmap pixel will be masked" —
and until now it did nothing at all: the mask was read from its PNG or matrix, checked
against the z-map's dimensions, validated as binary, converted to boolean, and then
discarded before plotting. A correct mask produced an unmasked z-map, with no error and no
warning. Masked cells are now dropped from the z-map exactly as sub-threshold cells are.Who is affected. Only direct calls to
plotZmap(mask = ...).generateCI()does not
passmasktoplotZmap()— it masks the classification image itself, via a separate and
working code path — so z-maps produced through the normal pipeline are unchanged, and no
stored numbers change anywhere. If you have been passing a mask and your z-maps looked
unmasked, that is why; they will now come out masked, and the earlier images were wrong
about which regions carry signal.A second bug is fixed alongside: the conversion to boolean set every cell to
FALSE
whatever you passed, so even once applied the mask would have masked nothing. -
The
maskconvention is documented correctly for the first time, in both
?plotZmapand?generateCI. Both said a matrix masks where the value is1/TRUE
while a PNG masks where it is black (0) — two opposite conventions in one sentence.
generateCI()'s implementation has always masked where the value is0, for a matrix and
a PNG alike, so the matrix half of the documentation was simply wrong. The code is
unchanged and the documentation now matches it, since existing masks were built against
the behaviour, not the prose.plotZmap()follows the same single convention, so one mask
can be passed to both functions — which is now asserted by a test rather than assumed.If you built a mask by reading
?generateCIrather than by looking at your output, check
it:0/black/FALSEis the region that gets masked away.
New features
-
generateReferenceDistribution2IFC()andcomputeInfoVal2IFC()gained a
response_seedargument, so the null distribution InfoVal is scored against can be
varied deliberately. Until now there was no way to draw a second, independent null from
the same stimuli — which meant you could not check how much Monte Carlo error your choice
ofiterwas leaving in your InfoVal.response_seedseeds the simulated responses only;
the stimuli, and so the noise basis the null is built on, are untouched.Existing calls are unaffected. The default (
NULL) issues noset.seed()call at
all, so the reference distribution is byte-identical to what earlier versions produced.
Verified against norms generated before the change, not merely assumed.In
computeInfoVal2IFC(), passingresponse_seedforces the reference distribution to be
regenerated even when the.Rdatafile already holds one, and the result is deliberately
not written back — a one-off check of the Monte Carlo error cannot silently become the
number every later analysis of that stimulus set reports. -
generateReferenceDistribution2IFC()gainedsave_rdata(defaultTRUE, i.e. unchanged)
and now returns the reference distribution invisibly instead of returning nothing, so the
norms are reachable when you ask it not to write them to the.Rdatafile. -
The
.Rdatafile gained areference_norms_seedfield recording theresponse_seedthe
storedreference_normswere generated with (NULLfor the default). Purely additive;
files written by earlier versions simply lack it. A stimulus set carrying a deliberately
varied null is no longer indistinguishable from one carrying the default.
Bug fixes
-
autoscale()works on masked classification images.generateCI(mask = ...)sets masked
pixels toNAby design, andautoscale()took a barerange()over them, so the scaling
constant becameNAand the call died withmissing value where TRUE/FALSE needed. The
scaling constant is now computed from the unmasked pixels, exactly asgenerateCI()'s own
scaling has always done, and masked pixels stay masked in the result. A CI that is
entirelyNAnow raises an error naming the CI instead of failing the same opaque way. -
generateCI()accepts a pre-0.3.0.Rdatafile when computing a CI from a single
trial. rcicr 0.3.0 stopped drawing four random contrasts per trial that no patch index
ever referred to (4096 → 4092), andgenerateCI()has truncated older files ever since —
but the single-trial branch tested for a length of 4092 and then truncated to 4092, a
no-op that could never fire on the 4096-parameter input it existed for. Such a call failed
withStimulus generation aborted: number of parameters doesn't equal number of patches!.
The multi-trial path was always correct and is unchanged. -
computeInfoVal2IFC()andgenerateReferenceDistribution2IFC()work on.Rdatafiles
written beforenoise_typewas saved (#94). Such a file failed outright with
object 'noise_type' not found, and the workaround on record was to load the file and
assign the variable by hand. It now falls back tosinusoidwith a loud warning, matching
hownscalesis handled — a warning rather than a silent default, because guessing wrong
means the null is built on a different kind of noise than participants saw, and the
resulting InfoVal would be wrong. Files written by 1.1.0 or later already store the field
and are unaffected. -
generateStimuli2IFC(return_as_dataframe = TRUE)shows its progress bar (#82). The
returnhanding back each trial's noise exits the entire loop body, so it jumped past the
progress-bar update and the bar sat at zero for the whole run — on the slowest path there
is, sincegenerateReferenceDistribution2IFC()takes it for every InfoVal. -
The
.Rdatafile written bygenerateStimuli2IFC()now records the rcicr version that
actually wrote it (#29).generator_versionwas a hardcoded'0.4.0'string from 2016
onwards, so every file produced by 0.4.0 through 1.1.0 claims to come from 0.4.0 —
useless for the provenance the field exists for, and it disagreed with
p$generator_version, which held the real version all along.No result changes: nothing in the package has ever read this field. If your own code
does, note two things. Existing files cannot be trusted to say what wrote them, so treat
a stored'0.4.0'as "unknown, somewhere between 0.4.0 and 1.1.0" rather than as a
version. And the field is now apackage_versionobject rather than a character string,
so compare withutils::packageVersion()ornumeric_version(), never as text —
'0.10.0' < '0.4.0'isTRUEwhen compared as strings.
Documentation
-
README.mdnow describes the package's architecture and, more usefully, the anatomy of
the.Rdatafile field by field. That file is the only link between stimulus generation
and analysis — nothing about a stimulus set is recoverable without it — and until now its
contents were documented nowhere a user would look. -
A
CONTRIBUTING.mdsets out how to work on the package, leading with the constraint that
makes it unusual: researchers re-run old analysis scripts years later and publish the
results, so numeric output must not change silently. -
?generateStimuli2IFCdocuments a restriction onreturn_as_dataframe = TRUE: the frame
holds one noise image per trial, so it is meaningful only under the default
use_same_parameters = TRUE. Withuse_same_parameters = FALSEand more than one base
image, only the first base image's noise comes back — the frame's shape cannot represent
trial × base image. Behaviour is unchanged, and the files written to disk were never
affected; the restriction simply was not stated. -
?generateReferenceDistribution2IFCnow documents as a guarantee what was previously
only true by accident: with the defaultresponse_seed, the reference distribution — and
therefore InfoVal — is reproducible from the stimulus.Rdatafile alone, independent of
the calling session's random number state and ofncores. This held before, but nobody
had chosen it: it is a consequence ofgenerateStimuli2IFC()'s internalset.seed()
landing before the simulation draws. That call now carries a comment saying what depends
on it, so it is not moved casually.