Beispiel #1
0
def __get_response_transition() -> Factor:
    color = Factor("color", ["red", "blue", "green"])
    motion = Factor("motion", ["up", "down"])
    task = Factor("task", ["color", "motion"])

    # Response Definition
    def response_left(task, color, motion):
        return (task == "color"  and color  == "red") or \
            (task == "motion" and motion == "up")

    def response_right(task, color, motion):
        return not response_left(task, color, motion)

    response = Factor("response", [
        DerivedLevel("left", WithinTrial(response_left,
                                         [task, color, motion])),
        DerivedLevel("right", WithinTrial(response_right,
                                          [task, color, motion]))
    ])

    return Factor("response transition", [
        DerivedLevel(
            "repeat",
            Transition(lambda responses: responses[0] == responses[1],
                       [response])),
        DerivedLevel(
            "switch",
            Transition(lambda responses: responses[0] != responses[1],
                       [response]))
    ])
def test_generate_argument_list_with_transition():
    color_repeats_level = DerivedLevel(
        "yes", Transition(lambda colors: colors[0] == colors[1], [color]))
    x_product = color_repeats_level.get_dependent_cross_product()

    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[0]) == [['red', 'red']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[1]) == [['red', 'blue']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[2]) == [['blue', 'red']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[3]) == [['blue', 'blue']]

    double_repeat_level = DerivedLevel(
        "name", Transition(lambda colors, texts: True, [color, text]))
    x_product = double_repeat_level.get_dependent_cross_product()

    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[0]) == [['red', 'red'], ['red', 'red']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[1]) == [['red', 'red'], ['red', 'blue']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[2]) == [['red', 'red'], ['blue', 'red']]
    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[3]) == [['red', 'red'],
                                               ['blue', 'blue']]

    assert DerivationProcessor.generate_argument_list(
        color_repeats_level, x_product[15]) == [['blue', 'blue'],
                                                ['blue', 'blue']]
Beispiel #3
0
def test_factor_validation():
    Factor("factor name", ["level 1", "level 2"])

    # Non-string name
    with pytest.raises(ValueError):
        Factor(56, ["level "])

    # Non-list levels
    with pytest.raises(ValueError):
        Factor("name", 42)

    # Empty list
    with pytest.raises(ValueError):
        Factor("name", [])

    # Invalid level type
    with pytest.raises(ValueError):
        Factor("name", [1, 2])

    # Valid level types, but not uniform.
    with pytest.raises(ValueError):
        Factor("name", ["level1", con_level])

    # Derived levels with non-uniform window types
    with pytest.raises(ValueError):
        Factor("name", [
            con_level,
            DerivedLevel(
                "other",
                Transition(lambda colors: colors[0] == colors[1], [color]))
        ])
def test_shift_window():
    assert DerivationProcessor.shift_window([[0, 0], [1, 1]],
                                            WithinTrial(lambda x: x, None),
                                            0) == [[0, 0], [1, 1]]
    assert DerivationProcessor.shift_window([[0, 0], [1, 1]],
                                            Transition(lambda x: x, None),
                                            4) == [[0, 4], [1, 5]]
    assert DerivationProcessor.shift_window([[0, 2, 4], [1, 3, 5]],
                                            Window(lambda x: x, [1, 2], 2, 3),
                                            6) == [[0, 8, 16], [1, 9, 17]]
    assert DerivationProcessor.shift_window([[1, 1, 1, 1], [2, 2, 2, 2]], Window(lambda x: x, [1, 2], 2, 4), 10) == \
        [[1, 11, 21, 31], [2, 12, 22, 32]]
Beispiel #5
0
def test_factor_validation():
    Factor("factor name", ["level 1", "level 2"])
    Factor("name", [1, 2])

    # Duplicated name
    with pytest.raises(ValueError):
        Factor("name", ["a", "b", "a"])
    with pytest.raises(ValueError):
        Factor("name", [
            DerivedLevel("a", WithinTrial(op.eq, [color, text])),
            ElseLevel("a")
        ])

    # Non-string name
    with pytest.raises(ValueError):
        Factor(56, ["level "])

    # Non-list levels
    with pytest.raises(TypeError):
        Factor("name", 42)

    # Empty list
    with pytest.raises(ValueError):
        Factor("name", [])

    # Valid level types, but not uniform.
    with pytest.raises(ValueError):
        Factor("name", ["level1", con_level])

    # Derived levels with non-uniform window types
    with pytest.raises(ValueError):
        Factor("name", [
            con_level,
            DerivedLevel("other", WithinTrial(lambda color: color, [color]))
        ])

    # Derived levels with different window arguments
    with pytest.raises(ValueError):
        Factor("name", [
            con_level,
            DerivedLevel(
                "other",
                Transition(lambda colors: colors[0] == colors[1], [color]))
        ])
def test_derivation_with_three_level_transition():
    f = Factor("f", ["a", "b", "c"])
    f_transition = Factor("transition", [
        DerivedLevel("aa",
                     Transition(lambda c: c[0] == "a" and c[1] == "a", [f])),
        DerivedLevel("ab",
                     Transition(lambda c: c[0] == "a" and c[1] == "b", [f])),
        DerivedLevel("ac",
                     Transition(lambda c: c[0] == "a" and c[1] == "c", [f])),
        DerivedLevel("ba",
                     Transition(lambda c: c[0] == "b" and c[1] == "a", [f])),
        DerivedLevel("bb",
                     Transition(lambda c: c[0] == "b" and c[1] == "b", [f])),
        DerivedLevel("bc",
                     Transition(lambda c: c[0] == "b" and c[1] == "c", [f])),
        DerivedLevel("ca",
                     Transition(lambda c: c[0] == "c" and c[1] == "a", [f])),
        DerivedLevel("cb",
                     Transition(lambda c: c[0] == "c" and c[1] == "b", [f])),
        DerivedLevel("cc",
                     Transition(lambda c: c[0] == "c" and c[1] == "c", [f])),
    ])

    block = fully_cross_block([f, f_transition], [f], [])

    # a-a derivation
    d = Derivation(9, [[0, 3]], f_transition)
    backend_request = BackendRequest(28)
    d.apply(block, backend_request)

    (expected_cnf, expected_fresh) = to_cnf_tseitin(
        And([Iff(10, Or([And([1, 4])])),
             Iff(19, Or([And([4, 7])]))]), 28)

    assert backend_request.fresh == expected_fresh
    assert backend_request.cnfs == [expected_cnf]
Beispiel #7
0
red_text = get_level_from_name(text, "red")
blue_text = get_level_from_name(text, "blue")

con_factor_within_trial = Factor("congruent?", [
    DerivedLevel("con", WithinTrial(op.eq, [color, text])),
    DerivedLevel("inc", WithinTrial(op.ne, [color, text]))
])

con_factor_window = Factor("congruent?", [
    DerivedLevel("con", Window(op.eq, [color, text], 1, 1)),
    DerivedLevel("inc", Window(op.ne, [color, text], 1, 1))
])

color_repeats_factor = Factor("repeated color?", [
    DerivedLevel("yes",
                 Transition(lambda colors: colors[0] == colors[1], [color])),
    DerivedLevel("no",
                 Transition(lambda colors: colors[0] != colors[1], [color]))
])


def test_validate_accepts_basic_factors():
    block = fully_cross_block([color, text], [color, text], [])
    UniformCombinatoricSamplingStrategy._UniformCombinatoricSamplingStrategy__validate(
        block)


def test_validate_accepts_derived_factors_with_simple_windows():
    block = fully_cross_block([color, text, con_factor_within_trial],
                              [color, text], [])
    UniformCombinatoricSamplingStrategy._UniformCombinatoricSamplingStrategy__validate(
from sweetpea import fully_cross_block, __decode
from sweetpea.primitives import Factor, DerivedLevel, WithinTrial, Transition, Window
from sweetpea.constraints import NoMoreThanKInARow
from sweetpea.logic import to_cnf_tseitin


# Common variables for stroop.
color = Factor("color", ["red", "blue"])
text  = Factor("text",  ["red", "blue"])

con_level  = DerivedLevel("con", WithinTrial(op.eq, [color, text]))
inc_level  = DerivedLevel("inc", WithinTrial(op.ne, [color, text]))
con_factor = Factor("congruent?", [con_level, inc_level])

color_repeats_factor = Factor("color repeats?", [
    DerivedLevel("yes", Transition(lambda colors: colors[0] == colors[1], [color])),
    DerivedLevel("no",  Transition(lambda colors: colors[0] != colors[1], [color]))
])

text_repeats_factor = Factor("text repeats?", [
    DerivedLevel("yes", Transition(lambda colors: colors[0] == colors[1], [text])),
    DerivedLevel("no",  Transition(lambda colors: colors[0] != colors[1], [text]))
])

congruent_bookend = Factor("congruent bookend?", [
    DerivedLevel("yes", Window(lambda color, text: color == text, [color, text], 1, 3)),
    DerivedLevel("no",  Window(lambda color, text: color != text, [color, text], 1, 3))
])

design = [color, text, con_factor]
crossing = [color, text]
Beispiel #9
0
color      = Factor("color",  ["red", "blue", "green", "brown"])
word       = Factor("motion", ["red", "blue", "green", "brown"])
task       = Factor("task", ["color naming", "word reading"])
cue       = Factor("cue", ["cue 1", "cue 2"])

# DEFINE TASK TRANSITION

def task_repeat(tasks):
    return tasks[0] == tasks[1]

def task_switch(tasks):
    return not task_repeat(tasks)

task_transition = Factor("task_transition", [
    DerivedLevel("repeat", Transition(task_repeat, [task])),
    DerivedLevel("switch", Transition(task_switch, [task]))
])

# DEFINE CUE TRANSITION

def cue_repeat(cue):
    return cue[0] == cue[1]

def cue_switch(cue):
    return not cue_repeat(cue)

cue_transition = Factor("cue transition", [
    DerivedLevel("repeat", Transition(cue_repeat, [cue])),
    DerivedLevel("switch", Transition(cue_switch, [cue]))
])
Beispiel #10
0
import operator as op
import pytest

from sweetpea.primitives import Factor, DerivedLevel, WithinTrial, Transition, Window

color = Factor("color", ["red", "blue"])
text = Factor("text", ["red", "blue"])

con_level = DerivedLevel("con", WithinTrial(op.eq, [color, text]))
inc_level = DerivedLevel("inc", WithinTrial(op.ne, [color, text]))
con_factor = Factor("congruent?", [con_level, inc_level])

color_repeats_level = DerivedLevel("yes", Transition(op.eq, [color]))
color_no_repeat_level = DerivedLevel("no", Transition(op.ne, [color]))
color_repeats_factor = Factor("color repeats?",
                              [color_repeats_level, color_no_repeat_level])

color3 = Factor("color3", ["red", "blue", "green"])

yes_fn = lambda colors: colors[0] == colors[1] == colors[2]
no_fn = lambda colors: not yes_fn(colors)
color3_repeats_factor = Factor("color3 repeats?", [
    DerivedLevel("yes", Window(yes_fn, [color3], 3, 1)),
    DerivedLevel("no", Window(no_fn, [color3], 3, 1))
])


def test_factor_validation():
    Factor("factor name", ["level 1", "level 2"])

    # Non-string name
    def make_k_diff_level(k):
        def k_diff(colors, texts):
            return count_diff(colors, texts) == k

        return DerivedLevel(str(k), Transition(k_diff, [color, text]))
Beispiel #12
0
congruent = DerivedLevel("con", WithinTrial(operator.eq, [color, word]))
incongruent = DerivedLevel("inc", WithinTrial(operator.ne, [color, word]))

congruence = Factor("congruence", [congruent, incongruent])

one_con_at_a_time = at_most_k_in_a_row(1, (congruence, congruent))


def one_diff(colors, words):
    if (colors[0] == colors[1]):
        return words[0] != words[1]
    else:
        return words[0] == words[1]


def both_diff(colors, words):
    return not one_diff(colors, words)


one = DerivedLevel("one", Transition(one_diff, [color, word]))
both = DerivedLevel("both", Transition(both_diff, [color, word]))
changed = Factor("changed", [one, both])

block = fully_cross_block([color, word, congruence, changed],
                          [color, word, changed], [])

experiments = synthesize_trials_non_uniform(block, 1)

print_experiments(block, experiments)
Beispiel #13
0
- response (left, right)
- congruency transition (congruent-congruent, congruent-incongruent, congruent-neutral, incongruent-congruent, incongruent-incongruent, incongruent-neutral, neutral-congruent, neutral-incongruent, neutral-neutral)
constraints:
- counterbalancing congruency x reward x response x congruency transition (3*2*2*9 = 108)
- counterbalancing all transitions <-- ????
Total number of trials: around 120
"""

congruency = Factor("congruency", ["congruent", "incongruent", "neutral"])
reward = Factor("reward", ["rewarded", "non-rewarded"])
response = Factor("response", ["building", "house"])

congruency_transition = Factor("congruency_transition", [
    DerivedLevel(
        "c-c",
        Transition(lambda c: c[0] == "congruent" and c[1] == "congruent",
                   [congruency])),
    DerivedLevel(
        "c-i",
        Transition(lambda c: c[0] == "congruent" and c[1] == "incongruent",
                   [congruency])),
    DerivedLevel(
        "c-n",
        Transition(lambda c: c[0] == "congruent" and c[1] == "neutral",
                   [congruency])),
    DerivedLevel(
        "i-c",
        Transition(lambda c: c[0] == "incongruent" and c[1] == "congruent",
                   [congruency])),
    DerivedLevel(
        "i-i",
        Transition(lambda c: c[0] == "incongruent" and c[1] == "incongruent",
           ((color == "blue") and (motion == "down"))


def color_motion_incongruent(color, motion):
    return not color_motion_congruent(color, motion)


congruency = Factor("congruency", [
    DerivedLevel("con", WithinTrial(color_motion_congruent, [color, motion])),
    DerivedLevel("inc", WithinTrial(color_motion_incongruent, [color, motion]))
])

# Task Transition
task_transition = Factor("task Transition", [
    DerivedLevel("repeat",
                 Transition(lambda tasks: tasks[0] == tasks[1], [task])),
    DerivedLevel("switch",
                 Transition(lambda tasks: tasks[0] != tasks[1], [task]))
])

# Response Transition
response_transition = Factor("response Transition", [
    DerivedLevel(
        "repeat",
        Transition(lambda responses: responses[0] == responses[1],
                   [response])),
    DerivedLevel(
        "switch",
        Transition(lambda responses: responses[0] != responses[1], [response]))
])
Beispiel #15
0
import operator as op

from sweetpea.internal import get_all_level_names, intersperse
from sweetpea.primitives import Factor, DerivedLevel, Transition


color = Factor("color", ["red", "blue"])
text  = Factor("text",  ["red", "blue", "green"])

color_repeats_level   = DerivedLevel("yes", Transition(lambda colors: colors[0] == colors[1], [color]))
color_no_repeat_level = DerivedLevel("no", Transition(lambda colors: colors[0] != colors[1], [color]))
color_repeats_factor  = Factor("color repeats?", [color_repeats_level, color_no_repeat_level])

def test_get_all_level_names():
    assert get_all_level_names([color, text]) == [('color', 'red'),
                                                  ('color', 'blue'),
                                                  ('text', 'red'),
                                                  ('text', 'blue'),
                                                  ('text', 'green')]

    assert get_all_level_names([color_repeats_factor]) == [('color repeats?', 'yes'),
                                                           ('color repeats?', 'no')]


def test_intersperse():
    assert list(intersperse('', ['yes', 'no', 'yes'])) == \
        ['yes', '', 'no', '', 'yes']

    assert list(intersperse('', ['yes', 'no', 'yes'], 2)) == \
        ['yes', '', '', 'no', '', '', 'yes']
Beispiel #16
0
    DerivedLevel("right", WithinTrial(response_right, [color])),
])

# DEFINE RESPONSE TRANSITION FACTOR


def response_repeat(response):
    return response[0] == response[1]


def response_switch(response):
    return not response_repeat(response)


resp_transition = Factor("response_transition", [
    DerivedLevel("repeat", Transition(response_repeat, [response])),
    DerivedLevel("switch", Transition(response_switch, [response]))
])

# DEFINE SEQUENCE CONSTRAINTS

k = 7
constraints = [NoMoreThanKInARow(k, resp_transition)]

# DEFINE EXPERIMENT

design = [color, word, congruency, resp_transition, response]
crossing = [color, word, resp_transition]
block = fully_cross_block(design, crossing, constraints)

# SOLVE
.
if color-motion then task Transition = switch
if motion-color then task Transition = switch
"""


def task_repeat(tasks):
    return tasks[0] == tasks[1]


def task_switch(tasks):
    return not task_repeat(tasks)


task_transition = Factor("task_transition", [
    derived_level("repeat", Transition(task_repeat, [task])),
    derived_level("switch", Transition(task_switch, [task]))
])
"""
response Transition (repetition, switch). dependent Factor of correct response:
if left-left then task Transition = repetition
if right-right then task Transition = repetition
.
if left-right then task Transition = switch
if right-left then task Transition = switch
"""


def response_repeat(responses):
    return responses[0] == responses[1]
Beispiel #18
0
    DerivedLevel("up", WithinTrial(response_up,   [color])),
    DerivedLevel("down", WithinTrial(response_down,   [color])),
    DerivedLevel("left", WithinTrial(response_left,   [color])),
    DerivedLevel("right", WithinTrial(response_right,   [color])),
])

# DEFINE WORD TRANSITION FACTOR

def word_repeat(word):
    return word[0] == word[1]

def word_switch(word):
    return not word_repeat(word)

word_transition = Factor("word_transition", [
    DerivedLevel("repeat", Transition(word_repeat, [word])),
    DerivedLevel("switch", Transition(word_switch, [word]))
])

# DEFINE COLOR TRANSITION FACTOR

def color_repeat(color):
    return color[0] == color[1]

def color_switch(color):
    return not color_repeat(color)

color_transition = Factor("color_transition", [
    DerivedLevel("repeat", Transition(color_repeat, [color])),
    DerivedLevel("switch", Transition(color_switch, [color]))
])

def ntr_con(congruency):
    return congruency[0] == "neutral" and congruency[1] == "congruent"


def ntr_inc(congruency):
    return congruency[0] == "neutral" and congruency[1] == "incongruent"


def ntr_ntr(congruency):
    return congruency[0] == "neutral" and congruency[1] == "neutral"


congruency_transition = Factor("congruency_transition", [
    DerivedLevel("congruent-congruent", Transition(con_con, [congruency])),
    DerivedLevel("congruent-incongruent", Transition(con_inc, [congruency])),
    DerivedLevel("congruent-neutral", Transition(con_ntr, [congruency])),
    DerivedLevel("incongruent-congruent", Transition(inc_con, [congruency])),
    DerivedLevel("incongruent-incongruent", Transition(inc_inc, [congruency])),
    DerivedLevel("incongruent-neutral", Transition(inc_ntr, [congruency])),
    DerivedLevel("neutral-congruent", Transition(ntr_con, [congruency])),
    DerivedLevel("neutral-incongruent", Transition(ntr_inc, [congruency])),
    DerivedLevel("neutral-neutral", Transition(ntr_ntr, [congruency]))
])

# DEFINE RESPONSE TRANSITION FACTOR


def response_repeat(responses):
    return responses[0] == responses[1]
Beispiel #20
0
import operator as op

from sweetpea.internal import get_all_external_level_names, intersperse
from sweetpea.primitives import Factor, DerivedLevel, Transition

color = Factor("color", ["red", "blue"])
text = Factor("text", ["red", "blue", "green"])

color_repeats_level = DerivedLevel(
    "yes", Transition(lambda colors: colors[0] == colors[1], [color]))
color_no_repeat_level = DerivedLevel(
    "no", Transition(lambda colors: colors[0] != colors[1], [color]))
color_repeats_factor = Factor("color repeats?",
                              [color_repeats_level, color_no_repeat_level])


def test_get_all_external_level_names():
    assert get_all_external_level_names([color, text]) == [('color', 'red'),
                                                           ('color', 'blue'),
                                                           ('text', 'red'),
                                                           ('text', 'blue'),
                                                           ('text', 'green')]

    assert get_all_external_level_names([color_repeats_factor
                                         ]) == [('color repeats?', 'yes'),
                                                ('color repeats?', 'no')]


def test_intersperse():
    assert list(intersperse('', ['yes', 'no', 'yes'])) == \
        ['yes', '', 'no', '', 'yes']
# Congruency Definition
def color_motion_congruent(color, motion):
    return ((color == "red") and (motion == "up")) or \
           ((color == "blue") and (motion == "down"))

def color_motion_incongruent(color, motion):
    return not color_motion_congruent(color, motion)

congruency = Factor("congruency", [
    DerivedLevel("con", WithinTrial(color_motion_congruent,   [color, motion])),
    DerivedLevel("inc", WithinTrial(color_motion_incongruent, [color, motion]))
])

# Task Transition
task_transition = Factor("task transition", [
    DerivedLevel("repeat", Transition(lambda tasks: tasks[0] == tasks[1], [task])),
    DerivedLevel("switch", Transition(lambda tasks: tasks[0] != tasks[1], [task]))
])

# Response Transition
response_transition = Factor("response transition", [
    DerivedLevel("repeat", Transition(lambda responses: responses[0] == responses[1], [response])),
    DerivedLevel("switch", Transition(lambda responses: responses[0] != responses[1], [response]))
])


def __shuffled_design_sample():
    perms = list(permutations([color, motion, task, response, congruency, task_transition, response_transition]))
    shuffle(perms)
    return perms[:6]
color = Factor("color", color_list)
text = Factor("text", color_list)


def one_different(colors, texts):
    if (colors[0] == colors[1]):
        return texts[0] != texts[1]
    else:
        return texts[0] == texts[1]


def other_different(colors, texts):
    return not one_different(colors, texts)


one_level = DerivedLevel("one", Transition(one_different, [color, text]))
other_level = DerivedLevel("other", Transition(other_different, [color, text]))
change = Factor("change", [one_level, other_level])

design = [color, text, change]
crossing = [color, text]
block = fully_cross_block(design, crossing, [])


# Encoding diagram minus 1
# -----------------------------------------------
# |   Trial |   color   |   text    |  change   |
# |       # | red green | red green | one other |
# -----------------------------------------------
# |       1 |  0    1   |  2    3   |           |
# |       2 |  4    5   |  6    7   | 16   17   |
Beispiel #23
0
        (task == "motion" and motion == "up")


def response_right(task, color, motion):
    return not response_left(task, color, motion)


response = Factor("response", [
    DerivedLevel("left", WithinTrial(response_left, [task, color, motion])),
    DerivedLevel("right", WithinTrial(response_right, [task, color, motion]))
])

response_transition = Factor("response transition", [
    DerivedLevel(
        "repeat",
        Transition(lambda responses: responses[0] == responses[1],
                   [response])),
    DerivedLevel(
        "switch",
        Transition(lambda responses: responses[0] != responses[1], [response]))
])


def test_generate_derivations_with_transition_that_depends_on_derived_levels():
    block = fully_cross_block(
        [color, motion, task, response, response_transition],
        [color, motion, task], [])
    derivations = DerivationProcessor.generate_derivations(block)

    assert Derivation(64, [[6, 14], [7, 15]],
                      response_transition) in derivations
    assert Derivation(65, [[6, 15], [7, 14]],
Beispiel #24
0

def con_inc(congruency):
    return congruency[0] == "congruent" and congruency[1] == "incongruent"


def inc_con(congruency):
    return congruency[0] == "incongruent" and congruency[1] == "congruent"


def inc_inc(congruency):
    return congruency[0] == "incongruent" and congruency[1] == "incongruent"


congruency_transition = Factor("congruency Transition", [
    DerivedLevel("congruent-congruent", Transition(con_con, [congruency])),
    DerivedLevel("congruent-incongruent", Transition(con_inc, [congruency])),
    DerivedLevel("incongruent-congruent", Transition(inc_con, [congruency])),
    DerivedLevel("incongruent-incongruent", Transition(inc_inc, [congruency])),
])


# DEFINE FLANKER RESPONSE FACTOR
def flanker_left(target_direction, congruency):
    return ((target_direction == "1" and congruency == "congruent")
            or (target_direction == "-1" and congruency == "incongruent"))


def flanker_right(target_direction, congruency):
    return not flanker_left(target_direction, congruency)