First time here? Check out the FAQ!

Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

answered 0 years ago

Max Alekseyev gravatar image

This problem is best solved with integer programming. Here is my solution, which you can also run at Sagecell:

import itertools
from sage.numerical.mip import MIPSolverException

def find_claw_decomposition(G):

    # quick check
    if G.size()%3: 
        print('Size is not a multiple of 3')
        return None

    milp = MixedIntegerLinearProgram()

    # indicator variables: att[(edge,node)] = 1 iff edge is part of claw rooted at node
    att = milp.new_variable(binary=True)
    # ratios from division by 3
    rat = milp.new_variable(integer=True)

    # each edge should be attached to one of its endpoints
    for e in G.edges(labels=False):
        milp.add_constraint( att[(e,e[0])] + att[(e,e[1])] == 1 )

    # each node should have a multiple of 3 attached edges
    for v in G.vertices():
        milp.add_constraint( sum( att[(e,v)] for e in G.edges_incident(v,labels=False) ) == 3 * rat[v] )

    try:
        milp.solve()
    except MIPSolverException:
        print('No decomposition exists.')
        return None

    claws = []
    A = milp.get_values(att,convert=bool,tolerance=1e-6)

    for v in G.vertices():
        it = filter(lambda e: A[(e,v)], G.edges_incident(v,labels=False))
        while ( claw := tuple(itertools.islice(it,3)) ):
            claws.append(claw)

    return claws
click to hide/show revision 2
No.2 Revision

This problem is best solved with integer programming. Here is my solution, which you can also run at Sagecell:

import itertools
from sage.numerical.mip import MIPSolverException

def find_claw_decomposition(G):

    # quick check
    if G.size()%3: 
        print('Size is not a multiple of 3')
        return None

    milp = MixedIntegerLinearProgram()

    # indicator variables: att[(edge,node)] = 1 iff edge is part of claw rooted at node
    att = milp.new_variable(binary=True)
    # ratios from division by 3
    rat = milp.new_variable(integer=True)

    # each edge should be attached to one of its endpoints
    for e in G.edges(labels=False):
        milp.add_constraint( att[(e,e[0])] + att[(e,e[1])] == 1 )

    # each node should have a multiple of 3 attached edges
    for v in G.vertices():
        milp.add_constraint( sum( att[(e,v)] for e in G.edges_incident(v,labels=False) ) == 3 * rat[v] )

    try:
        milp.solve()
    except MIPSolverException:
        print('No decomposition exists.')
        return None

    claws = []
    A = milp.get_values(att,convert=bool,tolerance=1e-6)

    for v in G.vertices():
        it = filter(lambda e: A[(e,v)], G.edges_incident(v,labels=False))
        while ( claw := tuple(itertools.islice(it,3)) ):
            claws.append(claw)

    return claws

For example, running on your second graph:

G = Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5), (4, 5)])
find_claw_decomposition(G)

produces the following decomposition:

[((0, 1), (0, 2), (0, 3)),
 ((1, 2), (1, 3), (1, 5)),
 ((0, 4), (1, 4), (2, 4)),
 ((2, 5), (3, 5), (4, 5))]
click to hide/show revision 3
No.3 Revision

This problem is best solved with integer programming. Here is my solution, which you can also run at Sagecell:

import itertools
from sage.numerical.mip import MIPSolverException

def find_claw_decomposition(G):

    # quick check
    if G.size()%3: 
        print('Size is not a multiple of 3')
        return None

    milp = MixedIntegerLinearProgram()

    # indicator variables: att[(edge,node)] = 1 iff edge is part of claw rooted at node
    att = milp.new_variable(binary=True)
    # ratios from division by 3
rat[v] = number of claws rooted at v
    rat = milp.new_variable(integer=True)

    # each edge should be attached to one of its endpoints
    for e in G.edges(labels=False):
        milp.add_constraint( att[(e,e[0])] + att[(e,e[1])] == 1 )

    # each node should have a multiple of 3 attached edges
    for v in G.vertices():
        milp.add_constraint( sum( att[(e,v)] for e in G.edges_incident(v,labels=False) ) == 3 * rat[v] )

    try:
        milp.solve()
    except MIPSolverException:
        print('No decomposition exists.')
        return None

    claws = []
    A = milp.get_values(att,convert=bool,tolerance=1e-6)

    for v in G.vertices():
        it = filter(lambda e: A[(e,v)], G.edges_incident(v,labels=False))
        while ( claw := tuple(itertools.islice(it,3)) ):
            claws.append(claw)

    return claws

For example, running on your second graph:

G = Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (1, 5), (2, 4), (2, 5), (3, 5), (4, 5)])
find_claw_decomposition(G)

produces the following decomposition:

[((0, 1), (0, 2), (0, 3)),
 ((1, 2), (1, 3), (1, 5)),
 ((0, 4), (1, 4), (2, 4)),
 ((2, 5), (3, 5), (4, 5))]