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_fully_cross_block_crossing_size_with_overlapping_exclude(): # How about with two overlapping exclude constraints? Initial crossing size # should be 3 x 3 = 9. # Excluding congruent pairs will reduce that to 9 - 3 = 6 # Then excluding red and green on top of that should make it 5. color = Factor("color", ["red", "blue", "green"]) text = Factor("text", ["red", "blue", "green"]) congruent_factor = Factor("congruent?", [ DerivedLevel("congruent", WithinTrial(op.eq, [color, text])), DerivedLevel("incongruent", WithinTrial(op.ne, [color, text])), ]) def illegal(color, text): return (color == "red" and text == "green") or color == text def legal(color, text): return not illegal(color, text) legal_factor = Factor("legal", [ DerivedLevel("yes", WithinTrial(legal, [color, text])), DerivedLevel("no", WithinTrial(illegal, [color, text])) ]) assert FullyCrossBlock( [color, text, congruent_factor, legal_factor], [[color, text]], [ Exclude(congruent_factor, get_level_from_name(congruent_factor, "congruent")), # Excludes 3 Exclude(legal_factor, get_level_from_name(legal_factor, "no")) ], # Exludes 4, but 3 were already excluded require_complete_crossing=False).crossing_size() == 5
def test_generate_encoding_diagram_with_window_with_stride(): congruent_bookend = Factor("congruent bookend?", [ DerivedLevel("yes", Window(lambda colors, texts: colors[0] == texts[0], [color, text], 1, 3)), DerivedLevel("no", Window(lambda colors, texts: colors[0] != texts[0], [color, text], 1, 3)) ]) block = fully_cross_block([color, text, congruent_bookend], [color, text], []) assert __generate_encoding_diagram(block) == "\ ------------------------------------------------------\n\ | Trial | color | text | congruent bookend? |\n\ | # | red blue | red blue | yes no |\n\ ------------------------------------------------------\n\ | 1 | 1 2 | 3 4 | 17 18 |\n\ | 2 | 5 6 | 7 8 | |\n\ | 3 | 9 10 | 11 12 | |\n\ | 4 | 13 14 | 15 16 | 19 20 |\n\ ------------------------------------------------------\n" congruent_bookend = Factor("congruent bookend?", [ DerivedLevel("yes", Window(lambda colors, texts: colors[0] == texts[0], [color, text], 2, 2)), DerivedLevel("no", Window(lambda colors, texts: colors[0] != texts[0], [color, text], 2, 2)) ]) block = fully_cross_block([color, text, congruent_bookend], [color, text], []) assert __generate_encoding_diagram(block) == "\
def test_consistency_with_transition_first_and_uneven_level_lengths(): 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)) ]) block = fully_cross_block([color3_repeats_factor, color3, text], [color3, text], []) backend_request = BackendRequest(0) Consistency.apply(block, backend_request) assert backend_request.ll_requests == [ LowLevelRequest("EQ", 1, [1, 2, 3]), LowLevelRequest("EQ", 1, [4, 5]), LowLevelRequest("EQ", 1, [6, 7, 8]), LowLevelRequest("EQ", 1, [9, 10]), LowLevelRequest("EQ", 1, [11, 12, 13]), LowLevelRequest("EQ", 1, [14, 15]), LowLevelRequest("EQ", 1, [16, 17, 18]), LowLevelRequest("EQ", 1, [19, 20]), LowLevelRequest("EQ", 1, [21, 22, 23]), LowLevelRequest("EQ", 1, [24, 25]), LowLevelRequest("EQ", 1, [26, 27, 28]), LowLevelRequest("EQ", 1, [29, 30]), LowLevelRequest("EQ", 1, [31, 32]), LowLevelRequest("EQ", 1, [33, 34]), LowLevelRequest("EQ", 1, [35, 36]), LowLevelRequest("EQ", 1, [37, 38]) ]
def test_consistency(): # From standard example # [ LowLevelRequest("EQ", 1, [1, 2]), LowLevelRequest("EQ", 1, [3, 4]), ...] backend_request = BackendRequest(0) Consistency.apply(block, backend_request) assert backend_request.ll_requests == \ list(map(lambda x: LowLevelRequest("EQ", 1, [x, x+1]), range(1, 24, 2))) # Different case backend_request = BackendRequest(0) f = Factor("a", ["b", "c", "d", "e"]) f_block = fully_cross_block([f], [f], []) Consistency.apply(f_block, backend_request) assert backend_request.ll_requests == \ list(map(lambda x: LowLevelRequest("EQ", 1, [x, x+1, x+2, x+3]), range(1, 16, 4))) # Varied level lengths backend_request = BackendRequest(0) f1 = Factor("a", ["b", "c", "d"]) f2 = Factor("e", ["f"]) f_block = fully_cross_block([f1, f2], [f1, f2], []) Consistency.apply(f_block, backend_request) assert backend_request.ll_requests == [ LowLevelRequest("EQ", 1, [1, 2, 3]), LowLevelRequest("EQ", 1, [4]), LowLevelRequest("EQ", 1, [5, 6, 7]), LowLevelRequest("EQ", 1, [8]), LowLevelRequest("EQ", 1, [9, 10, 11]), LowLevelRequest("EQ", 1, [12]) ]
def test_generate_derivations_within_trial(): assert DerivationProcessor.generate_derivations(blk) == [ Derivation(4, [[0, 2], [1, 3]], con_factor), Derivation(5, [[0, 3], [1, 2]], con_factor) ] integer = Factor("integer", ["1", "2"]) numeral = Factor("numeral", ["I", "II"]) text = Factor("text", ["one", "two"]) twoConLevel = DerivedLevel("twoCon", WithinTrial(two_con, [integer, numeral, text])) twoNotConLevel = DerivedLevel( "twoNotCon", WithinTrial(two_not_con, [integer, numeral, text])) two_con_factor = Factor("twoCon?", [twoConLevel, twoNotConLevel]) one_two_design = [integer, numeral, text, two_con_factor] one_two_crossing = [integer, numeral, text] assert DerivationProcessor.generate_derivations( fully_cross_block(one_two_design, one_two_crossing, [])) == [ Derivation(6, [[0, 2, 5], [0, 3, 4], [0, 3, 5], [1, 2, 4], [1, 2, 5], [1, 3, 4]], two_con_factor), Derivation(7, [[0, 2, 4], [1, 3, 5]], two_con_factor) ]
def test_exclude_with_three_derived_levels(): color_list = ["red", "green", "blue"] color = Factor("color", color_list) text = Factor("text", color_list) def count_diff(colors, texts): changes = 0 if (colors[0] != colors[1]): changes += 1 if (texts[0] != texts[1]): changes += 1 return changes 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])) changed = Factor( "changed", [make_k_diff_level(0), make_k_diff_level(1), make_k_diff_level(2)]) exclude_constraint = Exclude(changed, get_level_from_name(changed, "2")) design = [color, text, changed] crossing = [color, text] block = fully_cross_block(design, crossing, [exclude_constraint]) backend_request = BackendRequest(0) exclude_constraint.apply(block, backend_request) assert backend_request.cnfs == [ And([-57, -60, -63, -66, -69, -72, -75, -78]) ]
def __build_stroop_block(color_count): color = Factor("color", color_list[:color_count]) text = Factor("text", color_list[:color_count]) congruent = Factor("congruent?", [ DerivedLevel("yes", WithinTrial(op.eq, [color, text])), DerivedLevel("no", WithinTrial(op.ne, [color, text])) ]) constraints = [AtMostKInARow(1, ("congruent?", "yes"))] return fully_cross_block([color, text, congruent], [color, text], constraints)
def test_generate_encoding_diagram_with_windows(): 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)) ]) block = fully_cross_block([color3_repeats_factor, color3, text], [color3, text], []) assert __generate_encoding_diagram(block) == "\
def test_generate_sample_basic_stroop_uneven_colors(sequence_number, expected_solution): text = Factor("text", ["red", "blue", "green"]) congruency = Factor("congruency", [ DerivedLevel("congruent", WithinTrial(op.eq, [color, text])), DerivedLevel("incongruent", WithinTrial(op.ne, [color, text])) ]) block = fully_cross_block([color, text, congruency], [color, congruency], []) enumerator = UCSolutionEnumerator(block) print(enumerator.generate_sample(sequence_number)) assert enumerator.generate_sample(sequence_number) == expected_solution
def test_decode(): solution = [-1, 2, -3, 4, 5, -6, -7, 8, 9, -10, -11, 12, 13, -14, -15, 16, -17, 18, 19, -20, 21, -22, 23, -24] shuffle(solution) assert SamplingStrategy.decode(blk, solution) == { 'color': ['blue', 'blue', 'red', 'red'], 'text': ['blue', 'red', 'blue', 'red'], 'congruent?': ['con', 'inc', 'inc', 'con'] } solution = [ -1, 2, -3, 4, 5, -6, 7, -8, -9, 10, -11, 12, 13, -14, 15, -16, 17, -18, -19, 20, 21, -22, -23, 24] shuffle(solution) assert SamplingStrategy.decode(blk, solution) == { 'color': ['blue', 'red', 'red', 'blue'], 'text': ['blue', 'blue', 'red', 'red'], 'congruent?': ['con', 'inc', 'con', 'inc'] } solution = [-1, 2, 3, -4, -5, 6, -7, 8, -9, 10, 11, -12, 13, -14, 15, -16, 17, -18, 19, -20, -21, 22, -23, 24] shuffle(solution) assert SamplingStrategy.decode(blk, solution) == { 'color': ['blue', 'blue', 'red', 'red'], 'text': ['red', 'blue', 'red', 'blue'], 'congruent?': ['inc', 'con', 'con', 'inc'] } f1 = Factor("a", ["b", "c", "d"]) f2 = Factor("e", ["f"]) f_blk = fully_cross_block([f1, f2], [f1, f2], []) solution = [-1, 2, -3, 4, -5, -6, 7, 8, 9, -10 -11, 12] shuffle(solution) assert SamplingStrategy.decode(f_blk, solution) == { 'a': ['c', 'd', 'b'], 'e': ['f', 'f', 'f'] }
def test_fully_cross_with_uncrossed_simple_factors(): other = Factor('other', ['l1', 'l2']) block = fully_cross_block([color, text, other], [color, text], []) backend_request = BackendRequest(25) FullyCross.apply(block, backend_request) (expected_cnf, _) = to_cnf_tseitin( And([ Iff(25, And([1, 3])), Iff(26, And([1, 4])), Iff(27, And([2, 3])), Iff(28, And([2, 4])), Iff(29, And([7, 9])), Iff(30, And([7, 10])), Iff(31, And([8, 9])), Iff(32, And([8, 10])), Iff(33, And([13, 15])), Iff(34, And([13, 16])), Iff(35, And([14, 15])), Iff(36, And([14, 16])), Iff(37, And([19, 21])), Iff(38, And([19, 22])), Iff(39, And([20, 21])), Iff(40, And([20, 22])) ]), 41) assert backend_request.fresh == 74 assert backend_request.cnfs == [expected_cnf] assert backend_request.ll_requests == [ LowLevelRequest("GT", 0, [25, 29, 33, 37]), LowLevelRequest("GT", 0, [26, 30, 34, 38]), LowLevelRequest("GT", 0, [27, 31, 35, 39]), LowLevelRequest("GT", 0, [28, 32, 36, 40]) ]
def test_build_variable_list_for_three_derived_levels(): def count_diff(colors, texts): changes = 0 if (colors[0] != colors[1]): changes += 1 if (texts[0] != texts[1]): changes += 1 return changes 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])) changed = Factor( "changed", [make_k_diff_level(0), make_k_diff_level(1), make_k_diff_level(2)]) block = fully_cross_block([color, text, changed], [color, text], []) assert block.build_variable_list( (changed, get_level_from_name(changed, "0"))) == [17, 20, 23] assert block.build_variable_list( (changed, get_level_from_name(changed, "1"))) == [18, 21, 24] assert block.build_variable_list( (changed, get_level_from_name(changed, "2"))) == [19, 22, 25]
def test_kinarow_with_bad_factor(): bogus_factor = Factor("f", ["a", "b", "c"]) with pytest.raises(ValueError): fully_cross_block(design, crossing, [ ExactlyKInARow( 2, (bogus_factor, get_level_from_name(bogus_factor, "a"))) ])
def factor_variables_for_trial(self, f: Factor, t: int) -> List[int]: if not f.applies_to_trial(t): raise ValueError('Factor does not apply to trial #' + str(t) + ' f=' + str(f)) previous_trials = sum( map(lambda trial: 1 if f.applies_to_trial(trial + 1) else 0, range(t))) - 1 initial_sequence = list( map(lambda l: self.first_variable_for_level(f, l), list(filter(lambda l: (f, l) not in self.exclude, f.levels)))) offset = 0 if f.has_complex_window(): offset = len(f.levels) * previous_trials else: offset = self.variables_per_trial() * previous_trials return list(map(lambda n: n + offset + 1, initial_sequence))
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 validate_factor_and_level(block: Block, factor: Factor, level: Union[SimpleLevel, DerivedLevel]) -> None: if not block.has_factor(factor): raise ValueError(("A factor with name '{}' wasn't found in the design. " +\ "Are you sure the factor was included, and that the name is spelled " +\ "correctly?").format(factor.factor_name)) if not factor.has_level(level): raise ValueError(("A level with name '{}' wasn't found in the '{}' factor, " +\ "Are you sure the level name is spelled correctly?").format(get_internal_level_name(level), factor.factor_name))
def test_generate_derivations_should_raise_error_if_some_factor_matches_multiple_levels( ): local_con_factor = Factor("congruent?", [ DerivedLevel("con", WithinTrial(op.eq, [color, text])), DerivedLevel("inc", WithinTrial(op.eq, [color, text])) ]) with pytest.raises(ValueError): fully_cross_block([color, text, local_con_factor], [color, text], [])
def __trials_required_for_crossing(self, f: Factor, crossing_size: int) -> int: trial = 0 counter = 0 while counter != crossing_size: trial += 1 if f.applies_to_trial(trial): counter += 1 return trial
def factor_variables_for_trial(self, f: Factor, t: int) -> List[int]: """Given a factor and a trial number (1-based) this function will return a list of the variables representing the levels of the given factor for that trial. The variable list is also 1-based. """ if not f.applies_to_trial(t): raise ValueError('Factor does not apply to trial #' + str(t) + ' f=' + str(f)) previous_trials = sum(map(lambda trial: 1 if f.applies_to_trial(trial + 1) else 0, range(t))) - 1 initial_sequence = list(map(lambda l: self.first_variable_for_level(f, l), list(filter(lambda l: (f, l) not in self.exclude, f.levels)))) offset = 0 if f.has_complex_window: offset = len(f.levels) * previous_trials else: offset = self.variables_per_trial() * previous_trials return list(map(lambda n: n + offset + 1, initial_sequence))
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_generate_derivations_should_produce_warning_if_some_level_is_unreachable(capsys): local_con_factor = Factor("congruent?", [ DerivedLevel("con", WithinTrial(op.eq, [color, text])), DerivedLevel("inc", WithinTrial(op.ne, [color, text])), DerivedLevel("dum", WithinTrial(lambda c, t: c=='green', [color, text])) ]) fully_cross_block([color, text, local_con_factor], [color, text], []) assert capsys.readouterr().out == "WARNING: There is no assignment that matches factor congruent? with level dum.\n"
def test_derived_level_get_dependent_cross_product(): assert [((tup[0][0].factor_name, tup[0][1].external_name), (tup[1][0].factor_name, tup[1][1].external_name)) for tup in con_level.get_dependent_cross_product() ] == [(('color', 'red'), ('text', 'red')), (('color', 'red'), ('text', 'blue')), (('color', 'blue'), ('text', 'red')), (('color', 'blue'), ('text', 'blue'))] integer = Factor("integer", ["1", "2"]) numeral = Factor("numeral", ["I", "II"]) text = Factor("text", ["one", "two"]) two_con_level = DerivedLevel( "twoCon", WithinTrial(lambda x: x, [integer, numeral, text])) assert [((tup[0][0].factor_name, tup[0][1].external_name), (tup[1][0].factor_name, tup[1][1].external_name), (tup[2][0].factor_name, tup[2][1].external_name)) for tup in two_con_level.get_dependent_cross_product() ] == [(('integer', '1'), ('numeral', 'I'), ('text', 'one')), (('integer', '1'), ('numeral', 'I'), ('text', 'two')), (('integer', '1'), ('numeral', 'II'), ('text', 'one')), (('integer', '1'), ('numeral', 'II'), ('text', 'two')), (('integer', '2'), ('numeral', 'I'), ('text', 'one')), (('integer', '2'), ('numeral', 'I'), ('text', 'two')), (('integer', '2'), ('numeral', 'II'), ('text', 'one')), (('integer', '2'), ('numeral', 'II'), ('text', 'two'))] mixed_level = DerivedLevel( "mixed", WithinTrial(lambda x: x, [ Factor("color", ["red", "blue", "green"]), Factor("boolean", ["true", "false"]) ])) assert [((tup[0][0].factor_name, tup[0][1].external_name), (tup[1][0].factor_name, tup[1][1].external_name)) for tup in mixed_level.get_dependent_cross_product() ] == [(('color', 'red'), ('boolean', 'true')), (('color', 'red'), ('boolean', 'false')), (('color', 'blue'), ('boolean', 'true')), (('color', 'blue'), ('boolean', 'false')), (('color', 'green'), ('boolean', 'true')), (('color', 'green'), ('boolean', 'false'))]
def test_factor_applies_to_trial(): assert color.applies_to_trial(1) == True assert color.applies_to_trial(2) == True assert color.applies_to_trial(3) == True assert color.applies_to_trial(4) == True with pytest.raises(ValueError): color.applies_to_trial(0) assert color_repeats_factor.applies_to_trial(1) == False assert color_repeats_factor.applies_to_trial(2) == True assert color_repeats_factor.applies_to_trial(3) == True assert color_repeats_factor.applies_to_trial(4) == True f = Factor('f', [DerivedLevel('l', Window(op.eq, [color], 2, 2))]) assert f.applies_to_trial(1) == False assert f.applies_to_trial(2) == True assert f.applies_to_trial(3) == False assert f.applies_to_trial(4) == True assert color3.applies_to_trial(1) == True
def test_fully_cross_block_trials_per_sample(): text_single = Factor("text", ["red"]) assert FullyCrossBlock([], [color, color], []).trials_per_sample() == 4 assert FullyCrossBlock([], [color, color, color], []).trials_per_sample() == 8 assert FullyCrossBlock([], [size, text_single], []).trials_per_sample() == 3 assert FullyCrossBlock([], [size, color], []).trials_per_sample() == 6 assert FullyCrossBlock([], [text_single], []).trials_per_sample() == 1 assert FullyCrossBlock([color, text, color_repeats_factor], [color, text], []).trials_per_sample() == 4
def test_generate_derivations_should_raise_error_if_fn_doesnt_return_a_boolean(): def local_eq(color, text): color == text # Notice the missing return stmt local_con_factor = Factor("congruent?", [ DerivedLevel("con", WithinTrial(local_eq, [color, text])), DerivedLevel("inc", WithinTrial(lambda c, t: not local_eq(c, t), [color, text])) ]) with pytest.raises(ValueError): fully_cross_block([color, text, local_con_factor], [color, text], [])
def test_atmostkinarow_validate(): color = Factor("color", ["red", "blue"]) with pytest.raises(ValueError): AtMostKInARow(SimpleLevel("yo"), color) # Levels must either be a factor/level tuple, or a Factor. AtMostKInARow(1, (color, get_level_from_name(color, "red"))) AtMostKInARow(1, color) with pytest.raises(ValueError): AtMostKInARow(1, 42) with pytest.raises(ValueError): AtMostKInARow(1, (color, get_level_from_name(color, "red"), "oops"))
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]
def test_exclude_with_reduced_crossing(): color = Factor("color", ["red", "blue", "green"]) text = Factor("text", ["red", "blue"]) def illegal_stimulus(color, text): return color == "green" and text == "blue" def legal_stimulus(color, text): return not illegal_stimulus(color, text) stimulus_configuration = Factor("stimulus configuration", [ DerivedLevel("legal", WithinTrial(legal_stimulus, [color, text])), DerivedLevel("illegal", WithinTrial(illegal_stimulus, [color, text])) ]) c = Exclude(stimulus_configuration, get_level_from_name(stimulus_configuration, "illegal")) block = fully_cross_block([color, text, stimulus_configuration], [color, text], [c], require_complete_crossing=False) backend_request = BackendRequest(0) c.apply(block, backend_request) assert backend_request.cnfs == [And([-7, -14, -21, -28, -35])]
def __trials_required_for_crossing(self, f: Factor, crossing_size: int) -> int: """Given a factor ``f``, and a crossing size, this function will compute the number of trials required to fully cross ``f`` with the other factors. For example, if ``f`` is a transition, it doesn't apply to trial 1. So when the ``crossing_size`` is ``4``, we'd actually need 5 trials to fully cross with ``f``. This is a helper for :class:`.MultipleCrossBlock.trials_per_sample`. """ trial = 0 counter = 0 while counter != crossing_size: trial += 1 if f.applies_to_trial(trial): counter += 1 return trial