mne_rt.protocols.ShamProtocol#

class mne_rt.protocols.ShamProtocol(inner: Any, sham_rate: float = 0.5, buffer_len: int = 60, rng_seed: int | None = None)#

Bases: object

Wraps any NF protocol with sham (double-blind) feedback.

In real-time NF, feedback is issued on every incoming window (e.g. every 1 second). ShamProtocol intercepts those feedback values and on sham_rate fraction of windows replaces the real reward with a randomly shuffled historical value drawn from a rolling buffer, creating placebo/sham feedback windows indistinguishable from real ones.

This enables within-session double-blind designs for neurofeedback RCTs without a separate sham session.

Parameters:
innerany protocol with evaluate(value) -> (bool, float)

The real protocol to wrap (ThresholdProtocol, ZScoreProtocol, etc.).

sham_ratefloat, default 0.5

Fraction of windows that receive sham feedback (0–1). 0.0 = never sham; 1.0 = always sham.

buffer_lenint, default 60

Number of historical real-reward values to keep in the sham pool.

rng_seedint | None, default None

Random seed for reproducibility.

Attributes:
n_realint

Number of real feedback windows so far.

n_shamint

Number of sham feedback windows so far.

sham_loglist[bool]

Per-window sham flag (True = was sham).

Raises:
ValueError

If sham_rate is not in [0, 1] or buffer_len < 1.

Examples

Wrap an existing protocol so 50 % of windows receive sham feedback:

from mne_rt.protocols import ZScoreProtocol
from mne_rt.protocols.sham import ShamProtocol

inner = ZScoreProtocol(direction="up")
proto = ShamProtocol(inner, sham_rate=0.5, rng_seed=42)
for value in nf_stream:
    crossed, magnitude = proto.evaluate(value)
    # On sham windows, (crossed, magnitude) comes from a historical draw.

Added in version 1.0.0.

__init__(inner: Any, sham_rate: float = 0.5, buffer_len: int = 60, rng_seed: int | None = None) None[source]#

Methods

__init__(inner[, sham_rate, buffer_len, ...])

evaluate(value)

Evaluate one NF value and return (crossed, magnitude).

reset()

Reset sham counters, log, and buffer; also resets the inner protocol.

Attributes

sham_fraction

Observed sham fraction so far (0–1).

evaluate(value: float) tuple[bool, float][source]#

Evaluate one NF value and return (crossed, magnitude).

Always delegates to the inner protocol first so that its state (running statistics, adaptive threshold, etc.) advances correctly. On sham_rate fraction of calls the real result is silently discarded and a randomly-drawn historical value is returned instead.

Parameters:
valuefloat

Current NF feature value.

Returns:
crossedbool

True if the criterion was met. On sham windows this value was drawn from the historical buffer, not the current signal.

magnitudefloat

Non-negative reward magnitude. On sham windows this value was drawn from the historical buffer.

reset() None[source]#

Reset sham counters, log, and buffer; also resets the inner protocol.

The inner protocol’s own reset() is called if it provides one. Buffer is re-seeded with zeros.

property sham_fraction: float#

Observed sham fraction so far (0–1).

Returns 0.0 when no evaluations have been recorded.