From 9f0d4bbcc68e51909c30269bb18fcb4e74b7bfca Mon Sep 17 00:00:00 2001 From: mbeisel Date: Tue, 6 Feb 2024 16:02:06 +0100 Subject: [PATCH 1/5] add initial template for shor for discrete log --- .../objectiveFns/objective_controller.py | 30 ++++++++++++++++ app/model/objective_request.py | 35 +++++++++++++++++++ app/services/objective_service.py | 21 +++++++++++ 3 files changed, 86 insertions(+) diff --git a/app/controller/objectiveFns/objective_controller.py b/app/controller/objectiveFns/objective_controller.py index bf69309..695d761 100644 --- a/app/controller/objectiveFns/objective_controller.py +++ b/app/controller/objectiveFns/objective_controller.py @@ -9,6 +9,8 @@ MaxCutObjectiveEvaluationRequestSchema, KnapsackObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequestSchema, + ShorDiscreteLogObjectiveEvaluationRequestSchema, + ShorDiscreteLogObjectiveEvaluationRequest ) blp = Blueprint( @@ -98,3 +100,31 @@ def max_cut(json: KnapsackObjectiveEvaluationRequest): return objective_service.generate_knapsack_objective_response( KnapsackObjectiveEvaluationRequest(**json) ) + + +@blp.route("/shor/discreteLog", methods=["POST"]) +@blp.arguments( + ShorDiscreteLogObjectiveEvaluationRequestSchema, + example={ + "b": 2, + "g": 5, + "p": 7, + "n": -1, + "counts": { + "10000110000101": 10, + "01100110000110": 20, + "10000011000000": 30, + "01001100000110": 40, + "11000011000000": 50, + }, + "objFun": "Expectation", + "visualization": "False", + }, +) +@blp.response(200, ObjectiveResponseSchema) +def max_cut(json: ShorDiscreteLogObjectiveEvaluationRequest): + print(json) + if json: + return objective_service.generate_knapsack_objective_response( + ShorDiscreteLogObjectiveEvaluationRequest(**json) + ) diff --git a/app/model/objective_request.py b/app/model/objective_request.py index 2e78ba9..c7c86bf 100644 --- a/app/model/objective_request.py +++ b/app/model/objective_request.py @@ -72,3 +72,38 @@ class KnapsackObjectiveEvaluationRequestSchema(ma.Schema): objFun = ma.fields.Str(required=True) visualization = ma.fields.Boolean(required=False) objFun_hyperparameters = ma.fields.Dict(keys=ma.fields.Str(), required=False) + + +class ShorDiscreteLogObjectiveEvaluationRequest(ObjectiveEvaluationRequest): + def __init__( + self, + b, + g, + p, + counts, + objFun, + r=-1, + n=-1, + objFun_hyperparameters={}, + visualization=False, + ): + super().__init__(counts, objFun, objFun_hyperparameters, visualization) + self.b = b + self.g = g + self.p = p + self.n = n + self.r = r + + +class ShorDiscreteLogObjectiveEvaluationRequestSchema(ma.Schema): + b = ma.fields.Integer(required=True) + g = ma.fields.Integer(required=True) + p = ma.fields.Integer(required=True) + n = ma.fields.Integer(required=False) + r = ma.fields.Integer(required=False) + counts = ma.fields.Dict( + keys=ma.fields.Str(), values=ma.fields.Float(), required=True + ) + objFun = ma.fields.Str(required=True) + visualization = ma.fields.Boolean(required=False) + objFun_hyperparameters = ma.fields.Dict(keys=ma.fields.Str(), required=False) diff --git a/app/services/objective_service.py b/app/services/objective_service.py index 0f26614..1863089 100644 --- a/app/services/objective_service.py +++ b/app/services/objective_service.py @@ -3,6 +3,7 @@ TSPObjectiveEvaluationRequest, MaxCutObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequest, + ShorDiscreteLogObjectiveEvaluationRequest ) from app.model.objective_response import ObjectiveResponse from app.services.objectiveFunctions import F_CVaR, F_EE, F_Gibbs @@ -89,6 +90,26 @@ def generate_knapsack_objective_response(input: KnapsackObjectiveEvaluationReque return ObjectiveResponse(objective_value, [costs], None) + +def generate_shor_discrete_log_objective_response(input: ShorDiscreteLogObjectiveEvaluationRequest): + objective_function = getObjectiveFunction( + input.objFun, MAX_CUT, **input.objFun_hyperparameters + ) + objective_value = objective_function.evaluate(input.counts, input.adj_matrix) + cost_dict = convert_cost_object_to_dict(objective_function.counts_cost) + + graphic = ( + MaxCutVisualization().visualize( + counts=objective_function.counts_cost, problem_instance=input.adj_matrix + ) + if input.visualization + else None + ) + + print("value", objective_value) + return ObjectiveResponse(objective_value, cost_dict, graphic) + + def getObjectiveFunction(objFun, costFun, **kwargs): if EXPECTATION in objFun.lower(): return F_EE(costFun) From 0bb3bee8fdb7aad5a994110c314b85d93c73b4da Mon Sep 17 00:00:00 2001 From: mbeisel Date: Tue, 6 Feb 2024 16:12:23 +0100 Subject: [PATCH 2/5] add test for knapsack --- tests/test_costFunctions.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_costFunctions.py b/tests/test_costFunctions.py index cb26bbb..ee2c79e 100644 --- a/tests/test_costFunctions.py +++ b/tests/test_costFunctions.py @@ -195,3 +195,39 @@ def test_max_cut(self): ) self.assertEqual(response.status_code, 200) print(response.get_json()) + + def test_knapsack(self): + response = self.client.post( + "/objective/knapsack", + data=json.dumps( + { + "items": [ + { + "value": 5, + "weight": 2 + }, + { + "value": 2, + "weight": 1 + }, + { + "value": 3, + "weight": 2 + } + ], + "max_weights": 20, + "counts": { + "100000": 30, + "100001": 10, + "110000": 50, + "011110": 20, + "010110": 40 + }, + "objFun": "Expectation", + "visualization": "True" + } + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, 200) + print(response.get_json()) \ No newline at end of file From 34cd2b752e31e9e1c83d3e4807619404cb7f8922 Mon Sep 17 00:00:00 2001 From: LaviniaStiliadou Date: Tue, 6 Feb 2024 18:10:27 +0100 Subject: [PATCH 3/5] add shor implementation --- .../objectiveFns/objective_controller.py | 39 +++++++--- app/helperfunctions.py | 26 +++++++ app/services/objective_service.py | 75 ++++++++++++++----- tests/test_costFunctions.py | 21 ++---- 4 files changed, 120 insertions(+), 41 deletions(-) diff --git a/app/controller/objectiveFns/objective_controller.py b/app/controller/objectiveFns/objective_controller.py index 695d761..b3da12e 100644 --- a/app/controller/objectiveFns/objective_controller.py +++ b/app/controller/objectiveFns/objective_controller.py @@ -10,7 +10,7 @@ KnapsackObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequestSchema, ShorDiscreteLogObjectiveEvaluationRequestSchema, - ShorDiscreteLogObjectiveEvaluationRequest + ShorDiscreteLogObjectiveEvaluationRequest, ) blp = Blueprint( @@ -109,22 +109,43 @@ def max_cut(json: KnapsackObjectiveEvaluationRequest): "b": 2, "g": 5, "p": 7, - "n": -1, + "n": 3, "counts": { - "10000110000101": 10, - "01100110000110": 20, - "10000011000000": 30, - "01001100000110": 40, - "11000011000000": 50, + "101 101": 4, + "000 100": 17, + "011 111": 15, + "101 001": 13, + "000 000": 12, + "110 010": 1, + "101 110": 3, + "000 101": 1, + "010 011": 2, + "011 011": 5, + "011 110": 3, + "011 010": 2, + "110 001": 2, + "010 111": 3, + "010 110": 1, + "011 100": 3, + "001 011": 2, + "110 101": 2, + "100 111": 2, + "011 101": 1, + "110 000": 1, + "011 101": 1, + "110 000": 1, + "011 001": 1, + "011 000": 1, + "101 010": 1, }, "objFun": "Expectation", "visualization": "False", }, ) @blp.response(200, ObjectiveResponseSchema) -def max_cut(json: ShorDiscreteLogObjectiveEvaluationRequest): +def shor_discrete_log(json: ShorDiscreteLogObjectiveEvaluationRequest): print(json) if json: - return objective_service.generate_knapsack_objective_response( + return objective_service.generate_shor_discrete_log_objective_response( ShorDiscreteLogObjectiveEvaluationRequest(**json) ) diff --git a/app/helperfunctions.py b/app/helperfunctions.py index e96406d..0b9b2fc 100644 --- a/app/helperfunctions.py +++ b/app/helperfunctions.py @@ -1,3 +1,6 @@ +from fractions import Fraction + + def take_first(elem): return elem[0] @@ -39,3 +42,26 @@ def convert_cost_object_to_dict(cost_object): for tup in sorted_costs ] return sorted_costs_dict + + +def find_period(result_list: [(int, int, int)], n: int, p: int, g: int) -> int: + """The order of the generator is not given, it has to be determined. + The list of results for the whole circuit will be used, since the + measurement of stage1 allows for phase estimation of the operator + (similar to Shor's algorithm for prime factorization).""" + smallest_fitting_denominator = ( + p + 1 + ) # init to p+1 (sth larger than the real result) + for (y1, _, _) in result_list: + meas_div = y1 / (2**n) + frac_meas = Fraction(meas_div).limit_denominator(p - 1) + + # check if denominator fits + r_candidate = frac_meas.denominator + if g**r_candidate % p == 1: + # fits + if r_candidate < smallest_fitting_denominator: + smallest_fitting_denominator = r_candidate + + print("Found r=", smallest_fitting_denominator) + return smallest_fitting_denominator diff --git a/app/services/objective_service.py b/app/services/objective_service.py index 1863089..5ea67da 100644 --- a/app/services/objective_service.py +++ b/app/services/objective_service.py @@ -1,15 +1,17 @@ -from app.helperfunctions import take_second, convert_cost_object_to_dict +from app.helperfunctions import take_second, convert_cost_object_to_dict, find_period from app.model.objective_request import ( TSPObjectiveEvaluationRequest, MaxCutObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequest, - ShorDiscreteLogObjectiveEvaluationRequest + ShorDiscreteLogObjectiveEvaluationRequest, ) from app.model.objective_response import ObjectiveResponse from app.services.objectiveFunctions import F_CVaR, F_EE, F_Gibbs from app.services.visualization import TspVisualization, MaxCutVisualization from app.constants import * from qiskit_optimization.applications import Knapsack +import math +import numpy as np def generate_tsp_objective_response(input: TSPObjectiveEvaluationRequest): @@ -90,24 +92,63 @@ def generate_knapsack_objective_response(input: KnapsackObjectiveEvaluationReque return ObjectiveResponse(objective_value, [costs], None) +def generate_shor_discrete_log_objective_response( + input: ShorDiscreteLogObjectiveEvaluationRequest, +): + # objective_function = getObjectiveFunction( + # input.objFun, MAX_CUT, **input.objFun_hyperparameters + # ) + # objective_value = objective_function.evaluate(input.counts, input.adj_matrix) + # cost_dict = convert_cost_object_to_dict(objective_function.counts_cost) + if input.n == -1: + n = math.ceil(math.log(input.p, 2)) + else: + n = input.n + res = list() + print("executed circuit") + for result in input.counts.keys(): + # split result measurements + result_s = result.split(" ") + m_stage1 = int(result_s[1], 2) + m_stage2 = int(result_s[0], 2) -def generate_shor_discrete_log_objective_response(input: ShorDiscreteLogObjectiveEvaluationRequest): - objective_function = getObjectiveFunction( - input.objFun, MAX_CUT, **input.objFun_hyperparameters - ) - objective_value = objective_function.evaluate(input.counts, input.adj_matrix) - cost_dict = convert_cost_object_to_dict(objective_function.counts_cost) + res.append((m_stage1, m_stage2, input.counts[result])) - graphic = ( - MaxCutVisualization().visualize( - counts=objective_function.counts_cost, problem_instance=input.adj_matrix - ) - if input.visualization - else None - ) + num_fail = 0 + num_success = 0 - print("value", objective_value) - return ObjectiveResponse(objective_value, cost_dict, graphic) + correct_m = -1 + + for (y1, y2, freq) in res: + + # period has to be calculated + r = find_period(res, n, input.p, input.g) + + # k is inferred from the measurement result from first stage + k = int(round((y1 * r) / (2**n), 0)) + print("k = ", k) + + # m (the discrete log) is calculated by using the congruence: + # ((km mod r)/r)*2^n = m_stage2 + # v = m_stage2*r/2^n + v = (y2 * r) / (2**n) + # print("v=", v) # = km mod r + + # k inverse exists? + if np.gcd(k, r) == 1: + kinv = pow(k, -1, r) + # print("kInv=", kinv) + m = int(round(v * kinv % r, 0)) + # print("found m=", m) + + # check if this m actually fits + if (input.g**m % input.p) == input.b: + correct_m = m + + graphic = None + + print("calculated m ", correct_m) + return ObjectiveResponse(correct_m, {}, graphic) def getObjectiveFunction(objFun, costFun, **kwargs): diff --git a/tests/test_costFunctions.py b/tests/test_costFunctions.py index ee2c79e..02f0280 100644 --- a/tests/test_costFunctions.py +++ b/tests/test_costFunctions.py @@ -202,18 +202,9 @@ def test_knapsack(self): data=json.dumps( { "items": [ - { - "value": 5, - "weight": 2 - }, - { - "value": 2, - "weight": 1 - }, - { - "value": 3, - "weight": 2 - } + {"value": 5, "weight": 2}, + {"value": 2, "weight": 1}, + {"value": 3, "weight": 2}, ], "max_weights": 20, "counts": { @@ -221,13 +212,13 @@ def test_knapsack(self): "100001": 10, "110000": 50, "011110": 20, - "010110": 40 + "010110": 40, }, "objFun": "Expectation", - "visualization": "True" + "visualization": "True", } ), content_type="application/json", ) self.assertEqual(response.status_code, 200) - print(response.get_json()) \ No newline at end of file + print(response.get_json()) From 648dbcaa024396c9d031e26659d655701514f489 Mon Sep 17 00:00:00 2001 From: mbeisel Date: Thu, 21 Mar 2024 13:00:07 +0100 Subject: [PATCH 4/5] reformating --- .../objectiveFns/objective_controller.py | 2 +- app/services/objective_service.py | 7 ++++--- tests/test_costFunctions.py | 21 ++++++------------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/app/controller/objectiveFns/objective_controller.py b/app/controller/objectiveFns/objective_controller.py index 695d761..8c91912 100644 --- a/app/controller/objectiveFns/objective_controller.py +++ b/app/controller/objectiveFns/objective_controller.py @@ -10,7 +10,7 @@ KnapsackObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequestSchema, ShorDiscreteLogObjectiveEvaluationRequestSchema, - ShorDiscreteLogObjectiveEvaluationRequest + ShorDiscreteLogObjectiveEvaluationRequest, ) blp = Blueprint( diff --git a/app/services/objective_service.py b/app/services/objective_service.py index 1863089..7caeee0 100644 --- a/app/services/objective_service.py +++ b/app/services/objective_service.py @@ -3,7 +3,7 @@ TSPObjectiveEvaluationRequest, MaxCutObjectiveEvaluationRequest, KnapsackObjectiveEvaluationRequest, - ShorDiscreteLogObjectiveEvaluationRequest + ShorDiscreteLogObjectiveEvaluationRequest, ) from app.model.objective_response import ObjectiveResponse from app.services.objectiveFunctions import F_CVaR, F_EE, F_Gibbs @@ -90,8 +90,9 @@ def generate_knapsack_objective_response(input: KnapsackObjectiveEvaluationReque return ObjectiveResponse(objective_value, [costs], None) - -def generate_shor_discrete_log_objective_response(input: ShorDiscreteLogObjectiveEvaluationRequest): +def generate_shor_discrete_log_objective_response( + input: ShorDiscreteLogObjectiveEvaluationRequest, +): objective_function = getObjectiveFunction( input.objFun, MAX_CUT, **input.objFun_hyperparameters ) diff --git a/tests/test_costFunctions.py b/tests/test_costFunctions.py index ee2c79e..02f0280 100644 --- a/tests/test_costFunctions.py +++ b/tests/test_costFunctions.py @@ -202,18 +202,9 @@ def test_knapsack(self): data=json.dumps( { "items": [ - { - "value": 5, - "weight": 2 - }, - { - "value": 2, - "weight": 1 - }, - { - "value": 3, - "weight": 2 - } + {"value": 5, "weight": 2}, + {"value": 2, "weight": 1}, + {"value": 3, "weight": 2}, ], "max_weights": 20, "counts": { @@ -221,13 +212,13 @@ def test_knapsack(self): "100001": 10, "110000": 50, "011110": 20, - "010110": 40 + "010110": 40, }, "objFun": "Expectation", - "visualization": "True" + "visualization": "True", } ), content_type="application/json", ) self.assertEqual(response.status_code, 200) - print(response.get_json()) \ No newline at end of file + print(response.get_json()) From 2513c935dda6ff1a65c62c2993b1cd5c34b4cd2c Mon Sep 17 00:00:00 2001 From: mbeisel Date: Thu, 21 Mar 2024 13:02:26 +0100 Subject: [PATCH 5/5] reformating --- app/helperfunctions.py | 4 ++-- app/services/objective_service.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helperfunctions.py b/app/helperfunctions.py index 0b9b2fc..a6edeec 100644 --- a/app/helperfunctions.py +++ b/app/helperfunctions.py @@ -53,12 +53,12 @@ def find_period(result_list: [(int, int, int)], n: int, p: int, g: int) -> int: p + 1 ) # init to p+1 (sth larger than the real result) for (y1, _, _) in result_list: - meas_div = y1 / (2**n) + meas_div = y1 / (2 ** n) frac_meas = Fraction(meas_div).limit_denominator(p - 1) # check if denominator fits r_candidate = frac_meas.denominator - if g**r_candidate % p == 1: + if g ** r_candidate % p == 1: # fits if r_candidate < smallest_fitting_denominator: smallest_fitting_denominator = r_candidate diff --git a/app/services/objective_service.py b/app/services/objective_service.py index 1fc8431..6086989 100644 --- a/app/services/objective_service.py +++ b/app/services/objective_service.py @@ -120,13 +120,13 @@ def generate_shor_discrete_log_objective_response( r = find_period(res, n, input.p, input.g) # k is inferred from the measurement result from first stage - k = int(round((y1 * r) / (2**n), 0)) + k = int(round((y1 * r) / (2 ** n), 0)) print("k = ", k) # m (the discrete log) is calculated by using the congruence: # ((km mod r)/r)*2^n = m_stage2 # v = m_stage2*r/2^n - v = (y2 * r) / (2**n) + v = (y2 * r) / (2 ** n) # print("v=", v) # = km mod r # k inverse exists? @@ -137,7 +137,7 @@ def generate_shor_discrete_log_objective_response( # print("found m=", m) # check if this m actually fits - if (input.g**m % input.p) == input.b: + if (input.g ** m % input.p) == input.b: correct_m = m graphic = None