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_generate_derivations_when_derived_factor_precedes_dependencies(): block = fully_cross_block([congruency, motion, color, task], [color, motion, task], []) derivations = DerivationProcessor.generate_derivations(block) assert Derivation(0, [[4, 2], [5, 3]], congruency) in derivations assert Derivation(1, [[4, 3], [5, 2]], congruency) in derivations
def test_generate_derivations_with_window(): block = fully_cross_block([color, text, congruent_bookend], [color, text], []) assert DerivationProcessor.generate_derivations(block) == [ Derivation(16, [[0, 2], [1, 3]], congruent_bookend), Derivation(17, [[0, 3], [1, 2]], congruent_bookend) ]
def test_generate_derivations_transition(design): block = fully_cross_block(design, [color, text], []) assert DerivationProcessor.generate_derivations(block) == [ Derivation(16, [[0, 4], [1, 5]], color_repeats_factor), Derivation(17, [[0, 5], [1, 4]], color_repeats_factor) ]
def fully_cross_block(design: List[Factor], crossing: List[Factor], constraints: List[Constraint], cnf_fn=to_cnf_tseitin) -> Block: all_constraints = cast(List[Constraint], [FullyCross, Consistency]) + constraints block = FullyCrossBlock(design, crossing, all_constraints, cnf_fn) block.constraints += DerivationProcessor.generate_derivations(block) return block
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]], response_transition) in derivations
def fully_cross_block(design: List[Factor], crossing: List[Factor], constraints: List[Constraint], require_complete_crossing=True, cnf_fn=to_cnf_tseitin) -> Block: """Returns a fully crossed :class:`.Block` meant to be used in experiment synthesis. This is the preferred mechanism for describing an experiment. :param design: A :class:`list` of all the :class:`Factors <.Factor>` in the design. When a sequence of trials is generated, each trial will have one level from each factor in ``design``. :param crossing: A :class:`list` of :class:`Factors <.Factor>` used to produce crossings. The number of trials in each run of the experiment is determined as the product of the number of levels of factors in ``crossing``. If ``require_complete_crossing`` is ``False``, the ``constraints`` can reduce the total number of trials. Different trial sequences of the experiment will have different combinations of levels in different orders. The factors in ``crossing`` supply an implicit constraint that every combination of levels in the cross should appear once. Derived factors impose additional constraints: only combinations of levels that are consistent with derivations can appear as a trial. Additional constraints can be manually imposed via the ``constraints`` parameter. :param constraints: A :class:`list` of :class:`Constraints <.Constraint>` that restrict the generated trials. :param require_complete_crossing: Whether every combination in ``crossing`` must appear in a block of trials. ``True`` by default. A ``False`` value is appropriate if combinations are excluded through an :class:`.Exclude` :class:`.Constraint`. :param cnf_fn: A CNF conversion function. Default is :func:`.to_cnf_tseitin`. """ all_constraints = cast(List[Constraint], [FullyCross(), Consistency()]) + constraints all_constraints = __desugar_constraints( all_constraints) #expand the constraints into a form we can process. block = FullyCrossBlock(design, [crossing], all_constraints, require_complete_crossing, cnf_fn) block.constraints += DerivationProcessor.generate_derivations(block) if not constraints and not list(filter( lambda f: f.is_derived(), crossing)) and not list( filter(lambda f: f.has_complex_window, design)): block.complex_factors_or_constraints = False return block
def test_generate_derivations_with_multiple_transitions(design): block = fully_cross_block([color, text, color_repeats_factor, text_repeats_factor], [color, text], []) assert DerivationProcessor.generate_derivations(block) == [ Derivation(16, [[0, 4], [1, 5]], color_repeats_factor), Derivation(17, [[0, 5], [1, 4]], color_repeats_factor), Derivation(22, [[2, 6], [3, 7]], text_repeats_factor), Derivation(23, [[2, 7], [3, 6]], text_repeats_factor) ]
def test_generate_derivations(): assert DerivationProcessor.generate_derivations(block) == [ Derivation(16, [[0, 4, 2, 7], [0, 4, 3, 6], [0, 5, 2, 6], [0, 5, 3, 7], [1, 4, 2, 6], [1, 4, 3, 7], [1, 5, 2, 7], [1, 5, 3, 6]], change), Derivation(17, [[0, 4, 2, 6], [0, 4, 3, 7], [0, 5, 2, 7], [0, 5, 3, 6], [1, 4, 2, 7], [1, 4, 3, 6], [1, 5, 2, 6], [1, 5, 3, 7]], change) ]
def multiple_cross_block(design: List[Factor], crossings: List[List[Factor]], constraints: List[Constraint], require_complete_crossing=True, cnf_fn=to_cnf_tseitin) -> Block: """Returns a :class:`.Block` with multiple crossings, meant to be used in experiment synthesis. Similar to :func:`fully_cross_block`, except it can be configured with multiple crossings. :param design: A :class:`list` of all the :class:`Factors <.Factor>` in the design. When a sequence of trials is generated, each trial will have one level from each factor in ``design``. :param crossings: A :class:`list` of :class:`lists <list>` of :class:`Factors <.Factor>` representing crossings. The number of trials in each run of the experiment is determined by the *maximum* product among the number of levels in the crossings. Every combination of levels in each individual crossing in ``crossings`` appears at least once. Different crossings can refer to the same factors, which constrains how factor levels are chosen across crossings. :param constraints: A :class:`list` of :class:`Constraints <.Constraint>` that restrict the generated trials. :param require_complete_crossing: Whether every combination in ``crossings`` must appear in a block of trials. ``True`` by default. A ``False`` value is appropriate if combinations are excluded through an :class:`.Exclude` :class:`.Constraint.` :param cnf_fn: A CNF conversion function. Default is :func:`.to_cnf_tseitin`. """ all_constraints = cast(List[Constraint], [MultipleCross(), Consistency()]) + constraints all_constraints = __desugar_constraints( all_constraints) #expand the constraints into a form we can process. block = MultipleCrossBlock(design, crossings, all_constraints, require_complete_crossing, cnf_fn) block.constraints += DerivationProcessor.generate_derivations(block) return block