summaryrefslogtreecommitdiff
path: root/experiments/verify_theory.py
diff options
context:
space:
mode:
Diffstat (limited to 'experiments/verify_theory.py')
-rw-r--r--experiments/verify_theory.py41
1 files changed, 41 insertions, 0 deletions
diff --git a/experiments/verify_theory.py b/experiments/verify_theory.py
index 21a7c06..c00a091 100644
--- a/experiments/verify_theory.py
+++ b/experiments/verify_theory.py
@@ -348,6 +348,46 @@ def check_kolen_pollack_difference_dynamics():
assert maximum_error < 3e-15
+def check_bci_error_derivative_structure():
+ """Separate causal role from temporal performance innovation."""
+ rng = np.random.default_rng(641)
+ role = np.concatenate((np.full(5, 0.2), np.full(5, -0.2),
+ np.zeros(30)))
+ examples = 200_000
+ xi = 2.0 * rng.integers(0, 2, size=(examples, role.size)) - 1.0
+ cursor_direction = xi @ role
+ role_estimate = (cursor_direction[:, None] * xi).mean(axis=0)
+ role_error = float(np.abs(role_estimate - role).max())
+ role_cosine = float(role_estimate @ role / (
+ np.linalg.norm(role_estimate) * np.linalg.norm(role)))
+
+ # Positive target error throughout the BCI trial. Improving events have
+ # smaller current error; worsening events have larger current error.
+ previous = np.asarray((0.8, 0.7, 0.6, 0.5, 0.4, 0.3))
+ current = np.asarray((0.6, 0.5, 0.4, 0.7, 0.6, 0.5))
+ improvement = previous - current
+ improving = improvement > 0
+ worsening = improvement < 0
+
+ def sign_index(modulator):
+ residual = modulator[:, None] * role[None, :]
+ difference = residual[:, :5].mean(1) - residual[:, 5:10].mean(1)
+ return 0.5 * (difference[improving].mean()
+ - difference[worsening].mean())
+
+ instantaneous_index = float(sign_index(current))
+ temporal_difference_index = float(sign_index(improvement))
+ print("\nBCI ERROR-DERIVATIVE STRUCTURE")
+ print(f"role_estimator_cosine={role_cosine:.9f} "
+ f"max_error={role_error:.3e} "
+ f"instantaneous_index={instantaneous_index:+.6f} "
+ f"td_index={temporal_difference_index:+.6f}")
+ assert role_cosine > 0.999
+ assert role_error < 0.005
+ assert instantaneous_index < 0.0
+ assert temporal_difference_index > 0.0
+
+
def main():
check_simultaneous_variance()
check_sigma_bias()
@@ -359,6 +399,7 @@ def main():
check_dynamic_neutral_projection()
check_intermittent_feedback_tracking()
check_kolen_pollack_difference_dynamics()
+ check_bci_error_derivative_structure()
print("\nALL THEORY CHECKS PASSED")