import streamlit as st import graphviz import random import gurobipy as gp from gurobipy import GRB import colorsys def hsv2rgb(h, s, v): r, g, b = colorsys.hsv_to_rgb(h, s, v) return int(255 * r), int(255 * g), int(255 * b) def format_color(color): return '#%02x%02x%02x' % color def generate_colors(n): colors = [hsv2rgb(i / n, 0.5, 1.0) for i in range(n)] return [format_color(color) for color in colors] def generate_random_graph(V, density): E = [(u, v) for u in range(V - 1) for v in range(u + 1, V)] random.shuffle(E) E = E[:int(density * V * (V - 1) / 2)] return V, E def solve_matching(V, E): m = gp.Model() x = m.addVars(E, vtype=GRB.BINARY) m.setObjective(x.sum(), GRB.MAXIMIZE) m.addConstrs(x.sum(u, '*') + x.sum('*', u) <= 1 for u in range(V)) m.optimize() return [e for e in E if x[e].x > 0.5] def app_matching(V, E): M = set(solve_matching(V, E)) if len(M) == V // 2: st.success('Perfect matching found') else: st.metric('Matching size', len(M)) G = graphviz.Graph() for u, v in E: if (u, v) in M: G.edge(str(u), str(v), color='red') else: G.edge(str(u), str(v), color='gray', style='dashed') st.graphviz_chart(G) def solve_hamilton(V, E): m = gp.Model() x = m.addVars(range(V), range(V), vtype=GRB.BINARY) m.addConstrs(x.sum(u, '*') == 1 for u in range(V)) m.addConstrs(x.sum('*', i) == 1 for i in range(V)) for u in range(V): for v in range(V): if (u, v) not in E and (v, u) not in E: for i in range(V - 1): m.addConstr(x[u, i] + x[v, i + 1] <= 1) m.addConstr(x[u, V - 1] + x[v, 0] <= 1) m.optimize() if m.status != GRB.OPTIMAL: return None cycle = [] for i in range(V): for u in range(V): if x[u, i].x > 0.5: cycle.append(u) break return cycle def app_hamilton(V, E): cycle = solve_hamilton(V, E) if cycle is None: st.error('Hamilton cycle not found') else: st.success('Hamilton cycle found') G = graphviz.Graph() if cycle is not None: for u in range(V): G.node(str(cycle.index(u))) for u, v in E: if ((cycle.index(u) + 1) % len(cycle)) == cycle.index(v): G.edge(str(cycle.index(u)), str(cycle.index(v)), dir='forward') elif ((cycle.index(u) - 1) % len(cycle)) == cycle.index(v): G.edge(str(cycle.index(u)), str(cycle.index(v)), dir='back') else: G.edge(str(cycle.index(u)), str(cycle.index(v)), style='dashed', color='gray') else: for u in range(V): G.node(str(u)) for u, v in E: G.edge(str(u), str(v)) st.graphviz_chart(G) def solve_vertex_cover(V, E): m = gp.Model() x = m.addVars(range(V), vtype=GRB.BINARY) m.setObjective(x.sum(), GRB.MINIMIZE) m.addConstrs(x[u] + x[v] >= 1 for u, v in E) m.optimize() return [u for u in range(V) if x[u].x > 0.5] def app_vertex_cover(V, E): cover = solve_vertex_cover(V, E) st.metric('Vertex cover size', len(cover)) G = graphviz.Graph() for u in range(V): if u in cover: G.node(str(u), style='filled', fillcolor='lightblue') else: G.node(str(u), color='gray') for u, v in E: G.edge(str(u), str(v)) st.graphviz_chart(G) def solve_coloring(V, E): m = gp.Model() vertex_color = m.addVars(range(V), vtype=GRB.INTEGER, lb=0) or_helper = m.addVars(E, vtype=GRB.BINARY) chi = m.addVar(vtype=GRB.INTEGER) m.setObjective(chi, GRB.MINIMIZE) m.addConstrs(vertex_color[u] <= chi for u in range(V)) m.addConstrs((or_helper[u, v] == 0) >> (vertex_color[u] - vertex_color[v] >= 1) for u, v in E) m.addConstrs((or_helper[u, v] == 1) >> (vertex_color[v] - vertex_color[u] >= 1) for u, v in E) m.optimize() return [round(vertex_color[u].x) for u in range(V)] def app_coloring(V, E): coloring = solve_coloring(V, E) st.metric('Chromatic number', max(coloring) + 1) colors = generate_colors(max(coloring) + 1) G = graphviz.Graph() for u in range(V): G.node(str(u), style='filled', fillcolor=colors[coloring[u]]) for u, v in E: G.edge(str(u), str(v)) st.graphviz_chart(G) def main(): V = st.number_input('Number of vertices', min_value=1, value=10) density = st.slider('Density', min_value=0.0, max_value=1.0, value=0.5) st.button('Generate') V, E = generate_random_graph(V, density) if len(E) > 40: st.warning('Too many edges to display') return apps = [ app_matching, app_hamilton, app_vertex_cover, app_coloring, ] tabs = st.tabs([ 'Matching', 'Hamilton', 'Vertex cover', 'Coloring', ]) for t, a in zip(tabs, apps): with t: a(V, E) if __name__ == '__main__': main()