|
378 | 378 | # print("-------------------------------------------------") |
379 | 379 | # print('[GSP]', next_heading_gsp) |
380 | 380 |
|
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)) |
382 | 389 | if model.gsp_neighbors: |
383 | 390 | states, state_prox_flags = model.make_gsp_states(old_agent_prox_flags, neighbors_old_heading_gsp, True) |
384 | 391 | new_states = model.make_gsp_states(agent_prox_flags, old_heading_gsp) |
385 | 392 | 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: |
387 | 394 | if model.gsp_networks['learning_scheme'] == 'attention': |
388 | 395 | model.store_gsp_transition(states[i], label, 0, 0, 0) |
389 | 396 | else: |
390 | | - # 2nd arg = label (supervised target for direct-MSE GSP training) |
391 | 397 | state = states[i] |
392 | 398 | new_state = new_states[i] |
393 | 399 | model.store_gsp_transition(state, label, 0, new_state, 0) |
394 | 400 | 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 |
399 | 401 | states = model.make_gsp_states_broadcast(old_agent_prox_flags, neighbors_old_heading_gsp) |
400 | 402 | new_states = model.make_gsp_states_broadcast(agent_prox_flags, old_heading_gsp) |
401 | 403 | 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: |
406 | 405 | model.store_gsp_transition(states[i], label, 0, new_states[i], 0) |
407 | 406 | else: |
408 | 407 | 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': |
413 | 411 | 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: |
419 | 413 | new_state = np.array(agent_prox_flags) |
420 | 414 | 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: |
426 | 416 | new_state = np.array(agent_prox_flags) |
427 | 417 | model.store_gsp_transition(state, label, 0, new_state, 0) |
428 | 418 |
|
|
0 commit comments