Example #1
0
    def apply_to_backend_request(self,
                                 block: Block,
                                 level: Tuple[Factor, Union[SimpleLevel, DerivedLevel]],
                                 backend_request: BackendRequest
                                 ) -> None:
        sublists = self._build_variable_sublists(block, level, self.k)
        implications = []

        # Handle the regular cases (1 => 2 ^ ... ^ n ^ ~n+1)
        trim = len(sublists) if self.k > 1 else len(sublists) - 1
        for idx, l in enumerate(sublists[:trim]):
            if idx > 0:
                p_list = [Not(sublists[idx-1][0]), l[0]]
                p = And(p_list) if len(p_list) > 1 else p_list[0]
            else:
                p = l[0]

            if idx < len(sublists) - 1:
                q_list = cast(List[Any], l[1:]) + [Not(sublists[idx+1][-1])]
                q = And(q_list) if len(q_list) > 1 else q_list[0]
            else:
                q = And(l[1:]) if len(l[1:]) > 1 else l[self.k - 1]
            implications.append(If(p, q))

        # Handle the tail
        if len(sublists[-1]) > 1:
            tail = sublists[-1]
            tail.reverse()
            for idx in range(len(tail) - 1):
                implications.append(If(l[idx], l[idx + 1]))

        (cnf, new_fresh) = block.cnf_fn(And(implications), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh
Example #2
0
    def apply(block: MultipleCrossBlock, backend_request: BackendRequest) -> None:
        # Treat each crossing seperately, and repeat the same process as fullycross
        for c in block.crossing:
            fresh = backend_request.fresh

            # Step 1: Get a list of the trials that are involved in the crossing.
            crossing_size = max(block.min_trials, block.crossing_size())
            crossing_trials = list(filter(lambda t: all(map(lambda f: f.applies_to_trial(t),
                                                            c)),
                                          range(1, block.trials_per_sample() + 1)))
            crossing_trials = crossing_trials[:crossing_size]

            # Step 2: For each trial, cross all levels of all factors in the crossing.
            crossing_factors = list(map(lambda t: (list(product(*[block.factor_variables_for_trial(f, t) for f in c]))), crossing_trials))

            # Step 3: For each trial, cross all levels of all design-only factors in the crossing.
            design_factors = cast(List[List[List[int]]], [])
            design_factors = list(map(lambda _: [], crossing_trials))
            for f in list(filter(lambda f: f not in c and not f.has_complex_window, block.design)):
                for i, t in enumerate(crossing_trials):
                    design_factors[i].append(block.factor_variables_for_trial(f, t))
            design_combinations = cast(List[List[Tuple[int, ...]]], [])
            design_combinations = list(map(lambda l: list(product(*l)), design_factors))

            # Step 4: For each trial, combine each of the crossing factors with all of the design-only factors.
            crossings = cast(List[List[List[Tuple[int, ...]]]], [])
            for i, t in enumerate(crossing_trials):
                crossings.append(list(map(lambda c: [c] + design_combinations[i], crossing_factors[i])))

            # Step 5: Remove crossings that are not possible.
            # From here on ignore all values other than the first in every list.
            crossings = block.filter_excluded_derived_levels(crossings)

            # Step 6: Allocate additional variables to represent each crossing.
            num_state_vars = list(map(lambda c: len(c), crossings))
            state_vars = list(range(fresh, fresh + sum(num_state_vars)))
            fresh += sum(num_state_vars)

            # Step 7: Associate each state variable with its crossing.
            flattened_crossings = list(chain.from_iterable(crossings))
            iffs = list(map(lambda n: Iff(state_vars[n], And([*flattened_crossings[n][0]])), range(len(state_vars))))

            # Step 8: Constrain each crossing to occur in only one trial.
            states = list(chunk(state_vars, block.crossing_size()))
            transposed = cast(List[List[int]], list(map(list, zip(*states))))

            # We Use n < 2 rather than n = 1 here because they may exclude some levels from the crossing.
            # This ensures that there won't be duplicates, while still allowing some to be missing.
            # backend_request.ll_requests += list(map(lambda l: LowLevelRequest("LT", 2, l), transposed))
            backend_request.ll_requests += list(map(lambda l: LowLevelRequest("GT", 0, l), transposed))

            (cnf, new_fresh) = block.cnf_fn(And(iffs), fresh)

            backend_request.cnfs.append(cnf)
            backend_request.fresh = new_fresh
Example #3
0
    def __apply_derivation(self, block: Block, backend_request: BackendRequest) -> None:
        trial_size = block.variables_per_trial()
        cross_size = block.trials_per_sample()

        iffs = []
        for n in range(cross_size):
            or_clause = Or(list(And(list(map(lambda x: x + (n * trial_size) + 1, l))) for l in self.dependent_idxs))
            iffs.append(Iff(self.derived_idx + (n * trial_size) + 1, or_clause))

        (cnf, new_fresh) = block.cnf_fn(And(iffs), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh
Example #4
0
    def apply_to_backend_request(self, block: Block, level: Tuple[Factor, Union[SimpleLevel, DerivedLevel]],
                                    backend_request: BackendRequest) -> None:

        # Request sublists for k+1 to allow us to determine the transition
        sublists = self._build_variable_sublists(block, level, self.k + 1)

        implications = []
        if sublists:
            # Starting corner case
            implications.append(If(sublists[0][0], And(sublists[0][1:-1])))
            for sublist in sublists:
                implications.append(If(And([Not(sublist[0]), sublist[1]]), And(sublist[2:])))
            # Ending corner case
            implications.append(If(sublists[-1][-1], And(sublists[-1][1:-1])))

        (cnf, new_fresh) = block.cnf_fn(And(implications), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh
Example #5
0
    def __apply_derivation_with_complex_window(self, block: Block, backend_request: BackendRequest) -> None:
        trial_size = block.variables_per_trial()
        trial_count = block.trials_per_sample()
        iffs = []
        f = self.factor
        window = f.levels[0].window
        t = 0
        for n in range(trial_count):
            if not f.applies_to_trial(n + 1):
                continue

            num_levels = len(f.levels)
            get_trial_size = lambda x: trial_size if x < block.grid_variables() else len(block.decode_variable(x+1)[0].levels)
            or_clause = Or(list(And(list(map(lambda x: x + (t * window.stride * get_trial_size(x) + 1), l))) for l in self.dependent_idxs))
            iffs.append(Iff(self.derived_idx + (t * num_levels) + 1, or_clause))
            t += 1
        (cnf, new_fresh) = block.cnf_fn(And(iffs), backend_request.fresh)

        backend_request.cnfs.append(cnf)
        backend_request.fresh = new_fresh