def planar_graph_grammar(): """Constructs the grammar for planar graphs. Returns ------- grammar : DecompositionGrammar The grammar for sampling from G, G_dx and G_dx_dx. """ # Some shortcuts to make the grammar more readable. Rule = pybo.AliasSampler G_1 = Rule('G_1') G_1_dx = Rule('G_1_dx') G_1_dx_dx = Rule('G_1_dx_dx') G_1_dx_dx_dx = Rule('G_1_dx_dx_dx') G = Rule('G') G_dx = Rule('G_dx') G_dx_dx = Rule('G_dx') Set = pybo.SetSampler grammar = pybo.DecompositionGrammar() grammar.rules = one_connected_graph_grammar().rules EarlyRejectionControl.grammar = grammar grammar.add_rules({ 'G': Set(0, G_1), 'G_dx': G_1_dx * G, 'G_dx_dx': G_1_dx_dx * G + G_1_dx * G_dx, 'G_dx_dx_dx': G_1_dx_dx_dx * G + G_1_dx_dx * G_dx + G_1_dx_dx * G_dx + G_1_dx * G_dx_dx }) grammar.set_builder(['G', 'G_dx', 'G_dx_dx', 'G_dx_dx_dx'], PlanarGraphBuilder()) return grammar
def irreducible_dissection_grammar(): """Builds the dissection grammar. Must still be initialized with init(). Returns ------- DecompositionGrammar The grammar for sampling from J_a and J_a_dx. """ # Some shorthands to keep the grammar readable. L = pybo.LAtomSampler Rule = pybo.AliasSampler K = Rule('K') K_dx = Rule('K_dx') K_dx_dx = Rule('K_dx_dx') I = Rule('I') I_dx = Rule('I_dx') I_dx_dx = Rule('I_dx_dx') J = Rule('J') J_dx = Rule('J_dx') J_dx_dx = Rule('J_dx_dx') Bij = pybo.BijectionSampler Rej = pybo.RejectionSampler grammar = pybo.DecompositionGrammar() # This grammar depends on the binary tree grammar so we add it. grammar.rules = binary_tree_grammar().rules EarlyRejectionControl.grammar = grammar grammar.add_rules({ # Non-derived dissections (standard, rooted, admissible). 'I': Bij(K, closure), # We drop the 3*L*U factor here. # This bijection does not preserve l-size/u-size. 'J': Bij(I, add_random_root_edge), 'J_a': Rej(J, is_admissible), # Derived dissections. # The result is not a derived class, the bijection does not preserve l-size. 'I_dx': Bij(K_dx, closure), # We drop the factor 3*U. # This bijection does not preserve l-size/u-size. 'J_dx': Bij(I + L() * I_dx, add_random_root_edge), 'J_a_dx': Rej(J_dx, is_admissible), # Bi-derived dissections. # Does not preserve l-size, result is not a derived class. 'I_dx_dx': Bij(K_dx_dx, closure), # We dropped a factor. 'J_dx_dx': Bij(2 * I_dx + L() * I_dx_dx, add_random_root_edge), 'J_a_dx_dx': Rej(J_dx_dx, is_admissible) }) return grammar
def one_connected_graph_grammar(): """Constructs the grammar for connected planar graphs. Returns ------- DecompositionGrammar The grammar for sampling from G_1_dx and G_1_dx_dx. """ # Some shortcuts to make the grammar more readable. L = pybo.LAtomSampler Rule = pybo.AliasSampler G_2_dx = Rule('G_2_dx') G_2_dx_dx = Rule('G_2_dx_dx') G_2_dx_dx_dx = Rule('G_2_dx_dx_dx') G_1_dx = Rule('G_1_dx') G_1_dx_dx = Rule('G_1_dx_dx') G_1_dx_dx_dx = Rule('G_1_dx_dx_dx') Set = pybo.SetSampler LSubs = pybo.LSubsSampler Bij = pybo.BijectionSampler Rej = pybo.RejectionSampler grammar = pybo.DecompositionGrammar() grammar.rules = two_connected_graph_grammar().rules EarlyRejectionControl.grammar = grammar grammar.add_rules({ 'G_1': Bij( Rej( G_1_dx, rej_to_G_1 # See lemma 15. ), underive ), 'G_1_dx': Set( 0, LSubs( G_2_dx, L() * G_1_dx ) ), 'G_1_dx_dx': Bij( Bij( (G_1_dx + L() * G_1_dx_dx) * LSubs(G_2_dx_dx, L() * G_1_dx), subs_marked_vertex ) * G_1_dx, merge ), 'G_1_dx_dx_dx': Bij( Bij( (2 * G_1_dx_dx + L() * G_1_dx_dx_dx) * LSubs(G_2_dx_dx, L() * G_1_dx), subs_marked_vertex ) * G_1_dx, merge ) + Bij( Bij( (G_1_dx + L() * G_1_dx_dx) ** 2 * LSubs(G_2_dx_dx_dx, L() * G_1_dx), subs_marked_vertex_2 ) * G_1_dx, merge ) + Bij( Bij( (G_1_dx + L() * G_1_dx_dx) * LSubs(G_2_dx_dx, L() * G_1_dx), subs_marked_vertex ) * G_1_dx_dx, merge ), }) grammar.set_builder(['G_1_dx'], Merger()) return grammar
def two_connected_graph_grammar(): """Constructs the grammar for two connected planar graphs. Returns ------- DecompositionGrammar The grammar for sampling from G_2_dx and G_2_dx_dx. """ Z = pybo.ZeroAtomSampler L = pybo.LAtomSampler Rule = pybo.AliasSampler D = Rule('D') D_dx = Rule('D_dx') D_dx_dx = Rule('D_dx_dx') F = Rule('F') F_dx = Rule('F_dx') F_dx_dx = Rule('F_dx_dx') G_2_dy = Rule('G_2_dy') G_2_dx_dy = Rule('G_2_dx_dy') G_2_dx_dx_dy = Rule('G_2_dx_dx_dy') G_2_arrow = Rule('G_2_arrow') G_2_arrow_dx = Rule('G_2_arrow_dx') G_2_arrow_dx_dx = Rule('G_2_arrow_dx_dx') Trans = pybo.TransformationSampler Bij = pybo.BijectionSampler DxFromDy = pybo.LDerFromUDerSampler grammar = pybo.DecompositionGrammar() grammar.rules = network_grammar().rules EarlyRejectionControl.grammar = grammar grammar.add_rules({ # two connected 'G_2_arrow': Trans(Z() + D, to_G_2_arrow, eval_transform=divide_by_1_plus_y), # see 5.5 'F': Bij(L()**2 * G_2_arrow, to_G_2), 'G_2_dy': Trans(F, to_u_derived_class, eval_transform=divide_by_2), 'G_2_dx': Bij( DxFromDy( G_2_dy, alpha_l_u=2.0 ), # see p. 26 TODO check this, error in paper? (no error, 2 is caused by the link graph) mark_l_atom), # l-derived two connected 'G_2_arrow_dx': Trans(D_dx, to_G_2_arrow_dx, eval_transform=divide_by_1_plus_y), 'F_dx': Bij(L()**2 * G_2_arrow_dx + 2 * L() * G_2_arrow, to_G_2_dx), 'G_2_dx_dy': Trans(F_dx, to_u_derived_class, eval_transform=divide_by_2), 'G_2_dx_dx': Bij( DxFromDy(G_2_dx_dy, alpha_l_u=1.0), # see 5.5 mark_2_l_atoms), # bi-l-derived two connected 'G_2_arrow_dx_dx': Trans(D_dx_dx, to_G_2_arrow_dx_dx, eval_transform=divide_by_1_plus_y), 'F_dx_dx': Bij(L()**2 * G_2_arrow_dx_dx + 4 * L() * G_2_arrow_dx + 2 * G_2_arrow, to_G_2_dx_dx), 'G_2_dx_dx_dy': Trans(F_dx_dx, to_u_derived_class, eval_transform=divide_by_2), 'G_2_dx_dx_dx': Bij(DxFromDy(G_2_dx_dx_dy, alpha_l_u=1.0), mark_3_l_atoms), }) grammar.set_builder(['G_2_arrow'], ZeroAtomGraphBuilder()) return grammar
def network_grammar(): """Constructs the grammar for networks. Returns ------- DecompositionGrammar The grammar for sampling from D and D_dx. """ L = pybo.LAtomSampler U = pybo.UAtomSampler Rule = pybo.AliasSampler G_3_arrow = Rule('G_3_arrow') G_3_arrow_dx = Rule('G_3_arrow_dx') G_3_arrow_dy = Rule('G_3_arrow_dy') D = Rule('D') D_dx = Rule('D_dx') S = Rule('S') S_dx = Rule('S_dx') P = Rule('P') P_dx = Rule('P_dx') H = Rule('H') H_dx = Rule('H_dx') D_dx_dx = Rule('D_dx_dx') S_dx_dx = Rule('S_dx_dx') P_dx_dx = Rule('P_dx_dx') H_dx_dx = Rule('H_dx_dx') G_3_arrow_dx_dx = Rule('G_3_arrow_dx_dx') G_3_arrow_dx_dy = Rule('G_3_arrow_dx_dy') G_3_arrow_dy_dy = Rule('G_3_arrow_dy_dy') Bij = pybo.BijectionSampler Set = pybo.SetSampler USubs = pybo.USubsSampler grammar = pybo.DecompositionGrammar() grammar.rules = three_connected_graph_grammar().rules grammar.add_rules({ # networks 'D': U() + S + P + H, 'S': (U() + P + H) * D * L(), 'P': U() * Set(1, S + H) + Set(2, S + H), 'H': Bij(USubs(G_3_arrow, D), g_3_arrow_to_network), # l-derived networks 'D_dx': S_dx + P_dx + H_dx, 'S_dx': (P_dx + H_dx) * D * L() + (U() + P + H) * D_dx * L() + (U() + P + H) * D, 'P_dx': U() * (S_dx + H_dx) * Set(0, S + H) + (S_dx + H_dx) * Set(1, S + H), 'H_dx': Bij( USubs(G_3_arrow_dx, D) + D_dx * USubs(G_3_arrow_dy, D), g_3_arrow_to_network ), # bi-l-derived networks 'D_dx_dx': S_dx_dx + P_dx_dx + H_dx_dx, 'S_dx_dx': (P_dx_dx + H_dx_dx) * D * L() + 2 * (P_dx + H_dx) * D_dx * L() + (U() + P + H) * D_dx_dx * L() + 2 * (P_dx + H_dx) * D + 2 * (U() + P + H) * D_dx, 'P_dx_dx': U() * (S_dx_dx + H_dx_dx) * Set(0, S + H) + U() * (S_dx + H_dx) ** 2 * Set(0, S + H) + (S_dx_dx + H_dx_dx) * Set(1, S + H) + (S_dx + H_dx) ** 2 * Set(0, S + H), 'H_dx_dx': Bij( USubs(G_3_arrow_dx_dx, D) + 2 * D_dx * USubs(G_3_arrow_dx_dy, D) + D_dx_dx * USubs(G_3_arrow_dy, D) + D_dx ** 2 * USubs(G_3_arrow_dy_dy, D), g_3_arrow_to_network ), }) # Set up builders. grammar.set_builder(['D', 'S', 'P', 'H', 'D_dx', 'S_dx', 'P_dx', 'H_dx', 'D_dx_dx', 'S_dx_dx', 'P_dx_dx', 'H_dx_dx'], NetworkBuilder()) grammar.set_builder(['P', 'P_dx', 'P_dx_dx'], PNetworkBuilder()) grammar.set_builder(['S', 'S_dx', 'S_dx_dx'], SNetworkBuilder()) EarlyRejectionControl.grammar = grammar return grammar
def binary_tree_grammar(): """ Builds the bicolored binary tree grammar. Must still be initialized with init(). Returns ------- DecompositionGrammar The grammar for sampling K and K_dx. """ # Some shorthands to keep the grammar readable. L = pybo.LAtomSampler U = pybo.UAtomSampler Rule = pybo.AliasSampler K_dy = Rule('K_dy') R_b_as = Rule('R_b_as') R_w_as = Rule('R_w_as') R_b_head = Rule('R_b_head') R_b_head_help = Rule('R_b_head_help') R_w_head = Rule('R_w_head') R_b = Rule('R_b') R_w = Rule('R_w') K_dy_dx = Rule('K_dy_dx') R_b_dx = Rule('R_b_dx') R_w_dx = Rule('R_w_dx') R_b_as_dx = Rule('R_b_as_dx') R_w_as_dx = Rule('R_w_as_dx') R_b_head_dx = Rule('R_b_head_dx') R_w_head_dx = Rule('R_w_head_dx') Bij = pybo.BijectionSampler Trans = pybo.TransformationSampler Hook = pybo.HookSampler DxFromDy = pybo.LDerFromUDerSampler grammar = pybo.DecompositionGrammar() # Add the decomposition rules. grammar.rules = { # Underived and derived binary trees. 'K': Hook(Trans(K_dy, underive), before=EarlyRejectionControl.activate_rejection, after=EarlyRejectionControl.deactivate_rejection), # See 4.1.6. 'K_dx': DxFromDy( K_dy, alpha_l_u=2 / 3 # See 5.3.1. ), 'K_dy': pybo.RestartableSampler( Hook( Bij(R_b_as + R_w_as, to_K_dy), EarlyRejectionControl. reset # Reset leaf-counter before sampling from this rule. ), ), 'R_b_as': R_w * L() * U() + U() * L() * R_w + R_w * L() * R_w, 'R_w_as': R_b_head * U() + U() * R_b_head + R_b**2, 'R_b_head': R_w_head * L() * R_b_head_help + R_b_head_help * L() * R_w_head + R_w_head * L() * R_w_head, 'R_b_head_help': U() * U(), 'R_w_head': U() + R_b * U() + U() * R_b + R_b**2, 'R_b': (U() + R_w) * L() * (U() + R_w), 'R_w': (U() + R_b)**2, # Bi-derived binary trees. 'K_dx_dx': DxFromDy( K_dy_dx, alpha_l_u=2 / 3 # See 5.3.1, 2/3 is also valid for K_dx. ), 'K_dy_dx': Bij(R_b_as_dx + R_w_as_dx, to_K_dy_dx), 'R_b_as_dx': U() * L() * R_w_dx + R_w_dx * L() * U() + R_w * L() * R_w_dx + R_w_dx * L() * R_w + U() * R_w + R_w * U() + R_w**2, 'R_w_as_dx': R_b_head_dx * U() + U() * R_b_head_dx + R_b * R_b_dx + R_b_dx * R_b, 'R_b_head_dx': R_b_head_help * L() * R_w_head_dx + R_w_head_dx * L() * R_b_head_help + R_w_head * L() * R_w_head_dx + R_w_head_dx * L() * R_w_head + R_w_head * R_b_head_help + R_b_head_help * R_w_head + R_w_head**2, 'R_w_head_dx': R_b_dx * (R_b + U()) + (R_b + U()) * R_b_dx, # is the same as R_w_dx! 'R_b_dx': (U() + R_w)**2 + R_w_dx * L() * (U() + R_w) + (U() + R_w) * L() * R_w_dx, 'R_w_dx': R_b_dx * (U() + R_b) + (U() + R_b) * R_b_dx, } # Set builders. grammar.set_builder([ 'R_w', 'R_w_head', 'R_w_as', 'R_b_head_help', 'R_w_dx', 'R_w_head_dx', 'R_w_as_dx' ], WhiteRootedBinaryTreeBuilder()) grammar.set_builder( ['R_b', 'R_b_head', 'R_b_as', 'R_b_dx', 'R_b_head_dx', 'R_b_as_dx'], BlackRootedBinaryTreeBuilder()) # Set the grammar in the early rejection control variables. EarlyRejectionControl.grammar = grammar # We do not init the grammar here. return grammar
def three_connected_graph_grammar(): """Builds the three-connected planar graph grammar. Returns ------- DecompositionGrammar The grammar for sampling from G_3_arrow_dx and G_3_arrow_dy """ # Some shorthands to keep the grammar readable. Rule = pybo.AliasSampler J_a = Rule('J_a') J_a_dx = Rule('J_a_dx') J_a_dx_dx = Rule('J_a_dx_dx') G_3_arrow = Rule('G_3_arrow') G_3_arrow_dx = Rule('G_3_arrow_dx') G_3_arrow_dx_dx = Rule('G_3_arrow_dx_dx') G_3_arrow_dx_dy = Rule('G_3_arrow_dx_dy') M_3_arrow = Rule('M_3_arrow') M_3_arrow_dx = Rule('M_3_arrow_dx') M_3_arrow_dx_dx = Rule('M_3_arrow_dx_dx') Bij = pybo.BijectionSampler Rej = pybo.RejectionSampler Trans = pybo.TransformationSampler DyFromDx = pybo.UDerFromLDerSampler grammar = pybo.DecompositionGrammar() # Depends on irreducible dissection so we add those rules. grammar.rules = irreducible_dissection_grammar().rules EarlyRejectionControl.grammar = grammar grammar.add_rules({ # Non-derived 3-connected rooted planar maps/graphs. 'M_3_arrow': Bij(J_a, primal_map), 'G_3_arrow': Trans(M_3_arrow, eval_transform=divide_by_2), # See 4.1.9. # Derived 3-connected rooted planar maps/graphs. 'M_3_arrow_dx': Bij(J_a_dx, primal_map), 'G_3_arrow_dx': Trans(M_3_arrow_dx, to_l_derived_class, eval_transform=divide_by_2), 'G_3_arrow_dy': Bij( DyFromDx(G_3_arrow_dx, alpha_u_l=3), # See 5.3.3. mark_u_atom), # Bi-derived 3-connected rooted planar maps/graphs. 'M_3_arrow_dx_dx': Bij(J_a_dx_dx, primal_map), 'G_3_arrow_dx_dx': Trans(M_3_arrow_dx_dx, to_bi_l_derived_class, eval_transform=divide_by_2), 'G_3_arrow_dx_dy': Bij(DyFromDx(G_3_arrow_dx_dx, alpha_u_l=3), mark_u_atom), 'G_3_arrow_dy_dy': Bij( DyFromDx(Bij(G_3_arrow_dx_dy, lambda gamma: gamma.invert_derivation_order()), alpha_u_l=3), mark_2_u_atoms), # Usual 3-connected planar graphs for testing/debugging purposes. 'G_3': Bij(Rej(G_3_arrow, lambda g: pybo.bern(1 / g.number_of_edges)), lambda g: HalfEdgeGraph(g.half_edge)), }) return grammar