def problem(args): p = GenericProblem( args["active_constraints"], args["passive_constraints"], args["leaf_constraints"], args["root_constraints"], leaf_allow_all=(args["leaf_constraints"] == []), root_allow_all=(args["root_constraints"] == []), flags=ProblemFlags(is_tree=args["is_tree"], is_cycle=args["is_cycle"], is_path=args["is_path"]), ) classified_problem = get_classified_problem_obj(p) if classified_problem is not None: return jsonify({ "problem": classified_problem.to_problem().dict(), "result": classified_problem.to_response().dict(), }) else: res = classify(p) if not (res.det_lower_bound == CONST and res.det_upper_bound == UNSOLVABLE and res.rand_lower_bound == CONST and res.rand_upper_bound == UNSOLVABLE): store_problem_and_classification(p, res) return jsonify({"problem": p.dict(), "result": res.dict()})
def validate(p: GenericProblem) -> None: if p.flags.is_cycle: raise Exception("tlp", "Cannot classify if the graph is a cycle") if p.flags.is_directed_or_rooted: raise Exception("tlp", "Cannot classify if the tree/path is rooted/directed") if not p.flags.is_regular: raise Exception("tlp", "Cannot classify if the graph is not regular") if not p.root_allow_all or not p.leaf_allow_all: raise Exception("tlp", "Leaves and roots must allow all configurations") if len(p.get_alphabet()) > 3: raise Exception("tlp", "Cannot classify problems with more than 3 labels") active_degree = len(p.active_constraints[0]) if len( p.active_constraints) else 3 passive_degree = len(p.passive_constraints[0]) if len( p.passive_constraints) else 2 if not ((active_degree == 2 and passive_degree == 2) or (active_degree == 2 and passive_degree == 3) or (active_degree == 3 and passive_degree == 2)): raise Exception("rooted-tree", "Allowed degrees pairs are (2, 2), (2, 3), (3, 2)")
def preprocess_problem(p: GenericProblem) -> List[str]: alphabet = p.get_alphabet() constraints = [move_root_label_to_center(x) for x in p.active_constraints] for i, label in enumerate(alphabet): constraints = [x.replace(label, str(i + 1)) for x in constraints] return constraints
def test_re2(self): REtorProblem2 = GenericProblem(active_constraints=["A AB AB AB"], passive_constraints=["B AB AB"]) res = classify(REtorProblem2) self.assertEqual(res.det_lower_bound, LOG) self.assertEqual(res.det_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_lower_bound, LOGLOG)
def test_brt0(self): binary_rooted_tree_problem0 = GenericProblem( ["a : a a"], ["a : a"], flags=ProblemFlags(is_tree=True)) res = classify(binary_rooted_tree_problem0) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def test_re3(self): # const, 1 round solvable REtorProblem3 = GenericProblem(["M U U U", "PM PM PM PM"], ["M UP UP UP", "U U U U"]) res = classify(REtorProblem3) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def test_re1(self): REtorProblem1 = GenericProblem( active_constraints=["M U U U", "P P P P"], passive_constraints=["M UP UP UP", "U U U U"], ) res = classify(REtorProblem1) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_lower_bound, CONST)
def test_tlp1(self): # AB, CC # BBC, AAA, BBB, AAC, BCC # output: log* n tlp_problem1 = GenericProblem( ["A B", "C C"], ["B B C", "A A A", "B B B", "A A C", "B C C"]) res = classify(tlp_problem1) self.assertEqual(res.det_lower_bound, ITERATED_LOG) self.assertEqual(res.det_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_lower_bound, ITERATED_LOG)
def test_tlp4(self): # AB, CC # BBC, AAA, BBB, AAC, BCC # output: log* n tlp_problem1 = GenericProblem( ["1 2", "3 3"], ["2 2 3", "1 1 1", "2 2 2", "1 1 3", "2 3 3"]) res = classify(tlp_problem1) self.assertEqual(res.det_lower_bound, ITERATED_LOG) self.assertEqual(res.det_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_lower_bound, ITERATED_LOG)
def test_tlp3(self): # AC AB CC BC # BB AC AB BC # output: O(1) 1 tlp_problem3 = GenericProblem(["A C", "A B", "C C", "B C"], ["B B", "A C", "A B", "B C"]) res = classify(tlp_problem3) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def test_tlp2(self): # AA CC BC # AAA AAB AAC BCC ACC # output: O(1) 0 tlp_problem2 = GenericProblem( ["A A", "C C", "B C"], ["A A A", "A A B", "A A C", "B C C", "A C C"]) res = classify(tlp_problem2) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def test_cycle_path4(self): # -undir -n "{ 11, 22, 33 }" -e "{ 12, 21, 13, 31, 23, 32 }" # cycle path classifier cycle_path_problem4 = GenericProblem( ["A A", "B B", "C C"], ["A B", "B A", "A C", "C A", "B C", "C B"], flags=ProblemFlags(is_cycle=True, is_path=False, is_tree=False), ) res = classify(cycle_path_problem4) self.assertEqual(res.det_lower_bound, ITERATED_LOG) self.assertEqual(res.det_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_lower_bound, ITERATED_LOG)
def test_cycle_path1(self): # 3-coloring on a rooted trees (degree not known i.e. not just binary) # 12, 13, 23, 21, 31, 32 in automata-theoretic formalism # cycle path classifier cycle_path_tree_problem1 = GenericProblem( ["a : a a a a", "b : b b b b", "c : c c c c"], ["a : b", "a : c", "b : c", "b : a", "c : a", "c : b"], flags=ProblemFlags(is_tree=True, ), ) res = classify(cycle_path_tree_problem1) self.assertEqual(res.det_lower_bound, ITERATED_LOG) self.assertEqual(res.det_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_upper_bound, ITERATED_LOG) self.assertEqual(res.rand_lower_bound, ITERATED_LOG)
def test_brt3(self): # "121", # "131" # "132" # decider, tree-classification # output: unsolvable binary_rooted_tree_problem3 = GenericProblem( ["b : a a", "c : a a", "c : a b"], ["a : a", "b : b", "c : c"], flags=ProblemFlags(is_tree=True), ) res = classify(binary_rooted_tree_problem3) self.assertEqual(res.det_lower_bound, UNSOLVABLE) self.assertEqual(res.det_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_upper_bound, UNSOLVABLE) self.assertEqual(res.rand_lower_bound, UNSOLVABLE)
def test_brt2(self): # "121", # "132", # "213" # decider, tree-classification # output: O(log n) binary_rooted_tree_problem2 = GenericProblem( ["b : a a", "c : a b", "a : b c"], ["a : a", "b : b", "c : c"], flags=ProblemFlags(is_tree=True), ) res = classify(binary_rooted_tree_problem2) self.assertEqual(res.det_lower_bound, LOG) self.assertEqual(res.det_upper_bound, LOG) self.assertEqual(res.rand_upper_bound, LOG) self.assertEqual(res.rand_lower_bound, LOG)
def test_brt1(self): # "111", # "121", # "131" # "132" # decider, tree-classification # output: O(1) binary_rooted_tree_problem1 = GenericProblem( ["a : a a", "b : a a", "c : a a", "c : a b"], ["a : a", "b : b", "c : c"], flags=ProblemFlags(is_tree=True), ) res = classify(binary_rooted_tree_problem1) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def test_cycle_path3(self): # -dir -n "{00, 1M}" -e "{01, 10, 11, MM}" # --start-constr "{ 1 }" --end-constr "{ 0 }" # cycle path classifier cycle_path_problem3 = GenericProblem( ["A : A", "B : M"], # node constraints ["A : B", "B : A", "B : B", "M : M"], # edge constraints leaf_constraints=["B"], leaf_allow_all=False, # leaf constraint = end-constr root_constraints=["B"], root_allow_all=False, # root constraint = start-constr flags=ProblemFlags( is_cycle=False, is_path=True, is_tree=False, ), ) res = classify(cycle_path_problem3) self.assertEqual(res.det_lower_bound, CONST) self.assertEqual(res.det_upper_bound, CONST) self.assertEqual(res.rand_upper_bound, CONST) self.assertEqual(res.rand_lower_bound, CONST)
def classify(p: GenericProblem, context: ClassifyContext) -> GenericResponse: if context.brt_preclassified: return GenericResponse(p) validate(p) alphabet = p.get_alphabet() constraints = [move_root_label_to_center(x) for x in p.active_constraints] for i, label in enumerate(alphabet): constraints = [x.replace(label, str(i + 1)) for x in constraints] result = getProblem(constraints) return GenericResponse( p, complexity_mapping[result["upper-bound"]], complexity_mapping[result["lower-bound"]], UNSOLVABLE, complexity_mapping[result[ "lower-bound"]], # because randomised LB is also a deterministic LB result["solvable-count"], result["unsolvable-count"], )
def to_problem(self) -> GenericProblem: p = GenericProblem(["A A"], ["A A"]) p.id = self.id p.active_constraints = self.active_constraints p.passive_constraints = self.passive_constraints p.leaf_constraints = self.leaf_constraints p.root_constraints = self.root_constraints alphabet = p.get_alphabet() p.leaf_allow_all = (set(flatten(p.leaf_constraints)) - {" "}) == set(alphabet) p.root_allow_all = (set(flatten(p.root_constraints)) - {" "}) == set(alphabet) p.flags = ProblemFlags( is_tree=self.is_tree, is_cycle=self.is_cycle, is_path=self.is_path, is_directed_or_rooted=self.is_directed_or_rooted, is_regular=self.is_regular, ) return p
def store_problem_and_get_with_id(p: GenericProblem) -> GenericProblem: with get_db_cursor(commit=True) as cur: problem_data = ( p.get_active_degree(), p.get_passive_degree(), len(p.get_alphabet()), each_constr_is_homogeneous(p.active_constraints), each_constr_is_homogeneous(p.passive_constraints), list(p.active_constraints), list(p.passive_constraints), list(p.leaf_constraints), list(p.root_constraints), p.flags.is_tree, p.flags.is_cycle, p.flags.is_path, p.flags.is_directed_or_rooted, p.flags.is_regular, ) cur.execute( """ INSERT INTO problems ( active_degree, passive_degree, label_count, actives_all_same, passives_all_same, active_constraints, passive_constraints, root_constraints, leaf_constraints, is_tree, is_cycle, is_path, is_directed_or_rooted, is_regular ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s ) ON CONFLICT DO NOTHING RETURNING id;""", problem_data, ) res = cur.fetchone() if res is None: cur.execute( """ SELECT id FROM problems WHERE active_degree = %s AND passive_degree = %s AND label_count = %s AND actives_all_same = %s AND passives_all_same = %s AND active_constraints = %s AND passive_constraints = %s AND root_constraints = %s AND leaf_constraints = %s AND is_tree = %s AND is_cycle = %s AND is_path = %s AND is_directed_or_rooted = %s AND is_regular = %s; """, problem_data, ) res = cur.fetchone() p.id = res["id"] return p
def classify(p: GenericProblem, context: ClassifyContext) -> GenericResponse: active_degree = len(p.active_constraints[0]) if len( p.active_constraints) else 2 passive_degree = len(p.passive_constraints[0]) if len( p.passive_constraints) else 2 leaf_degree = len(p.leaf_constraints[0]) if len(p.leaf_constraints) else 1 if leaf_degree != 1: raise Exception("cyclepath", "Leaf constraints must always be of degree 1") if passive_degree != 2: raise Exception("cyclepath", "Passive constraints must always be of degree 2") if p.flags.is_tree: if not each_constr_is_homogeneous(p.active_constraints): raise Exception( "cyclepath", "On trees, node constraints must be the same for all incident edges.", ) if not p.flags.is_directed_or_rooted: raise Exception( "cyclepath", "In the context of trees, only rooted ones can be classified.", ) elif active_degree != 2: raise Exception( "cyclepath", "In a path or cycle, active constraints must always be of degree 2", ) problem_type = (Type.TREE if p.flags.is_tree else ( Type.DIRECTED if p.flags.is_directed_or_rooted else Type.UNDIRECTED)) if not p.flags.is_directed_or_rooted: p.passive_constraints = p.passive_constraints + tuple( [cs[::-1] for cs in p.passive_constraints]) p.active_constraints = p.active_constraints + tuple( [cs[::-1] for cs in p.active_constraints]) edge_constraints = set(p.passive_constraints) node_constraints = {} if problem_type == Type.TREE else set( p.active_constraints) start_constraints = {} if p.root_allow_all else set(p.root_constraints) end_constraints = {} if p.leaf_allow_all else set(p.leaf_constraints) cp_problem = CyclePathProblem( node_constraints, edge_constraints, start_constraints, end_constraints, problem_type, ) result = cpClassify(cp_problem) complexity_mapping = { CP_CONST: CONST, CP_GLOBAL: GLOBAL, CP_ITERATED_LOG: ITERATED_LOG, CP_UNSOLVABLE: UNSOLVABLE, } normalised_complexity = complexity_mapping[result["complexity"]] return GenericResponse( p, normalised_complexity, normalised_complexity, normalised_complexity, normalised_complexity, result["solvable"], result["unsolvable"], )