Newer
Older
notebooks / py / 20200221_experiment_scheduling.py
#%% [markdown]
# Complex cognitive experiments require participants to perform multiple tasks across a span of limited lab time or online crowdsourcing time, hence demand flexible scheduling of tasks. 
# This notebook demonstrates a possible solution for the problem of scheduling experimental tasks, and mainly concerns reformulating experiments as a resource allocation problem.
#
# Typical experiments are comprised of several session, in each session participants view a set of questionnaires, perform a test, and then respond to another set of questionnaires. This structure is repeated for each srssion throughout the experiment.

# To orgnaize questionniare and tasks in a limited time, we first model a study as an ordered list of sessions (here, for example, 12 session). Sessions consist of several questionnaires before and after a set of experimental tasks. We then define constraints on sueverys and tasks (e.g., pre-task, post-task, end of study, and start of study surveys), and solve for a schedule for the given scenario. Note that his example scenario only plans the experiment for a single subject.
#
# Note: Use the following command to install dependencies: `pip install pyschedule`

#%%
from math import ceil

from pyschedule import Scenario, plotters, alt
from pyschedule.solvers.mip import solve
from pyschedule.plotters.matplotlib import plot

import seaborn as sns

# 1. set theme and style
sns.color_palette("colorblind")
sns.set_style('white')
# experiment duration in minutes
session_duration = 60
game_duration = 45
n_subjects = 2
n_sessions = 12
duration_per_question = .16 # minutes (~10sec)

prestudy_tasks = [
  "xcit_demographics"
]

pregame_tasks = [

]

postgame_tasks = [
  "nasa_tlx",
  "xcit_postgame_debrief"
]

poststudy_tasks = [
  "xcit_poststudy_debrief"
]

n_questions = {
  "xcit_demographics": 20,
  "aiss": 20,
  "arces": 12,
  "asrs": 18,
  "avg2019": 16,
  "bfi_2_par1": 30,
  "bfi_2_part2": 30,
  "bisbas": 24,
  "bis11": 12,
  "cfq": 25,
  "cfs": 12,
  "dyslexia": 15,
  "ehi_sf": 4,
  "grit12": 12,
  "i_panas_sf": 10,
  "ipaq_sf": 7,
  "maas": 15,
  "mfs": 12,
  #"mmi_part1": 66,
  "mw": 8,
  "mwq": 5,
  "ncs_6": 6,
  "nfc": 18,
  "psqi": 9,
  "rei": 40,
  "sci": 8,
  "sqs": 28,
  "upps_sf": 20,
  "webexec": 12,
  "who5": 5,
  "whoqol": 26,
  "xcit_poststudy_debrief": 20,
  "nasa_tlx": 6,
  "xcit_postgame_debrief": 15
}


# 1. define the study
s = Scenario('Prolific500', horizon=session_duration)
# 2. sessions
sessions = s.Resources('Session', num = n_sessions)


def _duration(questions):
  t = ceil(questions * duration_per_question)
  return t

# 3. games
games = s.Tasks('Game',length=game_duration, num=n_sessions, is_group=False)
games += alt(sessions)

# 3. questionnaires
for q in n_questions.keys():
  n = n_questions.get(q, 0)
  task = s.Task(q, length= _duration(n), delay_cost=1)

  if q in prestudy_tasks:
    task += sessions[0] # first session
    s += task < games
  elif q in poststudy_tasks:
    task += sessions[-1] # last session
    s += games < task
  elif q in postgame_tasks:
    task += sessions
    s += games < task
  elif q in pregame_tasks:
    task += sessions
    s += task < games
  else:
    task += alt(sessions)

print(s.tasks)

# 4. solve resource allocation
solve(s)

# 5. print last computed solution
print(s.solution())

# 6. pot gantt
plot(s, fig_size=(50,5))

#%%
# MPILX Scheduling
from pyschedule import Scenario, solvers, plotters, alt

# experiment duration for each subject in minutes
study_duration = 12 * 24 * 60 # days * hours * minutes
n_subjects = 2
task_durations = {
  'pretest_presurvey': 30,
  'pretest_DWI': 15,
  'pretest_rsfMRI': 15,
  'pretest_anatomical': 15,
  'pretest_taskfMRI': 45,
  'pretest_postsurvey': 30,
  'pretest_presurvey': 30,
  'training_presurvey': 30,
  'training_': 10 * 24 * 60, #TODO expand to daily
  'training_postsurvey': 30,
  'posttest_DWI': 15,
  'posttest_rsfMRI': 15,
  'posttest_anatomical': 15,
  'posttest_taskfMRI': 45,
  'posttest_postsurvey': 30
}

# the planning horizon has 
s = Scenario('MPILX50', horizon=study_duration)

subject = s.Resource('subject', num=n_subjects)

tasks = list()

for t in task_durations.keys():
  duration = task_durations[t]
  task = s.Task(t, length=duration, delay_cost=1)
  task += alt(subject)
  tasks.append(task)

for i in range(len(tasks)-1):
  s += tasks[i] < tasks[i+1]

# compute and print session schedules
solvers.mip.solve(s)
print(s.solution())

plotters.matplotlib.plot(s)