Skip to content

Commit

Permalink
Merge pull request #3 from UST-QuAntiL/feature/shorDiscreteLog
Browse files Browse the repository at this point in the history
add initial template for shor for discrete log
  • Loading branch information
mbeisel authored Mar 21, 2024
2 parents 97a5f5f + 2513c93 commit 7fd825a
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 1 deletion.
51 changes: 51 additions & 0 deletions app/controller/objectiveFns/objective_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
MaxCutObjectiveEvaluationRequestSchema,
KnapsackObjectiveEvaluationRequest,
KnapsackObjectiveEvaluationRequestSchema,
ShorDiscreteLogObjectiveEvaluationRequestSchema,
ShorDiscreteLogObjectiveEvaluationRequest,
)

blp = Blueprint(
Expand Down Expand Up @@ -98,3 +100,52 @@ 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": 3,
"counts": {
"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 shor_discrete_log(json: ShorDiscreteLogObjectiveEvaluationRequest):
print(json)
if json:
return objective_service.generate_shor_discrete_log_objective_response(
ShorDiscreteLogObjectiveEvaluationRequest(**json)
)
26 changes: 26 additions & 0 deletions app/helperfunctions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from fractions import Fraction


def take_first(elem):
return elem[0]

Expand Down Expand Up @@ -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
35 changes: 35 additions & 0 deletions app/model/objective_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
59 changes: 58 additions & 1 deletion app/services/objective_service.py
Original file line number Diff line number Diff line change
@@ -1,14 +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,
)
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):
Expand Down Expand Up @@ -89,6 +92,60 @@ def generate_knapsack_objective_response(input: KnapsackObjectiveEvaluationReque
return ObjectiveResponse(objective_value, [costs], None)


def generate_shor_discrete_log_objective_response(
input: ShorDiscreteLogObjectiveEvaluationRequest,
):
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)

res.append((m_stage1, m_stage2, input.counts[result]))

num_fail = 0
num_success = 0

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):
if EXPECTATION in objFun.lower():
return F_EE(costFun)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_costFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,30 @@ 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())

0 comments on commit 7fd825a

Please sign in to comment.