Skip to content

Commit a705dfd

Browse files
authored
Merge pull request #17 from NESTLab/feat/gsp-store-force-filter
feat(gsp): filter replay-buffer stores by per-robot force magnitude
2 parents d452013 + 59343aa commit a705dfd

2 files changed

Lines changed: 24 additions & 25 deletions

File tree

rl_code/Main.py

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -378,51 +378,41 @@
378378
# print("-------------------------------------------------")
379379
# print('[GSP]', next_heading_gsp)
380380

381-
# Store GSP Transition
381+
# Store GSP Transition — guard by per-robot force magnitude.
382+
# GSP_STORE_FORCE_THRESHOLD concentrates training on samples where
383+
# the robot is actively applying force (top ~25% of samples at
384+
# threshold ~4.0), which multiplies the linear-R² ceiling of the
385+
# prediction problem 3–4× (see
386+
# docs/research/2026-04-13-gsp-ddpg-vs-attention-collapse.md).
387+
# 0.0 = filter disabled (legacy behavior).
388+
force_thr = float(config.get('GSP_STORE_FORCE_THRESHOLD', 0.0))
382389
if model.gsp_neighbors:
383390
states, state_prox_flags = model.make_gsp_states(old_agent_prox_flags, neighbors_old_heading_gsp, True)
384391
new_states = model.make_gsp_states(agent_prox_flags, old_heading_gsp)
385392
for i in range(Utility.params['num_robots']):
386-
if np.sum(state_prox_flags[i]) > 0:
393+
if np.sum(state_prox_flags[i]) > 0 and stats[i][0] > force_thr:
387394
if model.gsp_networks['learning_scheme'] == 'attention':
388395
model.store_gsp_transition(states[i], label, 0, 0, 0)
389396
else:
390-
# 2nd arg = label (supervised target for direct-MSE GSP training)
391397
state = states[i]
392398
new_state = new_states[i]
393399
model.store_gsp_transition(state, label, 0, new_state, 0)
394400
elif model.gsp_broadcast:
395-
# GSP-B per-agent storage with broadcast inputs.
396-
# state_t : broadcast view at previous step (uses neighbors_old_heading_gsp so
397-
# the prev_gsp slot reflects the prediction from the previous tick)
398-
# state_{t+1}: broadcast view at current step
399401
states = model.make_gsp_states_broadcast(old_agent_prox_flags, neighbors_old_heading_gsp)
400402
new_states = model.make_gsp_states_broadcast(agent_prox_flags, old_heading_gsp)
401403
for i in range(Utility.params['num_robots']):
402-
# Gate on self-prox being non-zero so we only store informative transitions,
403-
# matching the GSP and GSP-N branches. Self-prox lives at index 0 under the
404-
# self-first layout.
405-
if states[i][0] != 0:
404+
if states[i][0] != 0 and stats[i][0] > force_thr:
406405
model.store_gsp_transition(states[i], label, 0, new_states[i], 0)
407406
else:
408407
for i in range(Utility.params['num_robots']):
409-
if model.gsp_networks['learning_scheme'] == 'attention':
410-
state = np.array(old_agent_prox_flags)
411-
# only store the state if it has value
412-
if np.sum(state) > 0:
408+
state = np.array(old_agent_prox_flags)
409+
if np.sum(state) > 0 and stats[i][0] > force_thr:
410+
if model.gsp_networks['learning_scheme'] == 'attention':
413411
model.store_gsp_transition(state, label, 0, 0, 0)
414-
elif args.independent_learning:
415-
state = np.array(old_agent_prox_flags)
416-
# only store the state if it has value
417-
if np.sum(state) > 0:
418-
# 2nd arg = label (supervised target for direct-MSE GSP training)
412+
elif args.independent_learning:
419413
new_state = np.array(agent_prox_flags)
420414
models[i].store_gsp_transition(state, label, 0, new_state, 0)
421-
else:
422-
state = np.array(old_agent_prox_flags)
423-
# only store the state if it has value
424-
if np.sum(state) > 0:
425-
# 2nd arg = label (supervised target for direct-MSE GSP training)
415+
else:
426416
new_state = np.array(agent_prox_flags)
427417
model.store_gsp_transition(state, label, 0, new_state, 0)
428418

run_baseline_experiments.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ def make_config(exp_name, gsp, neighbors, num_obstacles, use_gate, gate_curricul
162162
"GSP_LEARNING_FREQUENCY": 4,
163163
"LEARN_EVERY": 4,
164164
"GSP_BATCH_SIZE": 256,
165+
# Per-robot force_magnitude threshold for GSP replay buffer store filter.
166+
# 0.0 = disabled (store every transition with prox activity, legacy behavior).
167+
# > 0 = only store transitions where stats[i][0] (force_magnitude) exceeds
168+
# the threshold. This concentrates GSP training on samples where the robot
169+
# is actively applying force, which empirically multiplies the linear-R²
170+
# ceiling of the prediction problem 3–4× (see
171+
# docs/research/2026-04-13-gsp-ddpg-vs-attention-collapse.md in Stelaris).
172+
# Recommended starting point: ~4.0 (≈ p75 of force_magnitude in 2-obstacle runs).
173+
"GSP_STORE_FORCE_THRESHOLD": 0.0,
165174
}
166175

167176

0 commit comments

Comments
 (0)