Lukeetah commited on
Commit
348b2f6
·
verified ·
1 Parent(s): 9cb0d1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -34
app.py CHANGED
@@ -283,52 +283,145 @@ def _ensure_min_events(sequence, event_check_func, min_proportion, generator_fun
283
  return current_sequence, False
284
  else: return current_sequence, True
285
 
286
- # Attention Sequence Generator (Unchanged from v8.1)
287
  def generate_attention_sequence(params):
288
- n = params['trials']; cue = params['cue']; target = params['target']
289
- similar_distractors = params['similar_distractors']; other_distractors = ATTN_OTHER_DISTRACTORS
290
- p_ax = params['prob_A_then_X']; p_ao = params['prob_A_then_Other']
291
- p_x = params['prob_X_alone']; p_s = params['prob_Similar_alone']; p_o = params['prob_Other_distractor']
 
 
 
 
 
 
 
 
 
 
292
  total_p = p_ax + p_ao + p_x + p_s + p_o
293
  if abs(total_p - 1.0) > 1e-6:
294
- if total_p > 1e-9: p_ax /= total_p; p_ao /= total_p; p_x /= total_p; p_s /= total_p; p_o /= total_p
295
- final_sequence = []
 
 
 
 
296
  def _generate_single_pass():
297
- nonlocal final_sequence; sq = []; last_stim = None
 
 
298
  for i in range(n):
299
- chosen_stim = None; r = random.random(); cdf = 0.0
 
 
 
 
300
  if r < (cdf := cdf + p_ax):
301
- if last_stim != cue:
302
- if cue != last_stim: sq.append(cue); last_stim = cue
303
- else: chosen_stim = target; \
304
- if random.random() < p_s / (p_s + p_x + p_o + 1e-9): chosen_stim = random.choice(similar_distractors); \
305
- elif random.random() < p_o / (p_o + p_x + 1e-9): chosen_stim = random.choice(other_distractors)
306
- if chosen_stim is None: chosen_stim = target
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  elif r < (cdf := cdf + p_ao):
 
308
  distractor_after_a = random.choice(similar_distractors + other_distractors)
309
  if last_stim != cue:
310
- if cue != last_stim: sq.append(cue); last_stim = cue
311
- else: pool = [target] + similar_distractors + other_distractors; chosen_stim = random.choice([s for s in pool if s != last_stim]); \
312
- if not chosen_stim: chosen_stim = random.choice(pool)
313
- if chosen_stim is None: chosen_stim = distractor_after_a; \
314
- if chosen_stim == cue: pool = [target] + similar_distractors + other_distractors; chosen_stim = random.choice([s for s in pool if s != cue]); \
315
- if not chosen_stim: chosen_stim = random.choice(pool)
316
- elif r < (cdf := cdf + p_x): chosen_stim = target
317
- elif r < (cdf := cdf + p_s): chosen_stim = random.choice(similar_distractors)
318
- else: chosen_stim = random.choice(other_distractors)
319
- if chosen_stim == last_stim and chosen_stim not in [cue, target]: pool = similar_distractors + other_distractors; available = [d for d in pool if d != last_stim]; chosen_stim = random.choice(available) if available else chosen_stim
320
- if len(sq) < n: sq.append(chosen_stim); last_stim = chosen_stim
321
- final_sequence = sq[:n]; return final_sequence
322
- def check_ax_pairs(seq, index, item): return index > 0 and item == target and seq[index - 1] == cue
323
- def check_similar_alone(seq, index, item): return item in similar_distractors and (index == 0 or seq[index - 1] != cue)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  generated_sequence = _generate_single_pass()
325
- sequence_ax_ok, success_ax = _ensure_min_events(generated_sequence, check_ax_pairs, ATTN_MIN_PAIR_PROPORTION if p_ax > 0 else 0, _generate_single_pass)
326
- if not success_ax: print(f"WARN: Failed to ensure minimum A->X pairs ({ATTN_MIN_PAIR_PROPORTION*100:.1f}%).")
327
- final_sequence, success_sa = _ensure_min_events(sequence_ax_ok, check_similar_alone, ATTN_MIN_SIMILAR_ALONE_PROPORTION if p_s > 0 else 0, _generate_single_pass)
328
- if not success_sa: print(f"WARN: Failed to ensure minimum Similar Alone pairs ({ATTN_MIN_SIMILAR_ALONE_PROPORTION*100:.1f}%).")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  final_ax_count = sum(1 for i, item in enumerate(final_sequence) if check_ax_pairs(final_sequence, i, item))
330
  min_ax_needed = int(n * ATTN_MIN_PAIR_PROPORTION) if p_ax > 0 else 0
331
- if final_ax_count < min_ax_needed: print(f"WARN: Final Attn sequence low on A->X pairs ({final_ax_count}/{min_ax_needed}) after S-Alone check.")
 
 
 
332
  ex = {'target': target, 'cue': cue, 'similar_distractor_key': params['similar_distractor_key']}
333
  return final_sequence, ex
334
 
 
283
  return current_sequence, False
284
  else: return current_sequence, True
285
 
286
+ # --- Modified Attention Sequence Generator (v8.2 - FIX SyntaxError) ---
287
  def generate_attention_sequence(params):
288
+ n = params['trials']
289
+ cue = params['cue']
290
+ target = params['target']
291
+ similar_distractors = params['similar_distractors']
292
+ other_distractors = ATTN_OTHER_DISTRACTORS
293
+
294
+ # Get probabilities from params (calculated in get_difficulty_params)
295
+ p_ax = params['prob_A_then_X']
296
+ p_ao = params['prob_A_then_Other'] # Note: A->Other can include Similar or Other distractors
297
+ p_x = params['prob_X_alone']
298
+ p_s = params['prob_Similar_alone']
299
+ p_o = params['prob_Other_distractor']
300
+
301
+ # Normalize just in case they don't perfectly sum to 1 due to floating point
302
  total_p = p_ax + p_ao + p_x + p_s + p_o
303
  if abs(total_p - 1.0) > 1e-6:
304
+ # print(f"WARN: Attention probabilities sum to {total_p:.4f}, normalizing.")
305
+ if total_p > 1e-9:
306
+ p_ax /= total_p; p_ao /= total_p; p_x /= total_p; p_s /= total_p; p_o /= total_p
307
+
308
+ final_sequence = [] # Define upfront for access within _generate_single_pass
309
+
310
  def _generate_single_pass():
311
+ nonlocal final_sequence # Allow modification
312
+ sq = []
313
+ last_stim = None
314
  for i in range(n):
315
+ chosen_stim = None
316
+ # Core logic based on probabilities of specific conditions happening *at this trial*
317
+ r = random.random()
318
+ cdf = 0.0
319
+
320
  if r < (cdf := cdf + p_ax):
321
+ # Force an A->X pair if possible, otherwise just X
322
+ if last_stim != cue: # Need to insert A first
323
+ # Check if inserting A makes sense (not A->A)
324
+ if cue != last_stim:
325
+ sq.append(cue)
326
+ last_stim = cue
327
+ else: # Cannot insert A (would be A->A), so choose X, S, or O based on their probabilities
328
+ # --- START FIX for SyntaxError ---
329
+ chosen_stim = None
330
+ prob_pool_xso = p_x + p_s + p_o # Probabilities for X_alone, S_alone, O_distractor
331
+ if prob_pool_xso < 1e-9:
332
+ chosen_stim = target # Fallback if all probs are near zero
333
+ else:
334
+ rand_choice = random.random() * prob_pool_xso # Random value scaled to the pool sum
335
+ if rand_choice < p_x:
336
+ chosen_stim = target
337
+ elif rand_choice < p_x + p_s:
338
+ chosen_stim = random.choice(similar_distractors)
339
+ else: # Implicitly rand_choice < p_x + p_s + p_o
340
+ chosen_stim = random.choice(other_distractors)
341
+ # --- END FIX for SyntaxError ---
342
+
343
+ if chosen_stim is None: # We added 'A' or it was already 'A'
344
+ chosen_stim = target
345
  elif r < (cdf := cdf + p_ao):
346
+ # Force an A -> Other pair (can be Similar or Other)
347
  distractor_after_a = random.choice(similar_distractors + other_distractors)
348
  if last_stim != cue:
349
+ if cue != last_stim:
350
+ sq.append(cue)
351
+ last_stim = cue
352
+ else: # Cannot insert A, choose a different non-A stimulus
353
+ pool = [target] + similar_distractors + other_distractors
354
+ chosen_stim = random.choice([s for s in pool if s != last_stim])
355
+ if not chosen_stim: chosen_stim = random.choice(pool) # Fallback
356
+
357
+ if chosen_stim is None:
358
+ chosen_stim = distractor_after_a
359
+ # Avoid A -> A
360
+ if chosen_stim == cue:
361
+ pool = [target] + similar_distractors + other_distractors
362
+ chosen_stim = random.choice([s for s in pool if s != cue])
363
+ if not chosen_stim: chosen_stim = random.choice(pool) # Fallback
364
+ elif r < (cdf := cdf + p_x):
365
+ chosen_stim = target
366
+ elif r < (cdf := cdf + p_s):
367
+ chosen_stim = random.choice(similar_distractors)
368
+ else: # (implicitly r < cdf + p_o)
369
+ chosen_stim = random.choice(other_distractors)
370
+
371
+ # Final checks to avoid immediate repeats of non-cue/non-target
372
+ if chosen_stim == last_stim and chosen_stim not in [cue, target]:
373
+ pool = similar_distractors + other_distractors
374
+ available = [d for d in pool if d != last_stim]
375
+ chosen_stim = random.choice(available) if available else chosen_stim # Keep if no alternative
376
+
377
+ # If we inserted 'A', we might need to adjust sequence length
378
+ if len(sq) < n:
379
+ sq.append(chosen_stim)
380
+ last_stim = chosen_stim
381
+
382
+ # Trim if we added extra 'A's making it too long
383
+ final_sequence = sq[:n]
384
+ return final_sequence # Return for the helper function
385
+
386
+ # Define check functions for _ensure_min_events
387
+ def check_ax_pairs(seq, index, item):
388
+ # Is item at index 'target' and preceded by 'cue'?
389
+ return index > 0 and item == target and seq[index - 1] == cue
390
+
391
+ def check_similar_alone(seq, index, item):
392
+ # Is item a similar distractor and NOT preceded by 'cue'?
393
+ return item in similar_distractors and (index == 0 or seq[index - 1] != cue)
394
+
395
+ # Generate initial sequence
396
  generated_sequence = _generate_single_pass()
397
+
398
+ # Ensure minimum A->X pairs
399
+ sequence_ax_ok, success_ax = _ensure_min_events(
400
+ generated_sequence,
401
+ check_ax_pairs,
402
+ ATTN_MIN_PAIR_PROPORTION if p_ax > 0 else 0,
403
+ _generate_single_pass # Regeneration function
404
+ )
405
+ if not success_ax:
406
+ print(f"WARN: Failed to ensure minimum A->X pairs ({ATTN_MIN_PAIR_PROPORTION*100:.1f}%).")
407
+
408
+ # Ensure minimum Similar Alone trials *using the potentially regenerated sequence*
409
+ final_sequence, success_sa = _ensure_min_events(
410
+ sequence_ax_ok, # Start with the sequence that passed (or failed) the A->X check
411
+ check_similar_alone,
412
+ ATTN_MIN_SIMILAR_ALONE_PROPORTION if p_s > 0 else 0,
413
+ _generate_single_pass # Regeneration function (might undo A->X guarantee, but necessary)
414
+ )
415
+ if not success_sa:
416
+ print(f"WARN: Failed to ensure minimum Similar Alone pairs ({ATTN_MIN_SIMILAR_ALONE_PROPORTION*100:.1f}%).")
417
+
418
+ # Final check (optional): Re-verify A->X count after ensuring Similar Alone, as regeneration might affect it
419
  final_ax_count = sum(1 for i, item in enumerate(final_sequence) if check_ax_pairs(final_sequence, i, item))
420
  min_ax_needed = int(n * ATTN_MIN_PAIR_PROPORTION) if p_ax > 0 else 0
421
+ if final_ax_count < min_ax_needed:
422
+ print(f"WARN: Final Attn sequence low on A->X pairs ({final_ax_count}/{min_ax_needed}) after S-Alone check.")
423
+
424
+ # Expected response info remains simple, logic is in process_response
425
  ex = {'target': target, 'cue': cue, 'similar_distractor_key': params['similar_distractor_key']}
426
  return final_sequence, ex
427