From 0cae5fc922bbde707e02988323731523526de091 Mon Sep 17 00:00:00 2001 From: <> Date: Fri, 29 Nov 2024 13:36:51 +0000 Subject: [PATCH] Deployed ca5ad19 with MkDocs version: 1.6.1 --- .nojekyll | 0 404.html | 1932 +++++ Announcement/index.html | 1961 +++++ .../Psychophysics/psychophysics/index.html | 3417 +++++++++ .../psychophysics/psychophysics.ipynb | 462 ++ Examples/Psychophysics/sweetpea/index.html | 2908 +++++++ .../Psychophysics/sweetpea/sweetpea.ipynb | 289 + Examples/index.html | 1969 +++++ .../Basic LLM Experiment.ipynb | 643 ++ Notebooks/Basic LLM Experiment/index.html | 3245 ++++++++ .../(1) First Experiment.ipynb | 214 + .../(1) First Experiment/index.html | 2811 +++++++ .../(2) Instruction Block.ipynb | 389 + .../(2) Instruction Block/index.html | 3422 +++++++++ .../(3) Stroop Block/(3) Stroop Block.ipynb | 395 + Quickstart Guide/(3) Stroop Block/index.html | 3146 ++++++++ .../(4) Responses and Feedback.ipynb | 593 ++ .../(4) Responses and Feedback/index.html | 3877 ++++++++++ Quickstart Guide/Mastery/Mastery.ipynb | 304 + Quickstart Guide/Mastery/index.html | 3174 ++++++++ Quickstart Guide/index.html | 1972 +++++ Stimuli/Text/basic_text/basic_text.py | 22 + Stimuli/Text/basic_text/index.html | 2615 +++++++ Stimuli/Text/derived/derived.py | 72 + Stimuli/Text/derived/index.html | 2933 +++++++ Stimuli/Text/derived_window/derived_window.py | 80 + Stimuli/Text/derived_window/index.html | 2974 ++++++++ Stimuli/Text/index.html | 1971 +++++ Stimuli/Text/predefined/index.html | 2619 +++++++ Stimuli/Text/predefined/predefined.py | 30 + Stimuli/Text/stroop_honeycomb/index.html | 2597 +++++++ .../Text/stroop_honeycomb/stroop_honeycomb.py | 19 + Stimuli/Text/timeline/index.html | 2811 +++++++ Stimuli/Text/timeline/timeline.py | 53 + Stimuli/Text/timeline_image/index.html | 2772 +++++++ Stimuli/Text/timeline_image/timeline_image.py | 42 + Stimuli/image/basic_image/basic_image.py | 12 + Stimuli/image/basic_image/index.html | 2514 ++++++ Stimuli/image/index.html | 1971 +++++ Stimuli/index.html | 1969 +++++ Stimuli/rdp/basic_rdp/basic_rdp.py | 45 + Stimuli/rdp/basic_rdp/index.html | 2725 +++++++ Stimuli/rdp/index.html | 1971 +++++ Stimuli/rdp/rdp_list_variables/index.html | 2785 +++++++ .../rdp_list_variables/rdp_list_variables.py | 53 + Stimuli/rok/basic_rok/basic_rok.py | 26 + Stimuli/rok/basic_rok/index.html | 2625 +++++++ Stimuli/rok/index.html | 1971 +++++ Stimuli/survey/index.html | 1971 +++++ Stimuli/survey/likert/index.html | 2705 +++++++ Stimuli/survey/likert/likert.py | 43 + Stimuli/survey/multi_choice/index.html | 2633 +++++++ Stimuli/survey/multi_choice/multi_choice.py | 30 + Stimuli/survey/text/index.html | 2627 +++++++ Stimuli/survey/text/text.py | 27 + .../symbols/basic_symbols/basic_symbols.py | 37 + Stimuli/symbols/basic_symbols/index.html | 2693 +++++++ Stimuli/symbols/index.html | 1971 +++++ User Guide/data_variables/index.html | 2048 +++++ User Guide/derived_variables/index.html | 2032 +++++ User Guide/index.html | 2001 +++++ .../llm_synthetic_participant/index.html | 2093 +++++ User Guide/overview/index.html | 2147 ++++++ User Guide/timeline_variables/index.html | 2013 +++++ assets/_mkdocstrings.css | 143 + assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.83f73b43.min.js | 16 + assets/javascripts/bundle.83f73b43.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.6ce7567c.min.js | 42 + .../workers/search.6ce7567c.min.js.map | 7 + assets/stylesheets/main.6f8fc17f.min.css | 1 + assets/stylesheets/main.6f8fc17f.min.css.map | 1 + assets/stylesheets/palette.06af60db.min.css | 1 + .../stylesheets/palette.06af60db.min.css.map | 1 + img/word-cloud.png | Bin 0 -> 40209 bytes index.html | 2191 ++++++ javascripts/mathjax.js | 16 + objects.inv | Bin 0 -> 645 bytes reference/SUMMARY/index.html | 1973 +++++ reference/sweetbean/index.html | 2022 +++++ reference/sweetbean/parameter/index.html | 2069 +++++ reference/sweetbean/sequence/index.html | 2943 ++++++++ .../stimulus/ImageStimulus/index.html | 2352 ++++++ reference/sweetbean/stimulus/ROS/index.html | 3220 ++++++++ .../stimulus/SurveyStimulus/index.html | 2071 +++++ .../stimulus/SymbolStimulus/index.html | 2392 ++++++ .../stimulus/TextStimulus/index.html | 3707 +++++++++ .../stimulus/VideoStimulus/index.html | 2352 ++++++ .../sweetbean/stimulus/_Stimulus/index.html | 2430 ++++++ .../sweetbean/stimulus/_utils/index.html | 2123 ++++++ reference/sweetbean/stimulus/index.html | 2022 +++++ search/search_index.json | 1 + sitemap.xml | 215 + sitemap.xml.gz | Bin 0 -> 645 bytes styles.css | 5 + 129 files changed, 145995 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 Announcement/index.html create mode 100644 Examples/Psychophysics/psychophysics/index.html create mode 100644 Examples/Psychophysics/psychophysics/psychophysics.ipynb create mode 100644 Examples/Psychophysics/sweetpea/index.html create mode 100644 Examples/Psychophysics/sweetpea/sweetpea.ipynb create mode 100644 Examples/index.html create mode 100644 Notebooks/Basic LLM Experiment/Basic LLM Experiment.ipynb create mode 100644 Notebooks/Basic LLM Experiment/index.html create mode 100644 Quickstart Guide/(1) First Experiment/(1) First Experiment.ipynb create mode 100644 Quickstart Guide/(1) First Experiment/index.html create mode 100644 Quickstart Guide/(2) Instruction Block/(2) Instruction Block.ipynb create mode 100644 Quickstart Guide/(2) Instruction Block/index.html create mode 100644 Quickstart Guide/(3) Stroop Block/(3) Stroop Block.ipynb create mode 100644 Quickstart Guide/(3) Stroop Block/index.html create mode 100644 Quickstart Guide/(4) Responses and Feedback/(4) Responses and Feedback.ipynb create mode 100644 Quickstart Guide/(4) Responses and Feedback/index.html create mode 100644 Quickstart Guide/Mastery/Mastery.ipynb create mode 100644 Quickstart Guide/Mastery/index.html create mode 100644 Quickstart Guide/index.html create mode 100644 Stimuli/Text/basic_text/basic_text.py create mode 100644 Stimuli/Text/basic_text/index.html create mode 100644 Stimuli/Text/derived/derived.py create mode 100644 Stimuli/Text/derived/index.html create mode 100644 Stimuli/Text/derived_window/derived_window.py create mode 100644 Stimuli/Text/derived_window/index.html create mode 100644 Stimuli/Text/index.html create mode 100644 Stimuli/Text/predefined/index.html create mode 100644 Stimuli/Text/predefined/predefined.py create mode 100644 Stimuli/Text/stroop_honeycomb/index.html create mode 100644 Stimuli/Text/stroop_honeycomb/stroop_honeycomb.py create mode 100644 Stimuli/Text/timeline/index.html create mode 100644 Stimuli/Text/timeline/timeline.py create mode 100644 Stimuli/Text/timeline_image/index.html create mode 100644 Stimuli/Text/timeline_image/timeline_image.py create mode 100644 Stimuli/image/basic_image/basic_image.py create mode 100644 Stimuli/image/basic_image/index.html create mode 100644 Stimuli/image/index.html create mode 100644 Stimuli/index.html create mode 100644 Stimuli/rdp/basic_rdp/basic_rdp.py create mode 100644 Stimuli/rdp/basic_rdp/index.html create mode 100644 Stimuli/rdp/index.html create mode 100644 Stimuli/rdp/rdp_list_variables/index.html create mode 100644 Stimuli/rdp/rdp_list_variables/rdp_list_variables.py create mode 100644 Stimuli/rok/basic_rok/basic_rok.py create mode 100644 Stimuli/rok/basic_rok/index.html create mode 100644 Stimuli/rok/index.html create mode 100644 Stimuli/survey/index.html create mode 100644 Stimuli/survey/likert/index.html create mode 100644 Stimuli/survey/likert/likert.py create mode 100644 Stimuli/survey/multi_choice/index.html create mode 100644 Stimuli/survey/multi_choice/multi_choice.py create mode 100644 Stimuli/survey/text/index.html create mode 100644 Stimuli/survey/text/text.py create mode 100644 Stimuli/symbols/basic_symbols/basic_symbols.py create mode 100644 Stimuli/symbols/basic_symbols/index.html create mode 100644 Stimuli/symbols/index.html create mode 100644 User Guide/data_variables/index.html create mode 100644 User Guide/derived_variables/index.html create mode 100644 User Guide/index.html create mode 100644 User Guide/llm_synthetic_participant/index.html create mode 100644 User Guide/overview/index.html create mode 100644 User Guide/timeline_variables/index.html create mode 100644 assets/_mkdocstrings.css create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.83f73b43.min.js create mode 100644 assets/javascripts/bundle.83f73b43.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js.map create mode 100644 assets/stylesheets/main.6f8fc17f.min.css create mode 100644 assets/stylesheets/main.6f8fc17f.min.css.map create mode 100644 assets/stylesheets/palette.06af60db.min.css create mode 100644 assets/stylesheets/palette.06af60db.min.css.map create mode 100644 img/word-cloud.png create mode 100644 index.html create mode 100644 javascripts/mathjax.js create mode 100644 objects.inv create mode 100644 reference/SUMMARY/index.html create mode 100644 reference/sweetbean/index.html create mode 100644 reference/sweetbean/parameter/index.html create mode 100644 reference/sweetbean/sequence/index.html create mode 100644 reference/sweetbean/stimulus/ImageStimulus/index.html create mode 100644 reference/sweetbean/stimulus/ROS/index.html create mode 100644 reference/sweetbean/stimulus/SurveyStimulus/index.html create mode 100644 reference/sweetbean/stimulus/SymbolStimulus/index.html create mode 100644 reference/sweetbean/stimulus/TextStimulus/index.html create mode 100644 reference/sweetbean/stimulus/VideoStimulus/index.html create mode 100644 reference/sweetbean/stimulus/_Stimulus/index.html create mode 100644 reference/sweetbean/stimulus/_utils/index.html create mode 100644 reference/sweetbean/stimulus/index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 styles.css diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..075d4607 --- /dev/null +++ b/404.html @@ -0,0 +1,1932 @@ + + + +
+ + + + + + + + + + + + + + +In this example, we use SweetBean to generate an experimental sequence for a same-different Psychophysics experiment. In this experiment, each trials has the following sequence of events:
+y
or n
.First, we will install SweetBean and Setup.
+%%capture
+!pip install git+https://github.com/AutoResearch/sweetbean
+
To illustrate the use of SweetBean, we first assume a fixed trial sequence:
+timeline = [{'dots_left': 40, 'dots_right': 70},
+ {'dots_left': 70, 'dots_right': 70},
+ {'dots_left': 70, 'dots_right': 40},
+ {'dots_left': 70, 'dots_right': 70},
+ {'dots_left': 40, 'dots_right': 70},
+ {'dots_left': 70, 'dots_right': 40},
+ {'dots_left': 40, 'dots_right': 40},
+ {'dots_left': 40, 'dots_right': 40},
+ {'dots_left': 40, 'dots_right': 40},
+ {'dots_left': 70, 'dots_right': 40}]
+
Note: You can generate such a trial sequence automatically using SweetPea. For this particular use case, you may refer to this example tutorial.
+Many experiments require instructions that tell the participants what to do.
+Creating instructions in SweetBean is quite simple. First, we define a number of text stimuli that the participant sees. Then, we specify the order of the text stimuli within a block of instructions.
+Let's begin with writing down our instructions in html code. We can specify the key required to move on to the next instruction.
+from sweetbean.stimulus import TextStimulus
+
+introduction_welcome = TextStimulus(text='Welcome to our perception experiment.<br><br> \
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+introduction_pictures = TextStimulus(text='Each picture contains two sets of dots, one left and one right.<br><br>\
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+introduction_responses = TextStimulus(text='You have to indicate whether the two sets contain an equal number of dots.<br><br>\
+ Press the y-key for yes (equal number) and<br> the n-key for no (unequal number).<br><br>\
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+introduction_note = TextStimulus(text='Note: For each picture, you have only 2 seconds to respond, so respond quickly.<br><br>\
+ You can only respond with the y and n keys while the dots are shown.<br><br> \
+ Press the SPACE key to BEGIN the experiment.',
+ choices=[' '])
+
Next, will pack these stimuli into a list to form an instruction block.
+from sweetbean.sequence import Block
+
+# create a list of instruction stimuli for the instruction block
+introduction_list = [introduction_welcome,
+ introduction_pictures,
+ introduction_responses,
+ introduction_note]
+
+# create the instruction block
+instruction_block = Block(introduction_list)
+
Similarly, we can specify a final instruction displayed at the end of the experiment.
+# create a text stimulus shown at the end of the experiment
+instruction_exit = TextStimulus(duration=3000,
+ text='Thank you for participating in the experiment.',
+ )
+
+# create a list of instruction stimuli for the exit block
+exit_list = [instruction_exit]
+
+# create the exit block
+exit_block = Block(exit_list)
+
First, we define the fixation cross. SweetBean provides a convenient method:
+from sweetbean.stimulus import FixationStimulus
+
+duration = 1500 # the duration is given in ms
+fixation = FixationStimulus(duration)
+
Next, we declare our sets of dots as features in the stimulus, using the timeline variables:
+First,
+declare the stimulus features dot_stimulus_left
and dot_stimulus_right
as timeline variables (since they come from the timeline). For the parser, we also need to provide all the possible levels of the stimulus. For now, we assume that each dot display can contain either 40 or 70 dots.
Then, we define the entire stimulus which is composed of the two features. SweetPea provides a convenient way of generating a stimulus with two sets of dots via RandomDotPatternsStimulus
.
from sweetbean.parameter import TimelineVariable
+from sweetbean.stimulus import RandomDotPatternsStimulus
+
+# define the stimuli features as timeline variables
+dot_stimulus_left = TimelineVariable('dots_left', [40, 70])
+dot_stimulus_right = TimelineVariable('dots_right', [40, 70])
+
+# We can use these variables in the stimuli declaration:
+rdp = RandomDotPatternsStimulus(
+ duration=2000,
+ number_of_oobs=[dot_stimulus_left, dot_stimulus_right],
+ number_of_apertures=2,
+ choices=["y", "n"],
+ background_color="black",
+)
+
Note that the dot stimulus is shown for 2000ms (duration=2000
). It consists of two set of dots (number_of_apertures=2
), which are parameterized by the two timeline variables number_of_oobs=[dot_stimulus_left, dot_stimulus_right]
. Finally, we allow participants to record a response on each stimulus, indicating whether the dots match or not by pressing the respective keys for y
and n
(choices=["y", "n"]
)
Now, we define the event sequence which determines the order of events within a trial. SweetBean groups event into event sequences, and event sequences into blocks. Here, an event sequence corresponds to a trial and a block to series of trials.
+from sweetbean.sequence import Block, Experiment, sequence_to_image
+
+# define the sequence of events within a trial
+event_sequence = [fixation, rdp]
+
+# group trials into blocks
+task_block = Block(event_sequence, timeline)
+
Now that we have specified all of our experiment blocks, we put them together into an experiment. The function below compiles the experiment and converts it into an html file.
+# define the entire experiment
+experiment = Experiment([instruction_block, task_block, exit_block])
+
+# export experiment to html file
+experiment.to_html("psychophysics_experiment.html")
+
The code above should have generated a local html file rok_weber_fechner.html
which can be opened and run.
You can also integrate SweetBean into closed-loop behavioral research workflows, e.g., using AutoRA. This may involve calling a function that generates a novel jsPsych experiment from scratch, depending on the inputs.
+The function below compiles the code above into a single function, and returns a web-based (JavaScript) experiment, written in jsPsych
.
The function takes a timeline, containing a sequence of trials, as input, along with the two levels for the dot stimuli.
+from sweetbean.stimulus import TextStimulus, FixationStimulus, RandomDotPatternsStimulus
+from sweetbean.sequence import Block, Experiment, sequence_to_image
+from sweetbean.parameter import TimelineVariable
+
+def stimulus_sequence(timeline, num_dots_1, num_dots_2):
+
+ # INSTRUCTION BLOCK
+
+ # generate several text stimuli that serve as instructions
+ introduction_welcome = TextStimulus(text='Welcome to our perception experiment.<br><br> \
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+ introduction_pictures = TextStimulus(text='Each picture contains two sets of dots, one left and one right.<br><br>\
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+ introduction_responses = TextStimulus(text='You have to indicate whether the two sets contain an equal number of dots.<br><br>\
+ Press the y-key for yes (equal number) and<br> the n-key for no (unequal number).<br><br>\
+ Press the SPACE key to continue.',
+ choices=[' '])
+
+ introduction_note = TextStimulus(text='Note: For each picture, you have only 2 seconds to respond, so respond quickly.<br><br>\
+ You can only respond with the y and n keys while the dots are shown.<br><br> \
+ Press the SPACE key to BEGIN the experiment.',
+ choices=[' '])
+
+
+ # create a list of instruction stimuli for the instruction block
+ introduction_list = [introduction_welcome,
+ introduction_pictures,
+ introduction_responses,
+ introduction_note]
+
+ # create the instruction block
+ instruction_block = Block(introduction_list)
+
+ # EXIT BLOCK
+
+ # create a text stimulus shown at the end of the experiment
+ instruction_exit = TextStimulus(duration=3000,
+ text='Thank you for participating in the experiment.',
+ )
+
+ # create a list of instruction stimuli for the exit block
+ exit_list = [instruction_exit]
+
+ # create the exit block
+ exit_block = Block(exit_list)
+
+ # TASK BLOCK
+
+ # define fixation cross
+ fixation = FixationStimulus(1500)
+
+ # define the stimuli features as timeline variables
+ dot_stimulus_left = TimelineVariable('dots_left', [num_dots_1, num_dots_2])
+ dot_stimulus_right = TimelineVariable('dots_right', [num_dots_1, num_dots_2])
+
+ # We can define a stimulus as a function of those stimulus features
+ rdp = RandomDotPatternsStimulus(
+ duration=2000,
+ number_of_oobs=[dot_stimulus_left, dot_stimulus_right],
+ number_of_apertures=2,
+ choices=["y", "n"],
+ background_color="black",
+ )
+
+ # define the sequence of events within a trial
+ event_sequence = [fixation, rdp]
+
+ # group trials into blocks
+ task_block = Block(event_sequence, timeline)
+
+ # EXPERIMENT
+
+ # define the entire experiment
+ experiment = Experiment([instruction_block, task_block, exit_block])
+
+ # return a js string to transfer to autora
+ return experiment.to_js_string(as_function=True, is_async=True)
+
In this example, we use SweetPea to generate an experimental sequence for a same-different Psychophysics experiment.
+You can find more in-depth tutorials on automated experimental design at the SweetPea Website.
+First, we will install SweetPea.
+%%capture
+!pip install sweetpea
+
Next, we define the experimental factors of our experiment. In this experiment, select whether the number of dots in the two sets is the same or not.
+The experiment has two independent variables: The number of dots in the first set and the number of dots in the second set. Here, we want to counterbalance the number of dots for stimulus 1 and stimulus, respectively.
+from sweetpea import Factor
+
+# the first experimental factor indicates the number of dots in the
+# left stimulus. It has two levels, i.e., two possible values for the
+# number of dots, either 40 and or 70 dots.
+num_dots_left = Factor('dots_left', [40, 70])
+
+# the second experimental factor indicates the number of dots in the
+# right stimulus. It also has two levels: 40 and 70 dots.
+num_dots_right = Factor('dots_right', [70, 40])
+
The code defines the two variables in terms of two experimental factors, respectively: the number of dots in the left stimulus and the number of dots in the right stimulus. Here, we assume that the number of dots can either be 40 or 70.
+Next, we generate an experiment trial sequence. In this sequence, we want to counterbalance the levels of both factors. We also want to specify a constraint to include at least 20 trials.
+from sweetpea import MinimumTrials, CrossBlock, synthesize_trials, CMSGen, experiments_to_dicts
+
+# the experimental design includes all relevant factors
+# (whether counterbalanced or not)
+design = [num_dots_left, num_dots_right]
+
+# the crossing specifies which factors are fully counterbalanced
+crossing = [num_dots_left, num_dots_right]
+
+# we also add a constraint to include at least 20 trials
+constraints = [MinimumTrials(20)]
+
+# next, we define an experimental block based on the design, crossing,
+# and constraints
+block = CrossBlock(design, crossing, constraints)
+
+# we then use a SAT-Solver to find one suitable experimental sequence
+experiment = synthesize_trials(block, 1, CMSGen)
+
Sampling 1 trial sequences using CMSGen. +Encoding experiment constraints... +Running CMSGen... ++
We can print the resulting experiment:
+from sweetpea import print_experiments
+
+print_experiments(block, experiment)
+
+1 trial sequences found. + +Experiment 0: +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 40 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 40 | Number of Dots for Right Stimulus 1 70 +Number of Dots for Left Stimulus 70 | Number of Dots for Right Stimulus 1 70 + ++
And we can store the resulting experimental sequence in a dictionary:
+# we can export the experimental sequence as a dictionary
+sequence = experiments_to_dicts(block, experiment)
+print(sequence)
+
[[{'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 40}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 40, 'Number of Dots for Right Stimulus 1': 70}, {'Number of Dots for Left Stimulus': 70, 'Number of Dots for Right Stimulus 1': 70}]] ++
Next, we wrap the code above into a function that will generate an experimental sequence for an arbitrary set of two stimulus intensities, e.g., number of dots. We will accept the number of trials as an argument.
+from sweetpea import Factor, MinimumTrials, CrossBlock, synthesize_trials, CMSGen, experiments_to_dicts
+
+def trial_sequence(num_dots_1, num_dots_2, min_trials):
+
+ # define regular factors
+ num_dots_left = Factor('dots_left', [num_dots_1, num_dots_2])
+ num_dots_right = Factor('dots_right', [num_dots_1, num_dots_2])
+
+ # define experimental block
+ design = [num_dots_left, num_dots_right]
+ crossing = [num_dots_left, num_dots_right]
+ constraints = [MinimumTrials(min_trials)]
+
+ block = CrossBlock(design, crossing, constraints)
+
+ # synthesize trial sequence
+ experiment = synthesize_trials(block, 1, CMSGen)
+
+ # export as dictionary
+ return experiments_to_dicts(block, experiment)[0]
+
This section provides examples of experiments generated by SweetBean. This may serve as a starting point for your own experiments.
+ + + + + + + + + + + + + + +Here, we run a experiment with a llm model via api.
+As prerequisite, we create functions that call the api on prompts and returns the response:
+"""
+To use this script, you need to be logged in to the Vertex AI platform:
+See https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login
+```bash
+gcloud auth application-default login
+```
+"""
+import vertexai
+from vertexai.generative_models import GenerativeModel, SafetySetting
+
+
+PROJECT = "auto-centauer"
+LOCATION = "us-central1"
+MODEL = "gemini-1.5-flash-002"
+
+
+def generate(prompt):
+ vertexai.init(project=PROJECT, location=LOCATION)
+ model = GenerativeModel(
+ MODEL,
+ )
+ responses = model.generate_content(
+ [prompt],
+ generation_config=generation_config,
+ safety_settings=safety_settings,
+ stream=True,
+ )
+
+ return "".join([response.text for response in responses])
+
+
+
+generation_config = {
+ "max_output_tokens": 1,
+ "temperature": 1,
+ "top_p": 0.95,
+ "stop_sequences": [">>"],
+}
+
+safety_settings = [
+ SafetySetting(
+ category=SafetySetting.HarmCategory.HARM_CATEGORY_HATE_SPEECH,
+ threshold=SafetySetting.HarmBlockThreshold.OFF,
+ ),
+ SafetySetting(
+ category=SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
+ threshold=SafetySetting.HarmBlockThreshold.OFF,
+ ),
+ SafetySetting(
+ category=SafetySetting.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
+ threshold=SafetySetting.HarmBlockThreshold.OFF,
+ ),
+ SafetySetting(
+ category=SafetySetting.HarmCategory.HARM_CATEGORY_HARASSMENT,
+ threshold=SafetySetting.HarmBlockThreshold.OFF,
+ ),
+]
+
First, we define the experiment.
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+from sweetbean.parameter import TimelineVariable, DataVariable, DerivedLevel, DerivedParameter
+
+# TIMELINE
+timeline = [
+ {"color": "red", "word": "RED"},
+ {"color": "green", "word": "GREEN"},
+ {"color": "green", "word": "RED"},
+ {"color": "red", "word": "GREEN"},
+]
+
+# EVENT SEQUENCE
+
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
+
+def is_correct_f(color):
+ return color == "red"
+
+
+def is_correct_j(color):
+ return not is_correct_f(color)
+
+
+j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
+correct_key = DerivedParameter("correct", [j_key, f_key])
+
+# Creating a data variable
+correct = DataVariable("correct", [True, False])
+
+
+# Predicates
+def is_correct(correct):
+ return correct
+
+
+def is_false(correct):
+ return not correct
+
+
+# Derived Levels
+correct_feedback = DerivedLevel("correct", is_correct, [correct], 2)
+false_feedback = DerivedLevel("false", is_false, [correct], 2)
+
+# Derived Parameter
+feedback_text = DerivedParameter("feedback_text", [correct_feedback, false_feedback])
+
+# Using it in the stimulus
+fixation = TextStimulus(1000, "+")
+
+so_s = TextStimulus(400)
+stroop = TextStimulus(2000, word, color, ["j", "f"], correct_key)
+so_f = TextStimulus(300)
+feedback = TextStimulus(800, feedback_text)
+
+event_sequence = [fixation, so_s, stroop, so_f, feedback]
+
+# BLOCK DESIGN
+
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
To test what the LLM "sees", we can run the experiment as chat on ourselves. If we set multiturn to true we will not see the full chat history but only the last generated prompt.
+data = experiment.run_on_language(get_input=input, multiturn=True)
+data
+
{'full_chat': ' You see "+" written in white. You see a blank screen. You see "RED" written in red. You can press [\'j\', \'f\']. You press <<>>. You see a blank screen. You see "false" written in white. You see "+" written in white. You see a blank screen. You see "GREEN" written in green. You can press [\'j\', \'f\']. You press <<>>. You see a blank screen. You see "false" written in white. You see "+" written in white. You see a blank screen. You see "RED" written in green. You can press [\'j\', \'f\']. You press <<>>. You see a blank screen. You see "false" written in white. You see "+" written in white. You see a blank screen. You see "GREEN" written in red. You can press [\'j\', \'f\']. You press <<>>. You see a blank screen. You see "false" written in white.', + 'data_lst': {'choices': [[], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + []], + 'type': ['jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse'], + 'text': ['+', + '', + 'RED', + '', + 'false', + '+', + '', + 'GREEN', + '', + 'false', + '+', + '', + 'RED', + '', + 'false', + '+', + '', + 'GREEN', + '', + 'false'], + 'color': ['white', + 'white', + 'red', + 'white', + 'white', + 'white', + 'white', + 'green', + 'white', + 'white', + 'white', + 'white', + 'green', + 'white', + 'white', + 'white', + 'white', + 'red', + 'white', + 'white'], + 'duration': [1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800], + 'correct_key': ['', + '', + 'f', + '', + '', + '', + '', + 'j', + '', + '', + '', + '', + 'j', + '', + '', + '', + '', + 'f', + '', + ''], + 'correct': [None, + None, + False, + None, + None, + None, + None, + False, + None, + None, + None, + None, + False, + None, + None, + None, + None, + False, + None, + None], + 'response': [None, + None, + '', + None, + None, + None, + None, + '', + None, + None, + None, + None, + '', + None, + None, + None, + None, + '', + None, + None]}, + 'prompts_single': [' You see "+" written in white.', + ' You see a blank screen.', + ' You see "RED" written in red. You can press [\'j\', \'f\']. You press <<', + ' You see a blank screen.', + ' You see "false" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "GREEN" written in green. You can press [\'j\', \'f\']. You press <<', + ' You see a blank screen.', + ' You see "false" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "RED" written in green. You can press [\'j\', \'f\']. You press <<', + ' You see a blank screen.', + ' You see "false" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "GREEN" written in red. You can press [\'j\', \'f\']. You press <<', + ' You see a blank screen.', + ' You see "false" written in white.'], + 'prompts_multiturn': [[...], [...], [...], [...]], + 'intro': '', + 'reaction_appendix': '{{reaction}}>>.', + 'multiturn': True}+
Let's run the experiment on the AI. Here, we use api calls to google cloud, but all that is needed is a function that generates text from other text. In this case generate
uses the input and makes an api call and returns the response.
def parse_response(response, correct_key):
+ return correct_key in response
+
+
+data_ai = experiment.run_on_language(get_input=generate, parse_response=parse_response,
+ intro="You are a participant in a psychological experiment. You're goal is not to react as accurate as possible but as similar to a human as possible. This includes making similar mistakes as humans. You will be given a sequence of things you see and your goal is to react with a single letter indicating a key press. Your response should always be a single letter. Please respond with the keys 'j' and 'f' to indicate the color of the word. The correct key for red is 'f' and for green 'j'. Please indicate your response by either f or j.")
+data_ai
+
/Users/younesstrittmatter/Documents/GitHub/AutoResearch/sweetbean/.venv/lib/python3.11/site-packages/google/auth/_default.py:76: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK without a quota project. You might receive a "quota exceeded" or "API not enabled" error. See the following page for troubleshooting: https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. + warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING) ++
{'full_chat': 'You are a participant in a psychological experiment. You\'re goal is not to react as accurate as possible but as similar to a human as possible. This includes making similar mistakes as humans. You will be given a sequence of things you see and your goal is to react with a single letter indicating a key press. Your response should always be a single letter. Please respond with the keys \'j\' and \'f\' to indicate the color of the word. The correct key for red is \'f\' and for green \'j\'. Please indicate your response by either f or j. You see "+" written in white. You see a blank screen. You see "RED" written in red. You can press [\'j\', \'f\']. You press <<f>>. You see a blank screen. You see "correct" written in white. You see "+" written in white. You see a blank screen. You see "GREEN" written in green. You can press [\'j\', \'f\']. You press <<j>>. You see a blank screen. You see "correct" written in white. You see "+" written in white. You see a blank screen. You see "RED" written in green. You can press [\'j\', \'f\']. You press <<j>>. You see a blank screen. You see "correct" written in white. You see "+" written in white. You see a blank screen. You see "GREEN" written in red. You can press [\'j\', \'f\']. You press <<f>>. You see a blank screen. You see "correct" written in white.', + 'data_lst': {'choices': [[], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + [], + [], + [], + ['j', 'f'], + [], + []], + 'type': ['jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse', + 'jsPsychHtmlKeyboardResponse'], + 'text': ['+', + '', + 'RED', + '', + 'correct', + '+', + '', + 'GREEN', + '', + 'correct', + '+', + '', + 'RED', + '', + 'correct', + '+', + '', + 'GREEN', + '', + 'correct'], + 'color': ['white', + 'white', + 'red', + 'white', + 'white', + 'white', + 'white', + 'green', + 'white', + 'white', + 'white', + 'white', + 'green', + 'white', + 'white', + 'white', + 'white', + 'red', + 'white', + 'white'], + 'duration': [1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800, + 1000, + 400, + 2000, + 300, + 800], + 'correct_key': ['', + '', + 'f', + '', + '', + '', + '', + 'j', + '', + '', + '', + '', + 'j', + '', + '', + '', + '', + 'f', + '', + ''], + 'correct': [None, + None, + True, + None, + None, + None, + None, + True, + None, + None, + None, + None, + True, + None, + None, + None, + None, + True, + None, + None], + 'response': [None, + None, + 'f', + None, + None, + None, + None, + 'j', + None, + None, + None, + None, + 'j', + None, + None, + None, + None, + 'f', + None, + None]}, + 'prompts_single': [' You see "+" written in white.', + ' You see a blank screen.', + ' You see "RED" written in red. You can press [\'j\', \'f\']. You press <<', + 'f', + ' You see a blank screen.', + ' You see "correct" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "GREEN" written in green. You can press [\'j\', \'f\']. You press <<', + 'j', + ' You see a blank screen.', + ' You see "correct" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "RED" written in green. You can press [\'j\', \'f\']. You press <<', + 'j', + ' You see a blank screen.', + ' You see "correct" written in white.', + ' You see "+" written in white.', + ' You see a blank screen.', + ' You see "GREEN" written in red. You can press [\'j\', \'f\']. You press <<', + 'f', + ' You see a blank screen.', + ' You see "correct" written in white.'], + 'prompts_multiturn': [[...], [...], [...], [...]], + 'intro': "You are a participant in a psychological experiment. You're goal is not to react as accurate as possible but as similar to a human as possible. This includes making similar mistakes as humans. You will be given a sequence of things you see and your goal is to react with a single letter indicating a key press. Your response should always be a single letter. Please respond with the keys 'j' and 'f' to indicate the color of the word. The correct key for red is 'f' and for green 'j'. Please indicate your response by either f or j.", + 'reaction_appendix': '{{reaction}}>>.', + 'multiturn': False}+
Here, we learn how to create a very simple experiment consisting of a single text stimulus. We get familiar with installing the package and loading different functionality. Then we create a stimulus and get used to how to organize stimuli into blocks and the blocks into an experiment.
+In python, we can make use of packages that are created by the community. To use them, we need to install them first. We use pip, a package manager for python, to install the package.
+# installing the package
+!pip install sweetbean
+
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ +Requirement already satisfied: sweetbean in /usr/local/lib/python3.8/dist-packages (0.0.19) ++
We now create our first stimulus — a text greeting the participant. +First, we need to import the functionality from SweetBean to create such a stimulus, and then we specify the stimulus.
+There are multiple different stimuli types in SweetBean with varying sets of parameters. Here, we start with a text stimulus with the single feature text.
+# import the functionality from sweetbean to create a text stimulus
+from sweetbean.stimulus import TextStimulus
+
+# create an introduction
+introduction_stimulus = TextStimulus(text='Welcome to our awesome experiment.')
+
In SweetBean, an experiment consists of blocks, while blocks themselves consist of stimuli.
+For example, the first part of an experiment typically contains informed consent and instructions for the participants. In SweetBean, this can be achieved by assembling a sequence of text stimuli.
+The second block of the experiment could then consist of training trials, the following blocks could be experimental blocks with target trials, and the last block of the experiment could be a debriefing block where we again show text stimuli.
+Here, we have a simple block consisting only of a single text stimulus.
+# import the functionality from sweetbean to create a block
+from sweetbean.sequence import Block
+
+# create a list of stimuli for the block
+introduction_list = [introduction_stimulus]
+
+# create the block
+introduction_block = Block(introduction_list)
+
Now that we have a block, we can create a full experiment from this single block.
+# import the functionality from sweetbean to create experiments
+from sweetbean.sequence import Experiment
+
+# create a list of blocks
+block_list = [introduction_block]
+
+# create the experiment
+experiment = Experiment(block_list)
+
Finally, we create our html file from the experiment.
+# the functionality to create a html is a method of the experiment class. It expects a filename as input and creates the experiment we declared earlier.
+experiment.to_html('index.html')
+
If you run this file in google colab, you can locate the newly created file in the "files" tab (left side of your screen). This HTML file is ready to be viewed in any web browser of your choice.
+Here, we learn how to create an instruction block. We creat a block greeting the participants and give instruction for the tasks. We get familiar with the different options to customize text stimuli.
+Do you remember how to install the package?
+# Enter your code here:
+
!pip install sweetbean
+
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ +Collecting sweetbean + Downloading sweetbean-0.0.19-py3-none-any.whl (206 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 206.1/206.1 KB 4.5 MB/s eta 0:00:00 +Installing collected packages: sweetbean +Successfully installed sweetbean-0.0.19 ++
Do you remember how to import the functionality to create text stimuli?
+# Enter your code here:
+
from sweetbean.stimulus import TextStimulus
+
This time, we want to add more functionality to our stimulus. Remember:
+introduction_stimulus = TextStimulus(text='Welcome to our awesome experiment.')
+
+This stimulus has only one single parameter. The text parameter that defines what text is shown. But as we defined it, this stimulus would be shown indefinitely until the user closes the browser. To change this, we can add a duration parameter. The duration parameter is accepted by most stimuli types in SweetBean. It defines how long a stimulus is shown in ms.
+introduction_stimulus_timed = TextStimulus(duration=4000, text='Welcome to our awesome experiment.<br>The introduction will start in 4s.')
+
By adding the duration parameter, this stimulus will now be shown 4000ms.
+The timing of a stimulus is useful, but what if the for example, the introduction text is very long, and we want to give the participant the freedom to proceed whenever ready?
+For this purpose we can define a list of choices. These are keys on the keyboard, that will be recorded when the user presses them. They also end the current stimulus.
+introduction_stimulus_respond = TextStimulus(text="This is a very detailed explanation of our experiment.<br>Press SPACE to continue.", choices=[' '])
+
The choices parameter expects a list of characters. Here, we chose the SPACE character (whitespace). We can also add more or different characters. For example, can you write a stimulus that asks for the participants handedness and accepts the letters r
, l
or o
as choices?
# Replace None in the following line:
+introduction_survey_handedness = None
+
introduction_survey_handedness = TextStimulus(text="What is your handedness?<br>Press R for right, L for left, or O for other.", choices=['r', 'l', 'o'])
+
The described way to ask for demographic data is just a demonstration. SweetBean has extra functionality to conduct surveys and to design questionnaires.
+Do you remember how to create a block with the stimuli?
+# Enter your code:
+
# import the functionality from sweetbean to create a block
+from sweetbean.sequence import Block
+
+# create a list of stimuli for the block
+introduction_list = [introduction_stimulus_timed, introduction_stimulus_respond, introduction_survey_handedness]
+
+# create the block
+introduction_block = Block(introduction_list)
+
Do you remember how to create the experiment?
+# Enter your code:
+
# import the functionality from sweetbean to create experiments
+from sweetbean.sequence import Experiment
+
+# create a list of blocks
+block_list = [introduction_block]
+
+# create the experiment
+experiment = Experiment(block_list)
+
Do you remember how to export the experiment to html?
+# Enter your code:
+
# create the html file.
+experiment.to_html('index.html')
+
\n",
+ "stroop = TextStimulus(duration=2500, text=word, color=color)
\n",
+ "block = Block(stimulus_sequence, timeline)\n",
+ "
\n",
+ "stroop = TextStimulus(2500, word, color)
\n",
+ "block = Block(stimuli=stimulus_sequence, timeline=timeline)\n",
+ "
\n",
+ "Here, we learn how to use a timeline to create a Stroop Block. In the Stroop task (Stroop, 1935), participants are typically asked to name the color in which a color word is displayed (e.g., say green
to the word RED displayed in green). We create an experiment that loops through the following stimulus sequence while varying the ink color and the word of the color word.
!pip install sweetbean
+
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ +Collecting sweetbean + Downloading sweetbean-0.0.19-py3-none-any.whl (206 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 206.1/206.1 KB 4.2 MB/s eta 0:00:00 +Installing collected packages: sweetbean +Successfully installed sweetbean-0.0.19 ++
Instead of defining a stimulus for each trial, we can create a timeline of stimulus features. In this case, we define the color and the word of a trial in a timeline. The timeline bellow defines the trial sequence
+RED, GREEN, RED, GREEN
+timeline = [
+ {'color': 'red', 'word': 'RED'},
+ {'color': 'green', 'word': 'RED'},
+ {'color': 'green', 'word': 'GREEN'},
+ {'color': 'red', 'word': 'GREEN'},
+]
+
To use the timeline declared above we need to declare timeline variables. These variables can then be used in the creation of stimuli instead of static parameters.
+# import the functionality from sweetbean
+from sweetbean.parameter import TimelineVariable
+
+## declare the timeline variables
+
+# color: The name has to be color (it is the name in the timeline), and it has the levels red and green
+color = TimelineVariable(name="color", levels=["red", "green"])
+
+# word: The name has to be word (it is the name in the timeline), and it has the levels RED and GREEN
+word = TimelineVariable(name="word", levels=["RED", "GREEN"])
+
Now we create a stimulus sequence. When using a timeline, sweetbean can loop through a specified sequence of stimuli as often as there are trials defined in the timeline. Here we want to loop through the sequence:
+A blank screen first (fixation onset), then the fixation cross, then a blank screen again (stimulus onset), and finally the Stroop stimulus.
+Here, we also introduce a (rather boring) stimulus: A blank screen
+# importing the functionality
+from sweetbean.stimulus import TextStimulus, BlankStimulus
+
+## declaring the different stimuli
+
+# fixation onset (a blank screen for 600ms)
+fixation_onset = BlankStimulus(duration=600)
+
+# fixation cross (the character "+" shown for 800ms)
+fixation = TextStimulus(duration=800, text="+")
+
+# stimulus onset (a blank screen shown for 200ms)
+stimulus_onset = BlankStimulus(duration=200)
+
+# the Stroop stimulus. Here instead of fixed parameters, we use the timeline variables, that we defined previously.
+stroop = TextStimulus(duration=2500, text=word, color=color)
+
Note that for this example, we also used a new parameter color
for the text stimulus that we haven't used before.
For the purpouse of sound experimental design, it is often usefull to introduce a variable stimulus onset instead of a fixed one. This can make the modelling easier (e.g., for drift diffusion models in decision paradigms), since participant can not learn the stimulus onset and the decision process starts on the onset of the stimulus (not earlier).
+Can you change the code to accomodate for this? +To start we already provide the timeline:
+timeline = [
+ {'color': 'red', 'word': 'RED', 'soa': 300},
+ {'color': 'green', 'word': 'GREEN', 'soa': 200 },
+ {'color': 'green', 'word': 'RED', 'soa': 400},
+ {'color': 'red', 'word': 'GREEN', 'soa': 500},
+]
+
# Enter your code here:
+
# declare the timeline variable
+soa = TimelineVariable(name='soa', levels=[200, 300, 400, 500])
+
+# use the timeline variable in the stimulus instead of the fixed value
+stimulus_onset = BlankStimulus(duration=soa)
+
To create the block, all we have to do is add the timeline as parameter
+# import the functionality from sweetbean to create a block
+from sweetbean.sequence import Block
+
+# create a list of stimuli for the block
+stimulus_sequence = [fixation_onset, fixation, stimulus_onset, stroop]
+
+# create the block
+stroop_block = Block(stimulus_sequence, timeline)
+
+stroop = TextStimulus(duration=2500, text=word, color=color)
+block = Block(stimulus_sequence, timeline)
+
+stroop = TextStimulus(2500, word, color)
+block = Block(stimuli=stimulus_sequence, timeline=timeline)
+
+# import the functionality from sweetbean to create experiments
+from sweetbean.sequence import Experiment
+
+# create a list of blocks
+block_list = [stroop_block]
+
+# create the experiment
+experiment = Experiment(block_list)
+
+# export to html
+experiment.to_html('index.html')
+
SweetBean provides functionality to help document your experiments. For example, you can create a stimulus timeline as the one seen above. This a picture that shows the succesion of stimulu in your experiment and can help others to understand it.
+NOTE: This functionality does not work in google colab since we need access to simulate a browser, but you can use it when running SweetBean on your personal computer
+# this functionality depends on addiditional functionalities that are not in the core package of sweetbean. We need to install these optional dependencies
+!pip install html2image
+!pip install pillow
+!pip install numpy
+!pip install opencv-python
+
+# import the functionality
+from sweetbean.sequence import sequence_to_image
+
+# we can now create the image by providing additional arguments that are the labels of the images
+sequence_to_image(stroop_block, durations=['600', '800', '200-500', '2500'])
+
+Here, we learn how to add a feedback stimulus indicating a correct or false response.
+!pip install sweetbean
+
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ +Requirement already satisfied: sweetbean in /usr/local/lib/python3.8/dist-packages (0.0.19) ++
Now, we are looking to incorporate responses from the participant and response feedback to the experiment. Specifically, we would like the participant to press the key f
using their left index finger when the ink color of the color word is red. Similarly, we would like the participant to press the key j
with their right index finger when the color is green.
You can practice your SweetBean skills by implementing these instructions in a block (if you feel comfortable creating text stimuli you can skip this part and use the instructions block in the solution):
+# Enter your code here:
+
# imports
+from sweetbean.stimulus import TextStimulus
+from sweetbean.sequence import Block
+
+# Creating the Instructions
+welcome = TextStimulus(text="Welcome to our experiment.<br>Here, you will have to react to the ink color of a color word.<br>Press SPACE to continue", choices=[' '])
+instruction_red = TextStimulus(text="If the ink color is <b>red<b>,<br>press <b>F<b> with your left index finger as fast as possible.<br>Press F to continue", choices=['f'])
+instruction_green = TextStimulus(text="If the ink color is <b>green<b>,<br>press <b>J<b> with your right index finger as fast as possible.<br>Press J to continue", choices=['j'])
+instructions_end = TextStimulus(text="The experiment will start now.<br>React as fast an as accurate as possible.<br>Remember:<br>React to the ink color not the meaning of the word.<br>Ress SPACE to continus", choices=[' '])
+
+# Creating the stimulus sequence
+instructions_sequence = [welcome, instruction_red, instruction_green, instructions_end]
+
+# Creating the block
+instructions_block = Block(instructions_sequence)
+
timeline = [
+ {'color': 'red', 'word': 'RED'},
+ {'color': 'green', 'word': 'GREEN'},
+ {'color': 'green', 'word': 'RED'},
+ {'color': 'red', 'word': 'GREEN'},
+]
+
Do you remember how to declare the timeline variables(if you feel comfortable creating timeline variables, you can skip this part and use the solution)?
+# Enter your code here:
+
# import the functionality from sweetbean
+from sweetbean.parameter import TimelineVariable
+
+## declare the timeline variables
+
+# color: The name has to be color (it is the name in the timeline), and it has the levels red and green
+color = TimelineVariable(name="color", levels=["red", "green"])
+
+# word: The name has to be word (it is the name in the timeline), and it has the levels RED and GREEN
+word = TimelineVariable(name="word", levels=["RED", "GREEN"])
+
In SweetBean, in addition to static parameters and timeline parameters, we can also use dervived parameters. These are parameters, that derive from other features of the trial. Here, we want to derive the correct key from the ink color of the stimulus. Remember: f
for red color words and j
for green color words.
To define the various levels of the derived parameter, we need to create functions (known as predicates) for each level. These functions should accept the features that the derived parameter depends on and should return True
when the conditions for the specific level of the derived parameter are met.
For example, the function for the level f
should return True
if the color is red, and False
in all other cases. Similarly, the function for the level j
should return True
if the color is green, and False
in all other cases.
# defining the predicate for the f-level of the "correct response" parameter
+def is_correct_f(color):
+ return color == 'red'
+
+# defining the predicate for the j-level of the "correct response" paramater
+def is_correct_j(color):
+ return color == 'green'
+
Some people might have trouble understanding the code above. A equivalent writing with an if and else statement is:
+def is_correct_f(color):
+ if color == 'red':
+ return True
+ else:
+ return False
+
+def is_correct_j(color):
+ if color == 'green':
+ return True
+ else:
+ return False
+
+With the predicates defined, we can now define the corresponding levels. The first argument of the derived level constructor is it's value, the second is the predicate and the third is a list of the parameters it depends on.
+# importing the functionality
+from sweetbean.parameter import DerivedLevel
+
+# declare the f level
+correct_response_f = DerivedLevel(value='f', predicate=is_correct_f, factors=[color])
+
+# declare the j level
+correct_response_j = DerivedLevel('j', is_correct_j, [color])
+
Now, that we declared the levels of the parameter, we can declare the parameter itself.
+# importing the functionality
+from sweetbean.parameter import DerivedParameter
+
+# declare the "correct response" parameter
+correct_response = DerivedParameter(name='correct_response', levels=[correct_response_f, correct_response_j])
+
The derived parameter can be used in the creation of the stimulus just as a static or a timeline parameter. Here, we introduce another feature of the text stimulus: We can provide a correct_key parameter. This correct_key parameter can be used for data analysis later, but it can also be used to provide feedback (we will see how this is used later in this tutorial)
+# imports
+from sweetbean.stimulus import TextStimulus
+
+# declaring the stimulus
+stroop = TextStimulus(duration=2500, text=word, color=color, choices=['j', 'f'], correct_key=correct_response)
+
This is a stimulus that shows a color word in the color and with the word provided by the timeline. The stimulus is shown for 2500ms or till a response is given. The user can press either j
or f
.
To add feedback, we introduce yet another type of variable. The data-variable. At the end of each stimulus, data for this stimulus is stored in a container. This data contains stimulus features like color, word and duration, but also user input like key presses. With the data variable, we can access this container to create adaptive stimuli like feedback.
+We create a data variable for the correct-data of a trial. This is a special data point, that can be accessed in stimuli that have the correct_key parameter. A comprehensive list of data points that can be accessed will be made available in the documentation (It is mainly the data that gets stored via jsPsych.
+# import
+from sweetbean.parameter import DataVariable
+
+# declare the data variable
+correct = DataVariable('correct', [True, False])
+
In the same way we can access timeline variables in predicate functions, we can also access data variables.
+# positive feedback after correct responses (remember the correct data variable has boolean levels itself)
+def is_positive_feedback(correct):
+ return correct
+
+# negative feedback after incorrect responses
+def is_negative_feedback(correct):
+ return not correct
+
Now we create the levels for the text parameter of the feedback stimulus.
+Note: We provide an additional argument to this constructor. The purpose of this argument is to access the data variable of the stimulus that came before the current stimulus, which is the stroop stimulus in this case.
+The number 1
provided at the end of the constructor indicates that we want to access the data variable of the stimulus that occurred one stimulus back in time, which is the stroop stimulus. If we were to include an additional onset between the stroop and the feedback stimuli, we could insert a blank screen and then access the correct-data variable of the stimulus that occurred two stimuli back by providing a 2
as the last argument of the constructor.
positive_word_feedback = DerivedLevel('correct', is_positive_feedback, [correct], 1)
+negative_word_feedback = DerivedLevel('false', is_negative_feedback, [correct], 1)
+
We create the word parameter for the feedback stimulus
+feedback_word = DerivedParameter('feedback_word', [positive_word_feedback, negative_word_feedback])
+
Maybe we also want to change the color of the feedback (green for positive and red for negative feedback). Can you create the derived parameter?
+# Enter your code:
+# ...
+feedback_color = None
+
# create the levels
+positive_color_feedback = DerivedLevel('green', is_positive_feedback, [correct], 1)
+negative_color_feedback = DerivedLevel('red', is_negative_feedback, [correct], 1)
+# create the parameter
+feedback_color = DerivedParameter('feedback_color', [positive_color_feedback, negative_color_feedback])
+
feedback = TextStimulus(duration=1000, text=feedback_word,color=feedback_color)
+
You can practice your SweetBean skills by adding a fixation cross and finishing experiment (Remember there are two blocks now, if you feel comfortable creating blocks and experiments you can skip this part and use the code in the solution):
+# Enter your code:
+
# import the functionality from sweetbean to create experiments
+from sweetbean.sequence import Block, Experiment
+
+# fixation stimulus
+fixation = TextStimulus(800, '+')
+
+# create a stimulus sequence
+stimulus_sequence = [fixation, stroop, feedback]
+
+# create the trial block
+trial_block = Block(stimulus_sequence, timeline)
+
+# create the experiment from the two blocks
+experiment = Experiment([instructions_block, trial_block])
+
+# export to the html file
+experiment.to_html('index.html')
+
Here, we create a task switching experiment.
+!pip install sweetbean
+
Given the following timeline, could you program a task switching experiment?
+timeline = [
+ {'color': 'red', 'word': 'RED', 'task': 'color_naming'},
+ {'color': 'green', 'word': 'GREEN', 'task': 'color_naming'},
+ {'color': 'green', 'word': 'RED', 'task': 'word_reading'},
+ {'color': 'red', 'word': 'GREEN', 'task': 'word_reading'},
+]
+
First we want to declare the timeline variables
+# Enter your code here:
+
# imports
+from sweetbean.parameter import TimelineVariable
+
+color = TimelineVariable('color', ['red', 'green'])
+word = TimelineVariable('word', ['RED', 'GREEN'])
+task = TimelineVariable('task', ['color_naming', 'word_reading'])
+
The fixation cross will vary between the two tasks, maybe we can show a +
, when the task is color_naming, and a x
when the task is word_reading. We can do this with derived parameters
# Enter your code here:
+
from sweetbean.parameter import DerivedLevel, DerivedParameter
+
+# Predicates
+def is_fixation_plus(task):
+ return task == 'color_naming'
+
+def is_fixation_x(task):
+ return task == 'word_reading'
+
+# Levels
+x_shape = DerivedLevel('x', is_fixation_x, [task])
+plus_shape = DerivedLevel('+', is_fixation_plus, [task])
+
+# parameter
+fixation_shape = DerivedParameter('fixation_shape', [x_shape, plus_shape])
+
Now let's create a correct response parameter. This one is tricky! It will depend on the color, the word and the task. So the predicate will have three input arguments. Let's say we want the participant to press f when the color is "red" in the color_naming task or the word is "RED" in the word_reading task. They should press j when the color is "green" in the color_naming task or the word is "GREEN" in the word_reading task.
+# Predicate for f
+def is_correct_f(word, color, task):
+ return (task == 'word_reading' and word == 'RED') or \
+ (task == 'color_naming' and color == 'red')
+
+# Derived level for f
+f_key = DerivedLevel('f', is_correct_f, [word, color, task])
+
+# Enter your code for the
+# Predicate for j:
+
+# Derived level for j:
+
+# Parameter for the response:
+
# There are multiple solutions. Can you see why this one works?
+def is_correct_j(word, color, task):
+ return not is_correct_f(word, color, task)
+
+# j level
+j_key = DerivedLevel('j', is_correct_j, [word, color, task])
+
+# Parameter for the response
+correct_key = DerivedParameter('correct', [j_key, f_key])
+
Now, create the stimuli, the block and the experiment
+# Enter your code here:
+
from sweetbean.stimulus import TextStimulus
+from sweetbean.sequence import Block, Experiment
+
+# Stimuli
+fixation = TextStimulus(1000, fixation_shape)
+so_s = TextStimulus(800)
+stroop = TextStimulus(2000, word, color, ['j', 'g'], correct_key)
+so_f = TextStimulus(300)
+
+# Block
+train_block = Block([fixation, so_s, stroop, so_f], timeline)
+experiment = Experiment([train_block])
+
+# Experiment
+experiment.to_html('index.html')
+
From Zero to Hero in 5 Tutorials.
+In 5 Lessons, you learn everything that you need to know to create a Stroop experiment with SweetBean. At the end you +will know how different variable types in SweetBean work and how to use them to create a task switching experiment with +Stroop stimuli.
+ + + + + + + + + + + + + + +"""
+The text 'press a or b or wait' is shown in pink for 2000ms. The allowed responses are 'a' and 'b'.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+
EVENT SEQUENCE
+text = TextStimulus(
+ duration=2000, text="press a or b or wait", color="pink", choices=["a", "b"]
+)
+
event_sequence = [text]
+
BLOCK DESIGN
+block = Block(event_sequence)
+experiment = Experiment([block])
+
experiment.to_html("basic.html")
+
"""
+A fixation cross is followed by a blank screen, followed by a Stroop stimulus, followed by another
+blank screen. The fixation cross is shown for 1000ms. The first blank screen is shown for 800ms
+the second for 300ms. The Stroop stimulus is shown for 2000ms. This is a task-switching experiment.
+The shape of the fixation cross determines the task. An 'x indicates a word reading task, and a '+'
+indicates the color naming task. The color of the Stroop task and its word are indicated by a
+timeline, as is the task. In the word reading task, the correct response to "RED" is pressing f,
+and to "GRREN" is pressing j. Similarly, the correct response to a red word in the color naming
+task is f, and the correct response to a green word is j.
+"""
+
from sweetbean.parameter import DerivedLevel, DerivedParameter, TimelineVariable
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+
timeline = [
+ {"color": "red", "word": "RED", "task": "color_naming"},
+ {"color": "green", "word": "GREEN", "task": "color_naming"},
+ {"color": "green", "word": "RED", "task": "word_reading"},
+ {"color": "red", "word": "GREEN", "task": "word_reading"},
+]
+
EVENT SEQUENCE
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+task = TimelineVariable("task", ["color_naming", "word_reading"])
+
def is_fixation_x(task):
+ return task == "word_reading"
+
def is_fixation_plus(task):
+ return task == "color_naming"
+
x_shape = DerivedLevel("x", is_fixation_x, [task])
+plus_shape = DerivedLevel("+", is_fixation_plus, [task])
+
fixation_shape = DerivedParameter("fixation_shape", [x_shape, plus_shape])
+
def is_correct_f(word, color, task):
+ return (task == "word_reading" and word == "RED") or (
+ task == "color_naming" and color == "red"
+ )
+
def is_correct_j(word, color, task):
+ return not is_correct_f(word, color, task)
+
j_key = DerivedLevel("j", is_correct_j, [word, color, task])
+f_key = DerivedLevel("f", is_correct_f, [word, color, task])
+
correct_key = DerivedParameter("correct", [j_key, f_key])
+
fixation = TextStimulus(1000, fixation_shape)
+so_s = TextStimulus(800)
+stroop = TextStimulus(2000, word, color, ["j", "g"], correct_key)
+so_f = TextStimulus(300)
+
event_sequence = [fixation, so_s, stroop, so_f]
+
BLOCK DESIGN
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
experiment.to_autora("package.json", "main.js")
+
"""
+A fixation cross is followed by a blank screen, followed by a Stroop stimulus, followed by another
+blank screen, followed by feedback. The fixation cross is shown for 1000ms. The first blank screen
+is shown for 400ms the second for 300ms. The Stroop stimulus is shown for 2000ms. Feedback is shown
+for 800ms. This is a task-switching experiment. The color of the Stroop task and its words are
+indicated by the experimental design. The correct response to a red word in the color naming task
+is f, and the correct response to a green word is j. If the participant's response was correct,
+the text "correct" is shown. If the participant's response was false, the text "false" is shown.
+"""
+
from sweetbean.parameter import (
+ DataVariable,
+ DerivedLevel,
+ DerivedParameter,
+ TimelineVariable,
+)
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+
timeline = [
+ {"color": "red", "word": "RED"},
+ {"color": "green", "word": "GREEN"},
+ {"color": "green", "word": "RED"},
+ {"color": "red", "word": "GREEN"},
+]
+
EVENT SEQUENCE
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
def is_correct_f(color):
+ return color == "red"
+
def is_correct_j(color):
+ return not is_correct_f(color)
+
j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
correct_key = DerivedParameter("correct", [j_key, f_key])
+
# Creating a data variable
+correct = DataVariable("correct", [True, False])
+
# Predicates
+def is_correct(correct):
+ return correct
+
def is_false(correct):
+ return not correct
+
# Derived Levels
+correct_feedback = DerivedLevel("correct", is_correct, [correct], 2)
+false_feedback = DerivedLevel("false", is_false, [correct], 2)
+
# Derived Parameter
+feedback_text = DerivedParameter("feedback_text", [correct_feedback, false_feedback])
+
# Using it in the stimulus
+fixation = TextStimulus(1000, "+")
+so_s = TextStimulus(400)
+stroop = TextStimulus(2000, word, color, ["j", "f"], correct_key)
+so_f = TextStimulus(300)
+feedback = TextStimulus(800, feedback_text)
+
event_sequence = [fixation, so_s, stroop, so_f, feedback]
+
BLOCK DESIGN
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
experiment.to_html("derived_window.html")
+
Display colored text.
+ + + + + + + + + + + + + + +from sweetbean.parameter import TimelineVariable
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import (
+ BlankStimulus,
+ FeedbackStimulus,
+ FixationStimulus,
+ FlankerStimulus,
+)
+
timeline = [
+ {"direction": "left", "distractor": "right", "correct_key": "f"},
+ {"direction": "right", "distractor": "right", "correct_key": "j"},
+ {"direction": "left", "distractor": "left", "correct_key": "f"},
+ {"direction": "right", "distractor": "left", "correct_key": "j"},
+]
+
direction = TimelineVariable("direction", ["left", "right"])
+distractor = TimelineVariable("distractor", ["left", "right"])
+correct_key = TimelineVariable("correct_key", ["j", "f"])
+
fixation = FixationStimulus(1000)
+so_s = BlankStimulus(400)
+flanker = FlankerStimulus(2000, direction, distractor, ["j", "f"], correct_key)
+so_f = BlankStimulus(300)
+feedback = FeedbackStimulus(800, 2)
+
train_block = Block([fixation, so_s, flanker, so_f, feedback], timeline)
+experiment = Experiment([train_block])
+
experiment.to_html("predefined.html")
+
from sweetbean.parameter import TimelineVariable
+from sweetbean.sequence import Block, Experiment, Timeline
+from sweetbean.stimulus import TextStimulus
+
t_0 = Timeline("t_0", "../assets/timelines/${global.ATTEMPTS}/0.json")
+t_1 = Timeline("t_1", "../assets/timelines/${global.ATTEMPTS}/1.json")
+t_2 = Timeline("t_2", "../assets/timelines/${global.ATTEMPTS}/2.json")
+
color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
text = TextStimulus(duration=2000, text=word, color=color, choices=["a", "b"])
+
block_0 = Block([text], t_0)
+block_1 = Block([text], t_1)
+block_2 = Block([text], t_2)
+experiment = Experiment([block_0, block_1, block_2])
+
experiment.to_honeycomb(path_main="main.js")
+
"""
+A fixation cross is followed by a blank screen, followed by a Stroop stimulus, followed by another
+blank screen. The fixation cross is shown for 1000ms. The soa and iti durations are indicated by
+the experimental design. The color of the Stroop task and its words are also indicated by the
+experimental design. The correct response to a red word is f, and the correct response to a
+green word is j.
+"""
+
from sweetbean.parameter import DerivedLevel, DerivedParameter, TimelineVariable
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+
timeline = [
+ {"color": "red", "word": "RED", "so_s": 200, "so_f": 1000},
+ {"color": "green", "word": "GREEN", "so_s": 1000, "so_f": 100},
+ {"color": "green", "word": "RED", "so_s": 100, "so_f": 2000},
+ {"color": "red", "word": "GREEN", "so_s": 1000, "so_f": 1000},
+]
+
EVENT SEQUENCE
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+so_s_duration = TimelineVariable("so_s", [100, 200, 1000])
+so_f_duration = TimelineVariable("so_f", [100, 1000, 2000])
+
def is_correct_f(color):
+ return color == "red"
+
def is_correct_j(color):
+ return not is_correct_f(word)
+
j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
correct_key = DerivedParameter("correct", [j_key, f_key])
+
fixation = TextStimulus(800, "+")
+so_s = TextStimulus(so_s_duration)
+stroop = TextStimulus(2000, word, color, ["f", "j"], correct_key)
+so_f = TextStimulus(so_f_duration)
+
event_sequence = [fixation, so_s, stroop, so_f]
+
BLOCK DESIGN
+train_block = Block([fixation, so_s, stroop, so_f], timeline)
+experiment = Experiment([train_block])
+
experiment.to_html("timeline.html")
+
from sweetbean.parameter import TimelineVariable
+from sweetbean.sequence import Block, sequence_to_image
+from sweetbean.stimulus import BlankStimulus, TextStimulus
+
timeline = [
+ {"color": "red", "word": '<div style="font-size: 110pt">RED</div>', "soa": 300},
+ {"color": "green", "word": "GREEN", "soa": 200},
+ {"color": "green", "word": "RED", "soa": 400},
+ {"color": "red", "word": "GREEN", "soa": 500},
+]
+
** declare the timeline variables ** #
+# color: The name has to be color (it is the name in the timeline),
+# and it has the levels red and green
+color = TimelineVariable(name="color", levels=["red", "green"])
+
# word: The name has to be word (it is the name in the timeline),
+# and it has the levels RED and GREEN
+word = TimelineVariable(name="word", levels=["RED", "GREEN"])
+
# declare the timeline variable for soa
+soa = TimelineVariable(name="soa", levels=[200, 300, 400, 500])
+
** declaring the different stimuli ** #
+# fixation onset (a blank screen for 600ms)
+fixation_onset = BlankStimulus(duration=600)
+
# fixation cross (the character "+" shown for 800ms)
+fixation = TextStimulus(duration=800, text="<div style='font-size: 110pt'>+</div>")
+
# stimulus onset (a blank screen shown for 200ms)
+stimulus_onset = BlankStimulus(duration=soa)
+
# the Stroop stimulus. Here instead of fixed parameters,
+# we use the timeline variables, that we defined previously.
+stroop = TextStimulus(duration=2500, text=word, color=color)
+
stroop_block = Block([fixation_onset, fixation], timeline)
+
sequence_to_image(stroop_block, durations=["600", "800"])
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import ImageStimulus
+
stim_1 = ImageStimulus(
+ src="https://media.istockphoto.com/id/120492078/photo/"
+ "banana.jpg?s=1024x1024&w=is&k=20&c=M9KLVNgqLft_btWgSu0iZAmdv2asI11Qel-6fsQK140=",
+ choices=[" "],
+)
+
trial_sequence = Block([stim_1])
+experiment = Experiment([trial_sequence])
+experiment.to_html("basic.html")
+
Display Images
+ + + + + + + + + + + + + + +An overview of the stimuli that can be used in SweetBean experiments.
+ + + + + + + + + + + + + + +"""
+Two dot patterns are shown with a different number of dots for 2000ms.
+The number of dots is given by the experimental design.
+"""
+
from sweetbean.parameter import TimelineVariable
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import FeedbackStimulus, RandomDotPatternsStimulus
+
timeline = [
+ {"intensities": [90, 100], "correct_key": "y"},
+ {"intensities": [90, 90], "correct_key": "n"},
+ {"intensities": [10, 20], "correct_key": "y"},
+ {"intensities": [100, 90], "correct_key": "y"},
+ {"intensities": [10, 10], "correct_key": "n"},
+ {"intensities": [20, 20], "correct_key": "y"},
+ {"intensities": [100, 100], "correct_key": "n"},
+ {"intensities": [20, 20], "correct_key": "n"},
+]
+
# EVENT SEQUENCE
+intensities = TimelineVariable(
+ "intensities",
+ [[90, 100], [90, 90], [10, 20], [100, 90], [20, 20], [100, 100], [10, 10]],
+)
+
correct_key = TimelineVariable("correct_key", ["y", "n"])
+
rok = RandomDotPatternsStimulus(
+ duration=2000,
+ number_of_oobs=intensities,
+ number_of_apertures=2,
+ choices=["y", "n"],
+ correct_key=correct_key,
+)
+
feedback = FeedbackStimulus(500)
+event_sequence = [rok, feedback]
+
BLOCK DESIGN
+block = Block(event_sequence, timeline)
+experiment = Experiment([block])
+
experiment.to_html("rok_weber_fechner.html")
+
Display static colored dots.
+ + + + + + + + + + + + + + +""" Weber Fechner
+
+An example for Weber Fechner. Here, we have a simple stimulus timeline:
+[fixation, blank, rdp, blank]
+
+"""
+
from sweetbean.parameter import TimelineVariable
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import (
+ BlankStimulus,
+ FixationStimulus,
+ RandomDotPatternsStimulus,
+)
+
timeline = [
+ {"S1": 40, "S2": 70},
+ {"S1": 70, "S2": 70},
+ {"S1": 70, "S2": 40},
+ {"S1": 70, "S2": 70},
+ {"S1": 40, "S2": 70},
+ {"S1": 70, "S2": 40},
+ {"S1": 40, "S2": 40},
+ {"S1": 40, "S2": 40},
+ {"S1": 40, "S2": 40},
+ {"S1": 70, "S2": 40},
+]
+
fixation = FixationStimulus(800)
+
blank_1 = BlankStimulus(400)
+blank_2 = BlankStimulus(1000)
+
s1 = TimelineVariable("S1", [40, 70])
+s2 = TimelineVariable("S2", [40, 70])
+
We can use these variables in the stimuli declaration:
+rdp = RandomDotPatternsStimulus(
+ duration=1000,
+ number_of_oobs=[s1, s2],
+ number_of_apertures=2,
+ choices=["y", "n"],
+)
+
event_sequence = [fixation, blank_1, rdp, blank_2]
+
block = Block(event_sequence, timeline)
+
experiment = Experiment([block])
+
experiment.to_html("rok_weber_fechner.html")
+
"""
+The rok 'press a or b or wait' is shown in pink for 2000ms. The allowed responses are 'a' and 'b'.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import ROKStimulus
+
EVENT SEQUENCE
+rok = ROKStimulus(
+ duration=100000,
+ number_of_oobs=500,
+ coherent_movement_direction=100,
+ coherent_orientation=0,
+ coherence_movement=50,
+ coherence_orientation=90,
+)
+
event_sequence = [rok]
+
BLOCK DESIGN
+block = Block(event_sequence)
+experiment = Experiment([block])
+
experiment.to_html("basic.html")
+
Display moving and orientated objects (such as triangles).
+ + + + + + + + + + + + + + +Display a survey to the user.
+ + + + + + + + + + + + + + +"""
+A welcoming text the participant is shown until the participant presses the spacebar. The text says,
+'Welcome! We show a survey. Press SPACE to continue'. This is followed by a survey with two
+questions that the participant is supposed to answer on a likert scale. The first question is
+'How are you feeling?' and the participant is supposed to rate their feeling on a scale from
+-2 to 2 with 5 levels. The second question is 'Do you like this tool?' on a scale from 1 to 3.
+Then, the participant is supposed to rate five animals on their cuteness from 1 to 5.
+The animals are a bunny, a shark, a spider, a cat, and a dog.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import LikertSurveyStimulus, TextStimulus
+
EVENT SEQUENCE
+stim_1 = TextStimulus(
+ text="Welcome! We show a survey. Press SPACE to continue",
+ choices=[" "],
+)
+
stim_2 = LikertSurveyStimulus(
+ prompts=[
+ {"How are you feeling?": [-2, -1, 0, 1, 2]},
+ {"Do you like this tool?": [0, 1, 3]},
+ ]
+)
+
stim_3 = TextStimulus(
+ text="Rate the following animals on a scale from 1-5 on their cuteness.",
+ choices=[" "],
+)
+
stim_4 = LikertSurveyStimulus.from_scale(
+ prompts=["bunny", "shark", "spider", "cat", "dog"], scale=[1, 2, 3, 4, 5]
+)
+
event_sequence = [stim_1, stim_2, stim_3, stim_4]
+
BLOCK DESIGN
+trial_sequence = Block(event_sequence)
+experiment = Experiment([trial_sequence])
+experiment.to_html("likert.html")
+
"""
+After the participant is greeted with 'Welcome! We show a survey. Press SPACE to continue' until
+they press the spacebar, they are asked two multiple-choice questions. One is about how they are,
+which they can rate as bad, good, or fine. The other is about their handedness.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import MultiChoiceSurveyStimulus, TextStimulus
+
EVENT SEQUENCE
+stim_1 = TextStimulus(
+ text="Welcome! We show a survey. Press SPACE to continue",
+ choices=[" "],
+)
+
stim_2 = MultiChoiceSurveyStimulus(
+ prompts=[
+ {"How are you?": ["bad", "good", "fine"]},
+ {"What is your handedness?": ["left", "right", "other", "prefer not to say"]},
+ ]
+)
+
event_sequence = [stim_1, stim_2]
+
BLOCK DESIGN
+trial_sequence = Block(event_sequence)
+experiment = Experiment([trial_sequence])
+experiment.to_html("multi_choice.html")
+
"""
+After the participant is greeted with 'Welcome! We show a survey. Press SPACE to continue' until
+they press the spacebar, they are asked free-from questions about how old they are, their gender,
+and their handedness.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus, TextSurveyStimulus
+
EVENT SEQUENCE
+stim_1 = TextStimulus(
+ text="Welcome! We show a survey. Press SPACE to continue",
+ choices=[" "],
+)
+
stim_2 = TextSurveyStimulus(
+ prompts=["How old are you?", "What is your gender?", "What is your handedness?"]
+)
+
event_sequence = [stim_1, stim_2]
+
BLOCK DESIGN
+trial_sequence = Block(event_sequence)
+experiment = Experiment([trial_sequence])
+experiment.to_html("text.html")
+
"""
+First, a welcoming text, 'Welcome! We show some Symbols. Press SPACE to continue' is shown until
+the participant presses the Spacebar. Then a square in purple is followed by a triangle in red
+followed by a circle in green. All of these symbols are shown for 3000ms or until the participant
+presses the key f or j. The correct key for the purple square and the red triangle is f, and
+the correct key for the green circle is j.
+"""
+
from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import SymbolStimulus, TextStimulus
+
EVENT SEQUENCE
+stim_1 = TextStimulus(
+ text="Welcome! We show some Symbols. Press SPACE to continue",
+ choices=[" "],
+)
+
stim_2 = SymbolStimulus(
+ duration=3000, symbol="square", color="#f0f", choices=["f", "j"], correct_key="f"
+)
+
stim_3 = SymbolStimulus(
+ duration=3000, symbol="triangle", color="red", choices=["f", "j"], correct_key="f"
+)
+
stim_4 = SymbolStimulus(
+ duration=3000, symbol="circle", color="green", choices=["f", "j"], correct_key="j"
+)
+
event_sequence = [stim_1, stim_2, stim_3, stim_4]
+
BLOCK DESIGN
+trial_sequence = Block(event_sequence)
+experiment = Experiment([trial_sequence])
+experiment.to_html("basic.html")
+
Display colored symbols.
+ + + + + + + + + + + + + + +In the previous example, we created a derived parameter that determined the correct key based on the color of the Stroop +stimulus. We can now create a data variable that determines whether the participant pressed the correct key. We can then +use this data variable to create feedback for the participant.
+Assume the same timeline as before and the same derived parameter:
+from sweetbean.parameter import (
+ DataVariable,
+ DerivedLevel,
+ DerivedParameter,
+ TimelineVariable,
+)
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+
+timeline = [
+ {"color": "red", "word": "RED"},
+ {"color": "green", "word": "GREEN"},
+ {"color": "green", "word": "RED"},
+ {"color": "red", "word": "GREEN"},
+]
+
+# EVENT SEQUENCE
+
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
+
+def is_correct_f(color):
+ return color == "red"
+
+
+def is_correct_j(color):
+ return not is_correct_f(color)
+
+
+j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
+correct_key = DerivedParameter("correct", [j_key, f_key])
+
Now we can create a DataVariable
that determines whether the participant pressed the correct key. The 1
in the
+declaration indicates from which stimulus to get the variable (1 = previous, 2 = second previous, etc.).
correct = DataVariable("correct", [True, False])
+
+# Predicates
+def is_correct(correct):
+ return correct
+
+
+def is_false(correct):
+ return not correct
+
+
+correct_feedback = DerivedLevel("correct", is_correct, [correct], 1)
+false_feedback = DerivedLevel("false", is_false, [correct], 1)
+
Again, we can use the Derived Levels to create a Derived Parameter and use it in a feedback stimulus.
+feedback_text = DerivedParameter("feedback_text", [correct_feedback, false_feedback])
+
+fixation = FixationStimulus(500)
+stroop = TextStimulus(2000, word, color, ["j", "f"], correct_key)
+feedback = TextStimulus(800, feedback_text)
+
+event_sequence = [fixation, stroop, feedback]
+
+
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
+experiment.to_html("stroop.html")
+
In the previous examples, we have seen how to create a simple block with a fixed trials. Here, we will introduce derived
+variables. These variables allow us let a TimeLineVariable
depend on another TimeLineVariable
. This can be useful when providing feedback as a function of the participant's response.
Let us assume the same timeline:
+timeline = [
+ {"color": "red", "word": "RED"},
+ {"color": "green", "word": "GREEN"},
+ {"color": "green", "word": "RED"},
+ {"color": "red", "word": "GREEN"},
+]
+
First, we create the timeline variables
+from sweetbean.parameter import TimelineVariable
+
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
Now, we can create conditions (in form of functions). Here, we want to specify that the correct key is f if the color of +the Stroop Stimulus is red and j if it is green.
+def is_correct_f(color):
+ return color == "red"
+
+
+def is_correct_j(color):
+ return not is_correct_f(color)
+
We can now create a derived level by specifying the value of the level, the condition and on what variable it is +depending on:
+from sweetbean.parameter import DerivedLevel
+
+j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
We can now create a derived parameter with the derived levels:
+from sweetbean.parameter import DerivedParameter
+
+correct_key = DerivedParameter("correct", [j_key, f_key])
+
Now, we can use the derived Parameter in our stimuli:
+from sweetbean.stimulus import TextStimulus, FixationStimulus
+
+fixation = FixationStimulus(1000)
+stroop = TextStimulus(2000, word, color, ["j", "f"], correct_key)
+
Finally, we can create a block with the timeline. Here, we pass in the timeline to the block. The event sequence will +then be repeated as many times as there are entries in the timeline using the variables defined above.
+event_sequence = [fixation, stroop]
+
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
+experiment.to_html("stroop.html")
+
There is one additional variable, that we can use: The data variable. It is a special variable that accesses data from previous stimuli to dynamically adjust the current stimulus. For example, we can create Feedback: +Data Variables
+ + + + + + + + + + + + + + +This guide provides an overview of SweetBean features.
+SweetBean can be used to run experiments with Large Language Models (LLMs) as synthetic participants.
+We specify the experiment as above:
+from sweetbean.sequence import Block, Experiment
+from sweetbean.stimulus import TextStimulus
+from sweetbean.parameter import TimelineVariable, DataVariable, DerivedLevel, DerivedParameter
+
+# TIMELINE
+timeline = [
+ {"color": "red", "word": "RED", "correct_key": "f"},
+ {"color": "green", "word": "GREEN", "correct_key": "j"},
+ {"color": "green", "word": "RED", "correct_key": "f"},
+ {"color": "red", "word": "GREEN", "correct_key": "j"},
+]
+
+# EVENT SEQUENCE
+
+color = TimelineVariable("color", ["red", "green"])
+word = TimelineVariable("word", ["RED", "GREEN"])
+
+
+def is_correct_f(color):
+ return color == "red"
+
+
+def is_correct_j(color):
+ return not is_correct_f(color)
+
+
+j_key = DerivedLevel("j", is_correct_j, [color])
+f_key = DerivedLevel("f", is_correct_f, [color])
+
+correct_key = DerivedParameter("correct", [j_key, f_key])
+
+# Creating a data variable
+correct = DataVariable("correct", [True, False])
+
+
+# Predicates
+def is_correct(correct):
+ return correct
+
+
+def is_false(correct):
+ return not correct
+
+
+# Derived Levels
+correct_feedback = DerivedLevel("correct", is_correct, [correct], 2)
+false_feedback = DerivedLevel("false", is_false, [correct], 2)
+
+# Derived Parameter
+feedback_text = DerivedParameter("feedback_text", [correct_feedback, false_feedback])
+
+# Using it in the stimulus
+fixation = TextStimulus(1000, "+")
+
+so_s = TextStimulus(400)
+stroop = TextStimulus(2000, word, color, ["j", "f"], correct_key)
+so_f = TextStimulus(300)
+feedback = TextStimulus(800, feedback_text)
+
+event_sequence = [fixation, so_s, stroop, so_f, feedback]
+
+# BLOCK DESIGN
+
+train_block = Block(event_sequence, timeline)
+experiment = Experiment([train_block])
+
To test what the LLM "sees", we can run the experiment as chat on ourselves. If we set multitude to true we will not see +the full chat history but only the last generated prompt.
+data = experiment.run_on_language(get_input=input, multiturn=True)
+
We can run the experiment on any function that takes a string and returns a string. For example, on the +centauer model:
+pip install unsloth "xformers==0.0.28.post2"
+
We then define a function that generates the next token given a prompt:
+from unsloth import FastLanguageModel
+import transformers
+
+model, tokenizer = FastLanguageModel.from_pretrained(
+ model_name="marcelbinz/Llama-3.1-Centaur-8B-adapter",
+ max_seq_length=32768,
+ dtype=None,
+ load_in_4bit=True,
+)
+FastLanguageModel.for_inference(model)
+
+pipe = transformers.pipeline(
+ "text-generation",
+ model=model,
+ tokenizer=tokenizer,
+ trust_remote_code=True,
+ pad_token_id=0,
+ do_sample=True,
+ temperature=1.0,
+ max_new_tokens=1,
+)
+
+
+def generate(input):
+ return pipe(input)[0]["generated_text"]
+
To make the process more robust, we also add a function that parses the response and compares the output to the +correct key:
+def parse_response(response, correct_key):
+ return correct_key in response
+
Now, we can run the experiment on the model:
+data = experiment.run_on_language(get_input=generate, multiturn=False)
+
Note: The run_on_language
function will return a dictionary with the data from the experiment. Any model
+(for example,
+using OpenAI, HuggingFace, LLama
+or Google Api) can be used as a synthetic participant.
SweetBean can be installed via pip:
+pip install sweetbean
+
To create a SweetBean experiment, you define a sequence of stimuli. +For example, the following code defines a simple text stimulus to welcome participants. `:
+from sweetbean.stimulus import TextStimulus
+
+welcome = TextStimulus("Welcome to the experiment! Press >>Space<< to begin", choices=[" "])
+
We then use stimuli to create a sequence of events:
+event_sequence = [welcome]
+
From sequences, we can create a block:
+from sweetbean.sequence import Block
+
+introduction_block = Block(event_sequence)
+
Finally, we can create the experiment...
+from sweetbean.sequence import Experiment
+
+experiment = Experiment([indroduction_block])
+
... and export the experiment as html file
+experiment.to_html("basic.html")
+
Let us create a stroop experiment. The trials should start with a fixation cross, followed by a colored word:
+from sweetbean.stimulus import TextStimulus, FixationStimulus
+
+fixation_1 = FixationStimulus(duration=500)
+stroop_1 = TextStimulus("RED", color="red", choices=["r", "g"])
+fixation_2 = FixationStimulus(duration=500)
+stroop_2 = TextStimulus("GREEN", color="green", choices=["r", "g"])
+fixation_3 = FixationStimulus(duration=500)
+...
+
We can then create a sequence of events and blocks as before.
+event_sequence_stroop = [fixation_1, stroop_1, fixation_2, stroop_2, fixation_3, ...]
+stroop_block = Block(event_sequence_stroop)
+experiment = Experiment([introduction_block, stroop_block])
+experiment.to_html("stroop.html")
+
This way, we could use loops to create experiments with many trials. But a better way is to use
+the timeline variables
: Timeline Variables
In the previous examples, we have seen how to create a simple block with a fixed trials. Here, we will introduce +timeline variables that can be used to create more complex experiments with multiple trials.
+Consider the following timeline that consists of words and colors for a Stroop Task:
+timeline = [
+ {"color": "red", "word": 'RED'},
+ {"color": "green", "word": "GREEN"},
+ {"color": "green", "word": "RED"},
+ {"color": "red", "word": "GREEN"},
+]
+
First, we declare SweetBean Timeline Variables:
+color: The name has to be color (it is the name in the timeline), and it has the levels red and green +word: The name has to be word (it is the name in the timeline), and it has the levels RED and GREEN
+from sweetbean.parameter import TimelineVariable
+
+color = TimelineVariable(name="color", levels=["red", "green"])
+word = TimelineVariable(name="word", levels=["RED", "GREEN"])
+
Now, we can use these timeline variables when defining the stimuli:
+from sweetbean.stimulus import TextStimulus, FixationStimulus
+
+fixation = FixationStimulus(duration=500)
+stroop = TextStimulus(duration=1000, text=word, color=color)
+
Finally, we can create a block with the timeline. Here, we pass in the timeline to the block. The event sequence will +then be repeated as many times as there are entries in the timeline using the variables defined above.
+from sweetbean.sequence import Block, Experiment
+
+event_sequence = [fixation, stroop]
+stroop_block = Block(event_sequence, timeline)
+experiment = Experiment(stroop_block)
+experiment.to_html("stroop.html")
+
Additionally to timeline variables, we can also create derived variables. For example, we can create a variable that gives us the correct key press depending on the word of a StroopStimulus: +Derived Variable
+ + + + + + + + + + + + + + +