diff --git a/Kmn_2factor.sage b/Kmn_2factor.sage new file mode 100644 index 0000000..21ff5fd --- /dev/null +++ b/Kmn_2factor.sage @@ -0,0 +1,102 @@ +# cogirth-packing gap of projections of graphic matroids +# see if gap(projection) <= 2 * gap(graph) + +from sage.all import * +from sage.matroids.all import * +from sage.graphs.all import * +import gurobipy as gp +from gurobipy import GRB +from fractions import Fraction + +env = gp.Env(empty=True) +env.setParam("OutputFlag",0) +env.start() + +def representative_vectors(m, n): + for w1 in range(m+1): + for w2 in range(n+1): + v = [0]*(m+n) + v[:w1] = [1]*w1 + v[m:m+w2] = [1]*w2 + yield tuple(v) + +def base_hittingset_with_callback(M, integral=true): + # model + model = gp.Model("mip1",env=env) + groundset = M.groundset() + x = dict() + if integral: + for e in groundset: x[e]=model.addVar(vtype=GRB.BINARY) + else: + for e in groundset: x[e]=model.addVar(vtype=GRB.CONTINUOUS,lb=0,ub=1) + # there is no lazy constraint for LP... + for B in M.bases(): + model.addConstr(gp.quicksum([x[e] for e in B])>=1) + model.setObjective(gp.quicksum([x[e] for e in groundset]), GRB.MINIMIZE) + # callback + def callback_func(model, where): + if integral and where == GRB.Callback.MIPSOL: + sol_values = {key: model.cbGetSolution(var) + for key, var in x.items()} + # find min weight base in the matroid + base = frozenset() + minweight=0 + for e in sorted(groundset, key=lambda ee: sol_values[ee]): + if M.is_independent(base.union([e])): + base=base.union([e]) + minweight=minweight+sol_values[e] + if minweight < 1: + model.cbLazy(gp.quicksum([x[e] for e in base])>=1) + # solve + if integral: + model.Params.Heuristics = 0 + model.Params.LazyConstraints = 1 + model.optimize(callback_func) + else: + model.optimize() + return model.ObjVal + + +cnt=0 # actual number of instances tested +maxfactor=0 +for N in range(2,10): + for M in range(N,10): + g=graphs.CompleteBipartiteGraph(N,M) + MG=Matroid(g) + mincut=base_hittingset_with_callback(MG) + treepacking=base_hittingset_with_callback(MG,integral=false) + g_gap=mincut/treepacking + A=g.incidence_matrix() + n,m = A.dimensions() + # enumerate all vectors in F_2^n with even number of 1s + m_gap=0 + for v in representative_vectors(N,M): + cnt=cnt+1 + v_col=matrix(v).transpose() + A_t=A.augment(v_col) + # print(A_t) + MM=Matroid(matrix=A_t,field=GF(2))/m #contract the last element + cogirth = base_hittingset_with_callback(MM,integral=true) + strength = base_hittingset_with_callback(MM,integral=false) + gap = cogirth/strength + m_gap=max(m_gap,gap) + # maxgap=max(gap,maxgap) + # if gap > maxgap: + # maxgap = gap + # print(f"find a large gap: {gap}") + # with open("projection.out", "a") as file: + # file.write("##################################\n" + # +str(gap)+"\n"+str(A_t) + # +"\n##################################\n") + factor=m_gap/g_gap + fr = lambda xx: str(Fraction(xx).limit_denominator(m)) + maxfactor=max(maxfactor,factor) + print(f"K_{{{N},{M}}} graph gap = {fr(g_gap):8} projection gap = {fr(m_gap):8} factor={fr(factor):8} max={maxfactor}") + if factor >= 2: + with open("Kmn_2factor.out", "a") as file: + file.write(f"K_{{{N},{M}}} graph gap = {fr(g_gap):8} projection gap = {fr(m_gap):8} factor={fr(factor):8} max={maxfactor}\n" + +str(A_t)+"\n") + if factor > 2: + print("conjecture is wrong") + print(str(A_t)) + exit(1) \ No newline at end of file diff --git a/cogirth_callback.sage b/cogirth_callback.sage new file mode 100644 index 0000000..506f13f --- /dev/null +++ b/cogirth_callback.sage @@ -0,0 +1,73 @@ +from sage.all import * +from sage.matroids.all import * +from sage.graphs.all import * +import gurobipy as gp +from gurobipy import GRB + +env = gp.Env(empty=True) +env.setParam("OutputFlag",0) +env.start() + +def base_hittingset_with_callback(M, integral=true): + # model + model = gp.Model("mip1",env=env) + groundset = M.groundset() + x = dict() + if integral: + for e in groundset: x[e]=model.addVar(vtype=GRB.BINARY) + else: + for e in groundset: x[e]=model.addVar(vtype=GRB.CONTINUOUS,lb=0,ub=1) + # there is no lazy constraint for LP... + for B in M.bases(): + model.addConstr(gp.quicksum([x[e] for e in B])>=1) + model.setObjective(gp.quicksum([x[e] for e in groundset]), GRB.MINIMIZE) + # callback + def callback_func(model, where): + if integral and where == GRB.Callback.MIPSOL: + sol_values = {key: model.cbGetSolution(var) + for key, var in x.items()} + # find min weight base in the matroid + base = frozenset() + minweight=0 + for e in sorted(groundset, key=lambda ee: sol_values[ee]): + if M.is_independent(base.union([e])): + base=base.union([e]) + minweight=minweight+sol_values[e] + if minweight < 1: + model.cbLazy(gp.quicksum([x[e] for e in base])>=1) + # solve + if integral: + model.Params.Heuristics = 0 + model.Params.LazyConstraints = 1 + model.optimize(callback_func) + else: + model.optimize() + return model.ObjVal + +# tests +if __name__ == "__main__": + def cogirthip(bases, integral=true): + model = gp.Model("mip1",env=env) + groundset=frozenset() + for B in bases: groundset=groundset|frozenset(B) + x = dict() + if integral: + for e in groundset: x[e]=model.addVar(vtype=GRB.BINARY) + else: + for e in groundset: x[e]=model.addVar(vtype=GRB.CONTINUOUS,lb=0) + model.setObjective(gp.quicksum([x[e] for e in groundset]), GRB.MINIMIZE) + for B in bases: + model.addConstr(gp.quicksum([x[e] for e in B])>=1) + model.optimize() + return model.ObjVal + f = lambda g: g.is_connected() + for N in range(4,10): + for g in filter(f, graphs(N)): + M=Matroid(g) + cb=base_hittingset_with_callback(M) + bf=cogirthip(M.bases()) + if randint(0,100)==1: + print(f"callback:{cb}, bf:{bf}") + if abs(cb-bf) > 0.01: + print(f"callback:{cb}, bf:{bf}") + exit(1) \ No newline at end of file diff --git a/projection.out b/projection.out index 2108156..6afb851 100644 --- a/projection.out +++ b/projection.out @@ -1,108 +1,8 @@ -################################## -1.0 -[1 0 0 1] -[0 1 0 1] -[0 0 1 0] -[1 1 1 0] -################################## -################################## -1.3333333333333333 -[1 0 0 1] -[0 1 0 1] -[0 0 1 1] -[1 1 1 1] -################################## -################################## -1.4999999999999998 -[1 0 0 0 1] -[0 1 0 0 1] -[0 0 1 0 1] -[0 0 0 1 1] -[1 1 1 1 0] -################################## -################################## -1.5 -[1 1 0 0 0 0 1] -[0 0 1 1 0 0 1] -[0 0 0 0 1 1 1] -[1 0 1 0 1 0 0] -[0 1 0 1 0 1 1] -################################## -################################## -1.7142857142857137 -[1 1 0 0 0 0 0 1] -[0 0 1 1 0 0 0 1] -[0 0 0 0 1 1 0 1] -[1 0 1 0 1 0 1 1] -[0 1 0 1 0 1 1 0] -################################## -################################## -1.7142857142857142 -[1 1 0 0 0 0 0 1] -[0 0 1 1 0 0 0 1] -[0 0 0 0 1 1 0 1] -[0 0 0 0 0 0 1 1] -[1 0 1 0 1 0 0 0] -[0 1 0 1 0 1 1 0] -################################## -################################## -2.0 -[1 1 0 0 0 0 0 0 1] -[0 0 1 1 0 0 0 0 1] -[0 0 0 0 1 1 0 0 1] -[0 0 0 0 0 0 1 1 1] -[1 0 1 0 1 0 1 0 0] -[0 1 0 1 0 1 0 1 0] -################################## -################################## -2.1333333333333337 -[1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1] -[1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1] -[0 1 0 0 0 1 0 0 0 1 1 1 0 0 0 1] -[0 0 1 0 0 0 1 0 0 1 0 0 1 1 0 1] -[0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 1] -[0 0 0 0 1 0 0 0 1 0 0 1 0 1 1 1] -################################## -################################## -2.1428571428571423 -[1 1 1 0 0 0 0 0 0 0 0 0 0 0 1] -[0 0 0 1 1 1 0 0 0 0 0 0 0 0 1] -[0 0 0 0 0 0 1 1 1 0 0 0 0 0 1] -[0 0 0 0 0 0 0 0 0 1 1 1 0 0 1] -[1 0 0 1 0 0 1 0 0 1 0 0 1 0 1] -[0 1 0 0 1 0 0 1 0 0 1 0 0 1 1] -[0 0 1 0 0 1 0 0 1 0 0 1 1 1 0] -################################## -################################## -2.1818181818181817 -[1 1 0 0 0 0 0 0 0 0 0 1] -[0 0 1 1 0 0 0 0 0 0 0 1] -[0 0 0 0 1 1 0 0 0 0 0 1] -[0 0 0 0 0 0 1 1 0 0 0 1] -[0 0 0 0 0 0 0 0 1 1 0 1] -[1 0 1 0 1 0 0 0 0 0 1 1] -[0 1 0 1 0 0 1 0 1 0 0 0] -[0 0 0 0 0 1 0 1 0 1 1 0] -################################## -################################## -2.181818181818182 -[1 1 0 0 0 0 0 0 0 0 0 1] -[0 0 1 1 0 0 0 0 0 0 0 1] -[0 0 0 0 1 1 0 0 0 0 0 1] -[0 0 0 0 0 0 1 1 0 0 0 1] -[0 0 0 0 0 0 0 0 1 1 0 1] -[1 0 1 0 1 0 0 0 0 0 1 0] -[0 1 0 1 0 0 1 0 1 0 0 1] -[0 0 0 0 0 1 0 1 0 1 1 0] -################################## -################################## -2.4000000000000004 -[1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1] -[0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1] -[0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1] -[0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1] -[0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1] -[1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1] -[0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 1] -[0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 1] -################################## +n=7 , m=10 graph gap = 1 projection gap = 2 factor=2 max=2.0 +[1 1 1 0 0 0 0 0 0 0 0] +[0 0 0 1 1 1 0 0 0 0 1] +[0 0 0 0 0 0 1 1 1 0 1] +[0 0 0 0 0 0 0 0 0 1 1] +[1 0 0 1 0 0 1 0 0 0 1] +[0 1 0 0 1 0 0 1 0 0 1] +[0 0 1 0 0 1 0 0 1 1 1] diff --git a/projection.sage b/projection.sage index a15d080..e12b9c1 100644 --- a/projection.sage +++ b/projection.sage @@ -1,41 +1,67 @@ # cogirth-packing gap of projections of graphic matroids +# see if gap(projection) <= 2 * gap(graph) from sage.all import * from sage.matroids.all import * from sage.graphs.all import * import gurobipy as gp from gurobipy import GRB +from fractions import Fraction env = gp.Env(empty=True) env.setParam("OutputFlag",0) env.start() - -def cogirthip(bases, integral=true): +def base_hittingset_with_callback(M, integral=true): + # model model = gp.Model("mip1",env=env) - # model.Params.LogToConsole = 0 - groundset=frozenset() - for B in bases: groundset=groundset|frozenset(B) + groundset = M.groundset() x = dict() if integral: for e in groundset: x[e]=model.addVar(vtype=GRB.BINARY) else: - for e in groundset: x[e]=model.addVar(vtype=GRB.CONTINUOUS,lb=0) - model.setObjective(gp.quicksum([x[e] for e in groundset]), GRB.MINIMIZE) - for B in bases: + for e in groundset: x[e]=model.addVar(vtype=GRB.CONTINUOUS,lb=0,ub=1) + # there is no lazy constraint for LP... + for B in M.bases(): model.addConstr(gp.quicksum([x[e] for e in B])>=1) - model.optimize() + model.setObjective(gp.quicksum([x[e] for e in groundset]), GRB.MINIMIZE) + # callback + def callback_func(model, where): + if integral and where == GRB.Callback.MIPSOL: + sol_values = {key: model.cbGetSolution(var) + for key, var in x.items()} + # find min weight base in the matroid + base = frozenset() + minweight=0 + for e in sorted(groundset, key=lambda ee: sol_values[ee]): + if M.is_independent(base.union([e])): + base=base.union([e]) + minweight=minweight+sol_values[e] + if minweight < 1: + model.cbLazy(gp.quicksum([x[e] for e in base])>=1) + # solve + if integral: + model.Params.Heuristics = 0 + model.Params.LazyConstraints = 1 + model.optimize(callback_func) + else: + model.optimize() return model.ObjVal cnt=0 # actual number of instances tested -maxgap=0 +maxfactor=0 f = lambda g: g.is_connected() for N in range(4,10): for g in filter(f, graphs(N)): + MG=Matroid(g) + mincut=base_hittingset_with_callback(MG) + treepacking=base_hittingset_with_callback(MG,integral=false) + g_gap=mincut/treepacking A=g.incidence_matrix() n,m = A.dimensions() # enumerate all vectors in F_2^n with even number of 1s + m_gap=0 V = VectorSpace(GF(2), N) for v in filter(lambda v: v.hamming_weight() % 2 == 0 and v!=0, V): cnt=cnt+1 @@ -44,17 +70,27 @@ for N in range(4,10): # print(A_t) M=Matroid(matrix=A_t,field=GF(2))/m #contract the last element # print(M) - bases=M.bases() - strength=cogirthip(bases,integral=false) - cogirth =cogirthip(bases,integral=true) + cogirth = base_hittingset_with_callback(M,integral=true) + strength = base_hittingset_with_callback(M,integral=false) gap = cogirth/strength + m_gap=max(m_gap,gap) # maxgap=max(gap,maxgap) - if gap > maxgap: - maxgap = gap - print(f"find a large gap: {gap}") - with open("projection.out", "a") as file: - file.write("##################################\n" - +str(gap)+"\n"+str(A_t) - +"\n##################################\n") - if cnt%100==0: - print(f"#{cnt}, n={n}, max gap = {maxgap}") \ No newline at end of file + # if gap > maxgap: + # maxgap = gap + # print(f"find a large gap: {gap}") + # with open("projection.out", "a") as file: + # file.write("##################################\n" + # +str(gap)+"\n"+str(A_t) + # +"\n##################################\n") + factor=m_gap/g_gap + fr = lambda xx: str(Fraction(xx).limit_denominator(m)) + maxfactor=max(maxfactor,factor) + print(f"n={n:<2}, m={m:<4} graph gap = {fr(g_gap):8} projection gap = {fr(m_gap):8} factor={fr(factor):8} max={maxfactor}") + if factor >= 2: + with open("projection.out", "a") as file: + file.write(f"n={n:<2}, m={m:<4} graph gap = {fr(g_gap):8} projection gap = {fr(m_gap):8} factor={fr(factor):8} max={maxfactor}\n" + +str(A_t)+"\n") + if factor > 2: + print("conjecture is wrong") + print(str(A_t)) + exit(1) \ No newline at end of file