def test_decode_with_transition(): block = fully_cross_block([color, text, color_repeats_factor], [color, text], []) solution = [ 1, -2, 3, -4, 5, -6, -7, 8, # 17, -18 -9, 10, 11, -12, # -19, 20 -13, 14, -15, 16, # 21, -22 17, -18, -19, 20, 21, -22] # color_repeats_factor shuffle(solution) decoded = SamplingStrategy.decode(block, solution) assert decoded['color'] == ['red', 'red', 'blue', 'blue'] assert decoded['text'] == ['red', 'blue', 'red', 'blue'] assert decoded['color repeats?'] == ['', 'yes', 'no', 'yes' ] solution = [ 1, -2, -3, 4, -5, 6, 7, -8, -9, 10, -11, 12, 13, -14, 15, -16, -17, 18, 19, -20, -21, 22] shuffle(solution) decoded = SamplingStrategy.decode(block, solution) assert decoded['color'] == ['red', 'blue', 'blue', 'red'] assert decoded['text'] == ['blue', 'red', 'blue', 'red'] assert decoded['color repeats?'] == ['', 'no', 'yes', 'no' ]
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_decode_with_transition_and_only_positive_variables(): block = fully_cross_block([color, text, color_repeats_factor], [color, text], []) solution = [2, 3, 5, 8, 18, 9, 11, 19, 14, 16, 22] decoded = SamplingStrategy.decode(block, solution) assert decoded['color'] == ['blue', 'red', 'red', 'blue'] assert decoded['text'] == ['red', 'blue', 'red', 'blue'] assert decoded['color repeats?'] == ['', 'no', 'yes', 'no' ]
def test_decode_with_general_window(): block = fully_cross_block([color, text, congruent_bookend], [color, text], []) solution = [ 1, -2, -3, 4, -5, 6, 7, -8, -9, 10, -11, 12, 13, -14, 15, -16, -17, 18, 19, -20] shuffle(solution) decoded = SamplingStrategy.decode(block, solution) assert decoded['color'] == ['red', 'blue', 'blue', 'red'] assert decoded['text'] == ['blue', 'red', 'blue', 'red'] assert decoded['congruent bookend?'] == ['no', '', '', 'yes']
def sample(block: Block, sample_count: int) -> SamplingResult: backend_request = block.build_backend_request() if block.errors: for e in block.errors: print(e) if "WARNING" not in e: return SamplingResult([], {}) solutions = sample_non_uniform( sample_count, CNF(backend_request.get_cnfs_as_json()), backend_request.fresh - 1, block.variables_per_sample(), backend_request.get_requests_as_generation_requests()) result = list( map(lambda s: SamplingStrategy.decode(block, s.assignment), solutions)) return SamplingResult(result, {})
def sample(block: Block, sample_count: int, min_search: bool = False) -> SamplingResult: backend_request = block.build_backend_request() if block.errors: for e in block.errors: print(e) if "WARNING" not in e: return SamplingResult([], {}) solutions = sample_uniform( sample_count, CNF(backend_request.get_cnfs_as_json()), backend_request.fresh - 1, block.variables_per_sample(), backend_request.get_requests_as_generation_requests(), False) if not solutions: from sweetpea.constraints import AtLeastKInARow if min_search: return SamplingResult([], {}) else: max_constraints = list( map( lambda x: cast(AtLeastKInARow, x).max_trials_required, filter(lambda c: isinstance(c, AtLeastKInARow), block.constraints))) if max_constraints: print( "No solution found... We require a minimum trials contraint to find a solution." ) max_constraint = max(max_constraints) min_constraint = block.trials_per_sample() + 1 original_min_trials = block.min_trials last_valid_min_contraint = max_constraint last_valid = SamplingResult([], {}) progress = tqdm(total=math.ceil( math.log(max_constraint - min_constraint)) + 1, file=sys.stdout) while True: current_constraint = int( (max_constraint - min_constraint + 1) / 2) + min_constraint block.min_trials = original_min_trials c = minimum_trials(current_constraint) c.validate(block) c.apply(block, None) block.constraints.append(c) res = UnigenSamplingStrategy.sample( block, sample_count, True) progress.update(1) if res.samples: if current_constraint <= min_constraint: print( "Optimal minimum trials contraint is at ", current_constraint, ".") return res else: last_valid_min_contraint = current_constraint last_valid = res max_constraint = current_constraint - 1 else: if max_constraint <= current_constraint: print( "Optimal minimum trials contraint is at ", last_valid_min_contraint, ".") return last_valid else: min_constraint = current_constraint + 1 progress.close() return result else: return SamplingResult([], {}) result = list( map(lambda s: SamplingStrategy.decode(block, s.assignment), solutions)) return SamplingResult(result, {})
def __generate_sample(block: Block, cnf: CNF, sample_metrics: dict) -> dict: sample_metrics['trials'] = [] # Start a 'committed' list of CNFs committed = cast(List[And], []) for trial_number in range(block.trials_per_sample()): trial_start_time = time() trial_metrics = {'t': trial_number + 1, 'solver_calls': []} solver_calls = cast(List[dict], trial_metrics['solver_calls']) # Get the variable list for this trial. variables = block.variable_list_for_trial(trial_number + 1) variables = list(filter(lambda i: i != [], variables)) potential_trials = list(map(list, product(*variables))) trial_metrics['potential_trials'] = len(potential_trials) # Use env var to switch between filtering and not if GuidedSamplingStrategy.__prefilter_enabled(): # Flatten the list flat_vars = list(chain(*variables)) # Check SAT for each one unsat = [] for v in flat_vars: t_start = time() full_cnf = cnf + CNF(cnf_to_json(committed)) + CNF( cnf_to_json([And([v])])) allowed = cnf_is_satisfiable(full_cnf) duration_seconds = time() - t_start solver_calls.append({ 'time': duration_seconds, 'SAT': allowed }) if not allowed: unsat.append(v) # TODO: Count filtering SAT calls separately? # Filter out any potential trials with those vars set filtered_pts = [] for pt in potential_trials: if any(uv in pt for uv in unsat): continue else: filtered_pts.append(pt) # Record the number filterd out for metrics trial_metrics['prefiltered_out'] = len(potential_trials) - len( filtered_pts) potential_trials = filtered_pts allowed_trials = [] for potential_trial in potential_trials: start_time = time() full_cnf = cnf + CNF(cnf_to_json(committed)) + CNF( cnf_to_json([And(potential_trial)])) allowed = cnf_is_satisfiable(full_cnf) duration_seconds = time() - start_time solver_calls.append({'time': duration_seconds, 'SAT': allowed}) if allowed: allowed_trials.append(potential_trial) trial_metrics['allowed_trials'] = len(allowed_trials) trial_metrics['solver_call_count'] = len(solver_calls) sample_metrics['trials'].append(trial_metrics) # Randomly sample a single trial from the uniform distribution of the allowed trials, # and commit that trial to the committed sequence. trial_idx = np.random.randint(0, len(allowed_trials)) committed.append(And(allowed_trials[trial_idx])) trial_metrics['time'] = time() - trial_start_time # Aggregate the total solver calls sample_metrics['solver_call_count'] = 0 for tm in sample_metrics['trials']: sample_metrics['solver_call_count'] += tm['solver_call_count'] # Flatten the committed trials into a list of integers and decode it. solution = GuidedSamplingStrategy.__committed_to_solution(committed) return SamplingStrategy.decode(block, solution)
def sample(block: Block, sample_count: int, min_search: bool = False) -> SamplingResult: backend_request = block.build_backend_request() if block.errors: for e in block.errors: print(e) if "WARNING" not in e: return SamplingResult([], {}) solutions = sample_uniform( sample_count, CNF(backend_request.get_cnfs_as_json()), backend_request.fresh - 1, block.variables_per_sample(), backend_request.get_requests_as_generation_requests(), False) # This section deals with the problem caused by a corner case created # by at_least_k_in_a_row_constraint. I.e. in some cases this cotnraint # requires the support of a minimum_trials contraint to find valid # solutions. This will find the optimal minimum trials constraint to # the user using binary search with trial and error. if not solutions: from sweetpea.constraints import AtLeastKInARow if min_search: return SamplingResult([], {}) else: atleast_constraints = cast( List[AtLeastKInARow], filter(lambda c: isinstance(c, AtLeastKInARow), block.constraints)) max_constraints = list( map(lambda x: x.max_trials_required, atleast_constraints)) if max_constraints: print( "No solution found... We require a minimum trials contraint to find a solution." ) max_constraint = max(max_constraints) min_constraint = block.trials_per_sample() + 1 original_min_trials = block.min_trials last_valid_min_contraint = max_constraint last_valid = SamplingResult([], {}) progress = tqdm( total=ceil(log(max_constraint - min_constraint)) + 1, file=sys.stdout) while True: current_constraint = int( (max_constraint - min_constraint + 1) / 2) + min_constraint block.min_trials = original_min_trials c = minimum_trials(current_constraint) c.validate(block) c.apply(block, None) block.constraints.append(c) res = UnigenSamplingStrategy.sample( block, sample_count, True) progress.update(1) if res.samples: if current_constraint <= min_constraint: print( "Optimal minimum trials contraint is at ", current_constraint, ".") return res else: last_valid_min_contraint = current_constraint last_valid = res max_constraint = current_constraint - 1 else: if max_constraint <= current_constraint: print( "Optimal minimum trials contraint is at ", last_valid_min_contraint, ".") return last_valid else: min_constraint = current_constraint + 1 progress.close() return result else: return SamplingResult([], {}) result = list( map(lambda s: SamplingStrategy.decode(block, s.assignment), solutions)) return SamplingResult(result, {})