From 71f915851d981b1a07e4b67828fbd75d3af868fb Mon Sep 17 00:00:00 2001 From: Jason Yik Date: Fri, 16 Aug 2024 12:27:34 -0400 Subject: [PATCH 1/3] init gurobi solver --- QUBO/costs.json | 1 + QUBO/gurobi.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 QUBO/costs.json create mode 100644 QUBO/gurobi.py diff --git a/QUBO/costs.json b/QUBO/costs.json new file mode 100644 index 0000000..a3fd125 --- /dev/null +++ b/QUBO/costs.json @@ -0,0 +1 @@ +{"10_0.01_0": -10, "10_0.01_1": -10, "10_0.01_2": -10, "10_0.01_3": -10, "10_0.01_4": -10, "10_0.05_0": -8, "10_0.05_1": -8, "10_0.05_2": -9, "10_0.05_3": -8, "10_0.05_4": -8, "10_0.1_0": -7, "10_0.1_1": -7, "10_0.1_2": -7, "10_0.1_3": -8, "10_0.1_4": -7, "10_0.25_0": -6, "10_0.25_1": -6, "10_0.25_2": -5, "10_0.25_3": -6, "10_0.25_4": -5, "25_0.01_0": -22, "25_0.01_1": -22, "25_0.01_2": -23, "25_0.01_3": -22, "25_0.01_4": -22, "25_0.05_0": -15, "25_0.05_1": -18, "25_0.05_2": -17, "25_0.05_3": -17, "25_0.05_4": -17, "25_0.1_0": -12, "25_0.1_1": -15, "25_0.1_2": -14, "25_0.1_3": -13, "25_0.1_4": -13, "25_0.25_0": -9, "25_0.25_1": -9, "25_0.25_2": -8, "25_0.25_3": -10, "25_0.25_4": -8, "50_0.01_0": -40, "50_0.01_1": -42, "50_0.01_2": -41, "50_0.01_3": -43, "50_0.01_4": -40, "50_0.05_0": -28, "50_0.05_1": -27, "50_0.05_2": -27, "50_0.05_3": -27, "50_0.05_4": -26, "50_0.1_0": -20, "50_0.1_1": -21, "50_0.1_2": -19, "50_0.1_3": -21, "50_0.1_4": -22, "50_0.25_0": -12, "50_0.25_1": -13, "50_0.25_2": -12, "50_0.25_3": -12, "50_0.25_4": -13, "100_0.01_0": -73, "100_0.01_1": -71, "100_0.01_2": -74, "100_0.01_3": -73, "100_0.01_4": -71, "100_0.05_0": -44, "100_0.05_1": -42, "100_0.05_2": -45, "100_0.05_3": -45, "100_0.05_4": -42, "100_0.1_0": -30, "100_0.1_1": -29, "100_0.1_2": -30, "100_0.1_3": -31, "100_0.1_4": -30, "100_0.25_0": -17, "100_0.25_1": -17, "100_0.25_2": -16, "100_0.25_3": -16, "100_0.25_4": -18, "250_0.01_0": -142, "250_0.01_1": -140, "250_0.01_2": -137, "250_0.01_3": -138, "250_0.01_4": -143, "250_0.05_0": -69, "250_0.05_1": -62, "250_0.05_2": -59, "250_0.05_3": -61, "250_0.05_4": -58, "250_0.1_0": -37, "250_0.1_1": -37, "250_0.1_2": -42, "250_0.1_3": -37, "250_0.1_4": -40, "250_0.25_0": -20, "250_0.25_1": -19, "250_0.25_2": -20, "250_0.25_3": -18, "250_0.25_4": -19, "500_0.01_0": -196, "500_0.01_1": -190, "500_0.01_2": -185, "500_0.01_3": -188, "500_0.01_4": -188, "500_0.05_0": -73, "500_0.05_1": -71, "500_0.05_2": -72, "500_0.05_3": -74, "500_0.05_4": -73, "500_0.1_0": -45, "500_0.1_1": -46, "500_0.1_2": -44, "500_0.1_3": -46, "500_0.1_4": -45, "500_0.25_0": -22, "500_0.25_1": -23, "500_0.25_2": -23, "500_0.25_3": -21, "500_0.25_4": -22} \ No newline at end of file diff --git a/QUBO/gurobi.py b/QUBO/gurobi.py new file mode 100644 index 0000000..f59b845 --- /dev/null +++ b/QUBO/gurobi.py @@ -0,0 +1,110 @@ +from qubo_mis_generator import MISBenchmark +import csv +import json +import numpy as np + +from gurobipy import * + + +def check_mis_np(Q, sample): + # check for independent set in order to ensure cost is minimized + + assert len(sample) == qubo_matrix.shape[0], "Sample size does not match QUBO size" + + # get the nodes that are part of the MIS + mis_nodes = [idx for idx in range(len(sample)) if sample[idx] == 1] + + # check if the MIS is valid + for i in range(len(mis_nodes)): + for j in range(i + 1, len(mis_nodes)): + # check if dict entry exists + if qubo_matrix[mis_nodes[i], mis_nodes[j]] != 0: + return False + + return True + +def check_mis_bqm(bqm, sample): + # check for independent set in order to ensure cost is minimized + + assert len(sample) == bqm.num_variables, "Sample size does not match BQM size" + + # get the nodes that are part of the MIS + mis_nodes = [node for node in sample if sample[node] == 1] + + # check if the MIS is valid + for i in range(len(mis_nodes)): + for j in range(i + 1, len(mis_nodes)): + # check if dict entry exists + if bqm.adj.get(mis_nodes[i]).get(mis_nodes[j]) is not None: + return False + + return True + +def sample_to_binary_list(sample): + N = len(sample) + mis_nodes = [node for node in sample if sample[node] == 1] + l = [0 if i not in mis_nodes else 1 for i in range(N)] + return l + +if __name__ == '__main__': + benchmark = MISBenchmark('default_config.csv') + dataset_dir = './data/qubo_mis_dataset' + + # look for temp costs file + try: + with open('costs.json', 'r') as f: + costs = json.load(f) + except FileNotFoundError: + costs = {} + + for qubo_matrix, num_vertices, density, seed, _ in benchmark.load_problems(dataset_dir): + print("Problem: ", num_vertices, density, seed) + key = f'{num_vertices}_{density}_{seed}' + + if key in costs: + continue + + if num_vertices < 1000: + num_nodes = qubo_matrix.shape[0] + quadratic_model = Model() + + x = quadratic_model.addMVar(num_nodes, vtype=GRB.BINARY, name='x') + quadratic_model.setObjective(x @ qubo_matrix @ x) + + quadratic_model.optimize() + + c_optimal = quadratic_model.objVal + sample = x.X + + # sanity check + assert check_mis_np(qubo_matrix, sample), "Gurobi did not find a valid MIS" + + costs[key] = int(c_optimal) + + # else: + # bqm = BinaryQuadraticModel(qubo_matrix, "BINARY") + + # sampler = TabuSampler() + # sampleset = sampler.sample(bqm, timeout=None, num_reads=100, num_restarts=50) + + # # solver sanity check + # # while not guaranteed, it is highly likely the solver finds independent sets + # assert check_mis_bqm(bqm, sampleset.first.sample), "Solver did not find a valid MIS" + + # print("found min cost", int(sampleset.first.energy), "for sample", sample_to_binary_list(sampleset.first.sample), flush=True) + + # costs[key] = int(sampleset.first.energy) + + # temporary save costs dict as json + with open('costs.json', 'w') as f: + json.dump(costs, f) + + # rewrite csv + header = ['num_vertices', 'density', 'random_seed', 'c_optimal'] + with open('gurobi_default_config.csv', 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(header) + for key, value in costs.items(): + num_vertices, density, random_seed = key.split('_') + c_optimal = value + writer.writerow([num_vertices, density, random_seed, c_optimal]) From 0d07cb4cc79c704f598acc21cb8d7c47f4e0856f Mon Sep 17 00:00:00 2001 From: Jason Yik Date: Tue, 20 Aug 2024 12:10:19 -0400 Subject: [PATCH 2/3] complete solutions for 250_0.05 --- QUBO/default_config.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QUBO/default_config.csv b/QUBO/default_config.csv index d5b6844..a865360 100644 --- a/QUBO/default_config.csv +++ b/QUBO/default_config.csv @@ -84,11 +84,11 @@ num_vertices,density,random_seed,c_optimal 250,0.01,2,-137 250,0.01,3,-138 250,0.01,4,-143 -250,0.05,0,-67 -250,0.05,1,-69 +250,0.05,0,-69 +250,0.05,1,-70 250,0.05,2,-67 -250,0.05,3,-67 -250,0.05,4,-67 +250,0.05,3,-70 +250,0.05,4,-68 250,0.1,0,-43 250,0.1,1,-44 250,0.1,2,-44 From 9de085e519bb7a5282fb90acc8aae17ca78c4eb9 Mon Sep 17 00:00:00 2001 From: Jason Yik Date: Tue, 20 Aug 2024 12:11:43 -0400 Subject: [PATCH 3/3] rm costs json --- QUBO/costs.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 QUBO/costs.json diff --git a/QUBO/costs.json b/QUBO/costs.json deleted file mode 100644 index a3fd125..0000000 --- a/QUBO/costs.json +++ /dev/null @@ -1 +0,0 @@ -{"10_0.01_0": -10, "10_0.01_1": -10, "10_0.01_2": -10, "10_0.01_3": -10, "10_0.01_4": -10, "10_0.05_0": -8, "10_0.05_1": -8, "10_0.05_2": -9, "10_0.05_3": -8, "10_0.05_4": -8, "10_0.1_0": -7, "10_0.1_1": -7, "10_0.1_2": -7, "10_0.1_3": -8, "10_0.1_4": -7, "10_0.25_0": -6, "10_0.25_1": -6, "10_0.25_2": -5, "10_0.25_3": -6, "10_0.25_4": -5, "25_0.01_0": -22, "25_0.01_1": -22, "25_0.01_2": -23, "25_0.01_3": -22, "25_0.01_4": -22, "25_0.05_0": -15, "25_0.05_1": -18, "25_0.05_2": -17, "25_0.05_3": -17, "25_0.05_4": -17, "25_0.1_0": -12, "25_0.1_1": -15, "25_0.1_2": -14, "25_0.1_3": -13, "25_0.1_4": -13, "25_0.25_0": -9, "25_0.25_1": -9, "25_0.25_2": -8, "25_0.25_3": -10, "25_0.25_4": -8, "50_0.01_0": -40, "50_0.01_1": -42, "50_0.01_2": -41, "50_0.01_3": -43, "50_0.01_4": -40, "50_0.05_0": -28, "50_0.05_1": -27, "50_0.05_2": -27, "50_0.05_3": -27, "50_0.05_4": -26, "50_0.1_0": -20, "50_0.1_1": -21, "50_0.1_2": -19, "50_0.1_3": -21, "50_0.1_4": -22, "50_0.25_0": -12, "50_0.25_1": -13, "50_0.25_2": -12, "50_0.25_3": -12, "50_0.25_4": -13, "100_0.01_0": -73, "100_0.01_1": -71, "100_0.01_2": -74, "100_0.01_3": -73, "100_0.01_4": -71, "100_0.05_0": -44, "100_0.05_1": -42, "100_0.05_2": -45, "100_0.05_3": -45, "100_0.05_4": -42, "100_0.1_0": -30, "100_0.1_1": -29, "100_0.1_2": -30, "100_0.1_3": -31, "100_0.1_4": -30, "100_0.25_0": -17, "100_0.25_1": -17, "100_0.25_2": -16, "100_0.25_3": -16, "100_0.25_4": -18, "250_0.01_0": -142, "250_0.01_1": -140, "250_0.01_2": -137, "250_0.01_3": -138, "250_0.01_4": -143, "250_0.05_0": -69, "250_0.05_1": -62, "250_0.05_2": -59, "250_0.05_3": -61, "250_0.05_4": -58, "250_0.1_0": -37, "250_0.1_1": -37, "250_0.1_2": -42, "250_0.1_3": -37, "250_0.1_4": -40, "250_0.25_0": -20, "250_0.25_1": -19, "250_0.25_2": -20, "250_0.25_3": -18, "250_0.25_4": -19, "500_0.01_0": -196, "500_0.01_1": -190, "500_0.01_2": -185, "500_0.01_3": -188, "500_0.01_4": -188, "500_0.05_0": -73, "500_0.05_1": -71, "500_0.05_2": -72, "500_0.05_3": -74, "500_0.05_4": -73, "500_0.1_0": -45, "500_0.1_1": -46, "500_0.1_2": -44, "500_0.1_3": -46, "500_0.1_4": -45, "500_0.25_0": -22, "500_0.25_1": -23, "500_0.25_2": -23, "500_0.25_3": -21, "500_0.25_4": -22} \ No newline at end of file