From b4c9f138b0794f4662b3de5d8bb418a0575206c3 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Fri, 26 Apr 2024 22:03:24 -0400 Subject: [PATCH 1/9] Why is the LinearNonnegative function not converging? --- arbitrage.mpc | 4 +- optimizers.mpc | 163 +++++++++++++++++++++++++------------------------ router.mpc | 3 +- 3 files changed, 88 insertions(+), 82 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 687800b..f817d5e 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -4,8 +4,8 @@ exec(open("../optimizers.mpc").read()) # Skeleton code. Doesn't compile as of Nov 18 sfix.round_nearest = True -cfix.set_precision(f=8, k=34) -sfix.set_precision(f=8, k=34) +cfix.set_precision(f=16, k=40) +sfix.set_precision(f=16, k=40) # Create pools reserves_eq = sfix.Array(2) diff --git a/optimizers.mpc b/optimizers.mpc index 7a5700e..f2967db 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -3,8 +3,8 @@ from Compiler import mpc_math, ml program.use_edabit(True) sfix.round_nearest = True -cfix.set_precision(f=8, k=34) -sfix.set_precision(f=8, k=34) +cfix.set_precision(f=16, k=40) +sfix.set_precision(f=16, k=40) program.use_edabit(True) @@ -24,7 +24,7 @@ class Optimizer: @for_range_opt(self.dimension) def _(j): I[i][j] = (i == j).if_else(cfix(1), cfix(0)) - + return I @@ -40,30 +40,45 @@ class SGDOptimizer(Optimizer): class L_BFGS_BOptimizer(Optimizer): - def update_alpha(self, point_ap, guess_k, p_vec_k, rho, alpha): - current_position = self.function.at(point_ap) + def update_alpha(self, guess_k, p_vec_k, rho, alpha, armijo_c, n_iter=10): alpha_p_vec_k = Matrix(self.dimension, 1, sfix) alpha_p_vec_k.assign(alpha * p_vec_k[:]) - future_position = self.function.at(alpha_p_vec_k) + self.function.grad(guess_k).direct_trans_mul(alpha_p_vec_k) - tmp = sfix.Array(len(current_position)) - tmp.assign(current_position - future_position) + alpha_armijo_c_p_vec_k = Matrix(self.dimension, 1, sfix) + alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) - @while_do(lambda: (tmp[:] <= 0).reveal()) + #@while_do(lambda: (tmp[:] >= 0).reveal()) + """ + i = regint(0) + @do_while def _(): + tmp = Matrix(self.dimension, 1, sfix) + print_ln("%s", tmp.reveal()) + tmp.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k))) + @if_((tmp[:] <= 0).reveal()) + def _(): + i.update(i+1) + alpha.write(rho * alpha) - + alpha_p_vec_k.assign(alpha * p_vec_k[:]) + alpha_armijo_c_p_vec_k.assign(armijo_c * alpha_p_vec_k[:]) + """ + tmp = Matrix(self.dimension, 1, sfix) + @for_range_opt(n_iter) + def _(i): + tmp.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k))) + @if_e((tmp[:] <= 0).reveal()) + def _(): + #print_ln("rho * alpha: %s", rho * alpha) + alpha.write(rho * alpha) + alpha_p_vec_k.assign(alpha * p_vec_k[:]) + alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) + @else_ + def _(): + break_loop() def optimize(self, initial_guess, memory_size=17, threshold=cfix(0.000001), alpha=MemValue(cfix(1)), rho=cfix(0.5), armijo_c=cfix(0.0001), n_iter=10): k = regint(0) - size = self.dimension * memory_size - #S = Matrix(self.dimension, memory_size, sfix) - #Y = Matrix(self.dimension, memory_size, sfix) - #S = [] - #Y = [] - #for i in range(self.dimension): - # S.append(Matrix(self.dimension, 1, sfix)) - # Y.append(Matrix(self.dimension, 1, sfix)) guess_k = Matrix(len(initial_guess), 1, sfix) guess_k.assign(initial_guess) @@ -73,85 +88,75 @@ class L_BFGS_BOptimizer(Optimizer): invH = Matrix(self.dimension, self.dimension, sfix) p_vec_k = Matrix(self.dimension, 1, sfix) - I = self._identity() - #alpha = MemValue(alpha) - # Fine to reveal here since we aren't leaking guess_k - @while_do(lambda: self.function.norm(self.function.grad(guess_k)).reveal() <= threshold) - def f(): - @if_(k == 0) - def _(): - invH = self._identity() - - grad_f_k = self.function.grad(guess_k) - p_vec_k.assign(invH.dot(grad_f_k)) - - # Compute line search using backtracking - # It's okay to reveal here since we aren't revealing guess_k itself - point_ap = Matrix(self.dimension, 1, sfix) - point_ap.assign_vector((alpha * p_vec_k[:])) - point_ap.iadd(guess_k) - - self.update_alpha(point_ap, guess_k, p_vec_k, rho, alpha) + @for_range_opt(n_iter) + def _(i): - #@while_do(lambda: self.function.at(point_ap).reveal() >= (self.function.at(p_vec_k) + self.function.grad(guess_k).direct_trans_mul(p_vec_k)).reveal()) - #def f(): - # alpha.write(rho * alpha) + @if_((self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) + def _(): + print_ln("%s", self.function.norm(self.function.grad(guess_k)).reveal()) + alpha.write(cfix(1)) + @if_(k == 0) + def _(): + invH.assign(self._identity()) - #@for_range(n_iter) - #def _(i): - # if self.function.at(guess_k + p_vec_k).reveal() >= (self.function.at(p_vec_k) + self.function.grad(guess_k).direct_trans_mul(p_vec_k)).reveal(): - # alpha.write(rho * alpha) + grad_f_k = self.function.grad(guess_k) + p_vec_k.assign(invH.dot(grad_f_k)) - prev_guess_k.assign(guess_k) - tmp = Matrix(self.dimension, 1, sfix) - #alpha_vec = cfix(alpha, size=p_vec_k.sizes[0]) + # Compute line search using backtracking + # It's okay to reveal here since we aren't revealing guess_k itself - tmp.assign_vector(alpha * p_vec_k[:]) - guess_k.assign(prev_guess_k + tmp) - S_k = Matrix(self.dimension, 1, sfix) - S_k.assign(guess_k - prev_guess_k) - #S_k = Matrix(len(S[k]), 1, S[k].value_type, address=S[k].address) + self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, n_iter=10) - prev_grad_k.assign(grad_f_k) - grad_f_k.assign(self.function.grad(guess_k)) - Y_k = Matrix(self.dimension, 1, sfix) - Y_k.assign(grad_f_k - prev_grad_k) - #Y_k = Matrix(len(Y[k]), 1, Y[k].value_type, address=Y[k].address) + prev_guess_k.assign(guess_k) + tmp = Matrix(self.dimension, 1, sfix) + tmp.assign(alpha * p_vec_k[:]) + guess_k.assign(prev_guess_k + tmp) + + S_k = Matrix(self.dimension, 1, sfix) + S_k.assign(guess_k - prev_guess_k) + #S_k = Matrix(len(S[k]), 1, S[k].value_type, address=S[k].address) - y_kT_s_k = Matrix(Y_k.sizes[0], S_k.sizes[0], sfix) - y_kT_s_k.assign(Y_k.direct_trans_mul(S_k)) - rho_k = ml.mr(y_kT_s_k, 10) + prev_grad_k.assign(grad_f_k) + grad_f_k.assign(self.function.grad(guess_k)) + Y_k = Matrix(self.dimension, 1, sfix) + Y_k.assign(grad_f_k - prev_grad_k) + #Y_k = Matrix(len(Y[k]), 1, Y[k].value_type, address=Y[k].address) - s_k_y_kT = Matrix(S_k.sizes[0], Y_k.sizes[0], sfix) - s_k_y_kT.assign(S_k.direct_mul_trans(Y_k)) + y_kT_s_k = Matrix(Y_k.sizes[0], S_k.sizes[0], sfix) + y_kT_s_k.assign(Y_k.direct_trans_mul(S_k)) + rho_k = ml.mr(y_kT_s_k, 10) - y_k_s_kT = Matrix(Y_k.sizes[0], S_k.sizes[0], sfix) - y_k_s_kT.assign(Y_k.direct_mul_trans(S_k)) - s_k_s_kT = Matrix(S_k.sizes[0], S_k.sizes[0], sfix) - s_k_s_kT.assign(S_k.direct_mul_trans(S_k)) + s_k_y_kT = Matrix(S_k.sizes[0], Y_k.sizes[0], sfix) + s_k_y_kT.assign(S_k.direct_mul_trans(Y_k)) - rho_k_s_k_y_kT = Matrix(rho_k.sizes[0], s_k_y_kT.sizes[0], sfix) - rho_k_s_k_y_kT.assign(rho_k.direct_mul(s_k_y_kT)) + y_k_s_kT = Matrix(Y_k.sizes[0], S_k.sizes[0], sfix) + y_k_s_kT.assign(Y_k.direct_mul_trans(S_k)) + s_k_s_kT = Matrix(S_k.sizes[0], S_k.sizes[0], sfix) + s_k_s_kT.assign(S_k.direct_mul_trans(S_k)) - first_operand = Matrix(rho_k_s_k_y_kT.sizes[0], rho_k_s_k_y_kT.sizes[1], sfix) - first_operand.assign(I - rho_k_s_k_y_kT) + rho_k_s_k_y_kT = Matrix(rho_k.sizes[0], s_k_y_kT.sizes[0], sfix) + rho_k_s_k_y_kT.assign(rho_k.direct_mul(s_k_y_kT)) - res1 = Matrix(invH.sizes[0], invH.sizes[1], sfix) - res1.assign(first_operand.direct_mul(invH)) + first_operand = Matrix(rho_k_s_k_y_kT.sizes[0], rho_k_s_k_y_kT.sizes[1], sfix) + first_operand.assign(I - rho_k_s_k_y_kT) - second_operand = Matrix(self.dimension, self.dimension, sfix) - second_operand.assign(rho_k.direct_mul(y_k_s_kT)) + res1 = Matrix(invH.sizes[0], invH.sizes[1], sfix) + res1.assign(first_operand.direct_mul(invH)) - third_operand = Matrix(self.dimension, self.dimension, sfix) - third_operand.assign(rho_k.direct_mul(s_k_s_kT)) - + second_operand = Matrix(self.dimension, self.dimension, sfix) + second_operand.assign(rho_k.direct_mul(y_k_s_kT)) - invH.assign(res1.direct_mul((I - second_operand) + third_operand)) + third_operand = Matrix(self.dimension, self.dimension, sfix) + third_operand.assign(rho_k.direct_mul(s_k_s_kT)) + + invH.assign(res1.direct_mul((I - second_operand) + third_operand)) - k.update(k + 1) + #print_ln("%s", self.function.norm(self.function.grad(guess_k)).reveal()) + #guess_k.print_reveal_nested() + k.update(k + 1) return (guess_k, self.function.at(guess_k)) diff --git a/router.mpc b/router.mpc index 19aa04d..9eb9c27 100644 --- a/router.mpc +++ b/router.mpc @@ -60,7 +60,8 @@ class ArbitrageRouter: """ self.find_arb(self.v) - new_price_vector = optimizer.optimize(self.v)[0] + new_price_vector = optimizer.optimize(self.v, threshold=cfix(1.41))[0] + print_ln("router.v: %s", new_price_vector.reveal()) self.v.assign(new_price_vector) self.find_arb(self.v) From 5fb2cfe1abf47bd6c6eb463e21bfee1767eee9e3 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Sat, 27 Apr 2024 10:09:31 -0400 Subject: [PATCH 2/9] Implemented wolfe conditions --- optimizers.mpc | 52 ++++++++++++++++++++++++++++++++------------------ router.mpc | 6 +++--- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/optimizers.mpc b/optimizers.mpc index f2967db..f04d122 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -40,21 +40,21 @@ class SGDOptimizer(Optimizer): class L_BFGS_BOptimizer(Optimizer): - def update_alpha(self, guess_k, p_vec_k, rho, alpha, armijo_c, n_iter=10): + def update_alpha(self, guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10): alpha_p_vec_k = Matrix(self.dimension, 1, sfix) alpha_p_vec_k.assign(alpha * p_vec_k[:]) alpha_armijo_c_p_vec_k = Matrix(self.dimension, 1, sfix) - alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) + alpha_armijo_c_p_vec_k.assign(armijo_c * alpha_p_vec_k[:]) #@while_do(lambda: (tmp[:] >= 0).reveal()) """ i = regint(0) @do_while def _(): - tmp = Matrix(self.dimension, 1, sfix) - print_ln("%s", tmp.reveal()) - tmp.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k))) - @if_((tmp[:] <= 0).reveal()) + tmp1 = Matrix(self.dimension, 1, sfix) + #print_ln("%s", tmp.reveal()) + tmp.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k)) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k)) + @if_((tmp[:] >= 0).reveal()) def _(): i.update(i+1) @@ -63,21 +63,27 @@ class L_BFGS_BOptimizer(Optimizer): alpha_armijo_c_p_vec_k.assign(armijo_c * alpha_p_vec_k[:]) """ - tmp = Matrix(self.dimension, 1, sfix) + tmp1 = Matrix(self.dimension, 1, sfix) @for_range_opt(n_iter) def _(i): - tmp.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k))) - @if_e((tmp[:] <= 0).reveal()) + tmp1.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k)) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k)) + @if_e((tmp1[:] >= 0).reveal()) def _(): - #print_ln("rho * alpha: %s", rho * alpha) - alpha.write(rho * alpha) - alpha_p_vec_k.assign(alpha * p_vec_k[:]) - alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) + tmp2 = Matrix(self.dimension, 1, sfix) + c2_p_vec_k = Matrix(self.dimension, 1, sfix) + c2_p_vec_k.assign(-c_2 * p_vec_k[:]) + tmp2.assign(p_vec_k.direct_trans_mul(self.function.grad(guess_k + alpha_p_vec_k)) + c2_p_vec_k.direct_trans_mul(self.function.grad(guess_k))) + @if_((tmp2[:] >= 0).reveal()) + def _(): + #print_ln("rho * alpha: %s", rho * alpha) + alpha.write(rho * alpha) + alpha_p_vec_k.assign(alpha * p_vec_k[:]) + alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) @else_ def _(): break_loop() - def optimize(self, initial_guess, memory_size=17, threshold=cfix(0.000001), alpha=MemValue(cfix(1)), rho=cfix(0.5), armijo_c=cfix(0.0001), n_iter=10): + def optimize(self, initial_guess, memory_size=17, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): k = regint(0) guess_k = Matrix(len(initial_guess), 1, sfix) @@ -92,22 +98,26 @@ class L_BFGS_BOptimizer(Optimizer): @for_range_opt(n_iter) def _(i): - - @if_((self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) + #print_ln("%s", self.function.grad(guess_k).reveal()) + #print_ln("%s", threshold) + @if_e((self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) def _(): - print_ln("%s", self.function.norm(self.function.grad(guess_k)).reveal()) - alpha.write(cfix(1)) + #print_ln("%s", (self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) + #alpha.write(cfix(1)) @if_(k == 0) def _(): invH.assign(self._identity()) grad_f_k = self.function.grad(guess_k) p_vec_k.assign(invH.dot(grad_f_k)) + #print_ln("%s", invH.dot(grad_f_k).reveal()) + #print_ln("%s", p_vec_k.reveal()) + #invH.dot(grad_f_k).print_reveal_nested() # Compute line search using backtracking # It's okay to reveal here since we aren't revealing guess_k itself - self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, n_iter=10) + self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10) prev_guess_k.assign(guess_k) tmp = Matrix(self.dimension, 1, sfix) @@ -157,6 +167,10 @@ class L_BFGS_BOptimizer(Optimizer): #print_ln("%s", self.function.norm(self.function.grad(guess_k)).reveal()) #guess_k.print_reveal_nested() k.update(k + 1) + + @else_ + def _(): + break_loop() return (guess_k, self.function.at(guess_k)) diff --git a/router.mpc b/router.mpc index 9eb9c27..d84a82c 100644 --- a/router.mpc +++ b/router.mpc @@ -31,7 +31,7 @@ class ArbitrageRouter: def route(self, optimizer, price_vector=None): if price_vector is None: - self.v.assign_all(sfix(1)) # OG code says to use initial marginal price here + self.v.assign_all(sfix(1/2)) # OG code says to use initial marginal price here else: self.v.assign(price_vector) @@ -60,8 +60,8 @@ class ArbitrageRouter: """ self.find_arb(self.v) - new_price_vector = optimizer.optimize(self.v, threshold=cfix(1.41))[0] - print_ln("router.v: %s", new_price_vector.reveal()) + new_price_vector = optimizer.optimize(self.v, threshold=cfix(1))[0] + #print_ln("router.v: %s", new_price_vector.reveal()) self.v.assign(new_price_vector) self.find_arb(self.v) From ba79b4067dba056bb076bc2931f282fed187e4a2 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Sun, 28 Apr 2024 11:38:16 -0400 Subject: [PATCH 3/9] Added fn and g from CFMM.router but will need to refactor everything to use matrices --- arbitrage.mpc | 8 +++--- cfmms.mpc | 7 +++++- optimizers.mpc | 37 ++++++++++++++++----------- router.mpc | 68 +++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index f817d5e..88111f7 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -10,18 +10,18 @@ sfix.set_precision(f=16, k=40) # Create pools reserves_eq = sfix.Array(2) reserves_eq.assign([1000000, 1000000]) -globals_eq = [0, 1] +globals_eq = Array.create_from([regint(0), regint(1)]) fee = sfix(1) equal_pool = ProductTwoCoinCFMM(reserves_eq, fee, globals_eq) reserves_uneq = sfix.Array(2) reserves_uneq.assign_vector([1000, 2000]) -globals_uneq = [0, 1] +globals_uneq = Array.create_from([regint(0), regint(1)]) unequal_small_pool = ProductTwoCoinCFMM(reserves_uneq, fee, globals_uneq) reserves_weighted = sfix.Array(2) reserves_weighted.assign_vector([1000, 2000]) -globals_weighted = [0, 1] +globals_weighted = Array.create_from([regint(0), regint(1)]) weights = sfix.Array(2) weights.assign_vector([0.4, 0.6]) weighted_pool = GeometricMeanTwoCoinCFMM(reserves_weighted, weights, fee, globals_weighted) @@ -30,6 +30,8 @@ weighted_pool = GeometricMeanTwoCoinCFMM(reserves_weighted, weights, fee, global # Just get the market price of assets from a player. Will change to MPCaaS setting client later. price_vector = sfix.Array(2) price_vector[0], price_vector[1] = sfix.get_input_from(0, size=2) +#print("from arbitrage.mpc") +#print(type(price_vector[0])) lin_obj = LinearNonnegative(price_vector) l_bfgs_b_opt = L_BFGS_BOptimizer(lin_obj, 2) router = ArbitrageRouter([equal_pool, unequal_small_pool, weighted_pool], 2, lin_obj) diff --git a/cfmms.mpc b/cfmms.mpc index 8e8ed85..ab0e6dc 100644 --- a/cfmms.mpc +++ b/cfmms.mpc @@ -8,7 +8,7 @@ class CFMM: """ reserves: list[sfix] gamma: sfix - Ai: list[int] + Ai: Array[int] """ def __init__(self, reserves, fee, Ai): self.R = reserves @@ -74,6 +74,11 @@ class ProductTwoCoinCFMM(CFMM): runtime_error_if(is_gamma_nonneg.reveal() == 0, "Gamma should be nonnegative") k = (self.gamma > 0).if_else(self.phi(), False) + #print("From cfmms.mpc") + #print(type(price_vector[0])) + #print(type(tendered_baskets)) + #print(type(received_baskets)) + #print(type(price_vector)) tendered_baskets[0] = (self.gamma > 0).if_else(self.arb_delta(price_vector[1]/price_vector[0], self.R[0], k, self.gamma), False) tendered_baskets[1] = (self.gamma > 0).if_else(self.arb_delta(price_vector[0]/price_vector[1], self.R[1], k, self.gamma), False) diff --git a/optimizers.mpc b/optimizers.mpc index f04d122..16883e4 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -40,7 +40,7 @@ class SGDOptimizer(Optimizer): class L_BFGS_BOptimizer(Optimizer): - def update_alpha(self, guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10): + def update_alpha(self, guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10, fn=None, g=None): alpha_p_vec_k = Matrix(self.dimension, 1, sfix) alpha_p_vec_k.assign(alpha * p_vec_k[:]) alpha_armijo_c_p_vec_k = Matrix(self.dimension, 1, sfix) @@ -66,13 +66,13 @@ class L_BFGS_BOptimizer(Optimizer): tmp1 = Matrix(self.dimension, 1, sfix) @for_range_opt(n_iter) def _(i): - tmp1.assign(self.function.at(guess_k) - (self.function.at(guess_k + alpha_p_vec_k)) + self.function.grad(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k)) + tmp1.assign(fn(guess_k) - (fn(guess_k + alpha_p_vec_k)) + g(guess_k).direct_trans_mul(alpha_armijo_c_p_vec_k)) @if_e((tmp1[:] >= 0).reveal()) def _(): tmp2 = Matrix(self.dimension, 1, sfix) c2_p_vec_k = Matrix(self.dimension, 1, sfix) c2_p_vec_k.assign(-c_2 * p_vec_k[:]) - tmp2.assign(p_vec_k.direct_trans_mul(self.function.grad(guess_k + alpha_p_vec_k)) + c2_p_vec_k.direct_trans_mul(self.function.grad(guess_k))) + tmp2.assign(p_vec_k.direct_trans_mul(g(guess_k + alpha_p_vec_k)) + c2_p_vec_k.direct_trans_mul(g(guess_k))) @if_((tmp2[:] >= 0).reveal()) def _(): #print_ln("rho * alpha: %s", rho * alpha) @@ -83,12 +83,15 @@ class L_BFGS_BOptimizer(Optimizer): def _(): break_loop() - def optimize(self, initial_guess, memory_size=17, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): + def optimize(self, initial_guess, fn=None, g=None, memory_size=17, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): k = regint(0) - guess_k = Matrix(len(initial_guess), 1, sfix) + #guess_k = Matrix(len(initial_guess), 1, sfix) + guess_k = Array.create_from(initial_guess) guess_k.assign(initial_guess) - grad_f_k = self.function.grad(guess_k) + #print("from optimizers.mpc") + #print(type(initial_guess)) + grad_f_k = g(guess_k) prev_guess_k = Matrix(len(initial_guess), 1, sfix) prev_grad_k = Matrix(len(initial_guess), 1, sfix) @@ -100,7 +103,7 @@ class L_BFGS_BOptimizer(Optimizer): def _(i): #print_ln("%s", self.function.grad(guess_k).reveal()) #print_ln("%s", threshold) - @if_e((self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) + @if_e((self.function.norm(g(guess_k)) > threshold).reveal()) def _(): #print_ln("%s", (self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) #alpha.write(cfix(1)) @@ -108,8 +111,10 @@ class L_BFGS_BOptimizer(Optimizer): def _(): invH.assign(self._identity()) - grad_f_k = self.function.grad(guess_k) - p_vec_k.assign(invH.dot(grad_f_k)) + grad_f_k = g(guess_k) + tmp_grad_f_k = Matrix(len(grad_f_k), 1, sfix) + tmp_grad_f_k.assign(grad_f_k) + p_vec_k.assign(invH.dot(tmp_grad_f_k)) #print_ln("%s", invH.dot(grad_f_k).reveal()) #print_ln("%s", p_vec_k.reveal()) #invH.dot(grad_f_k).print_reveal_nested() @@ -117,7 +122,7 @@ class L_BFGS_BOptimizer(Optimizer): # Compute line search using backtracking # It's okay to reveal here since we aren't revealing guess_k itself - self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10) + self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10, fn=fn, g=g) prev_guess_k.assign(guess_k) tmp = Matrix(self.dimension, 1, sfix) @@ -129,7 +134,7 @@ class L_BFGS_BOptimizer(Optimizer): #S_k = Matrix(len(S[k]), 1, S[k].value_type, address=S[k].address) prev_grad_k.assign(grad_f_k) - grad_f_k.assign(self.function.grad(guess_k)) + grad_f_k.assign(g(guess_k)) Y_k = Matrix(self.dimension, 1, sfix) Y_k.assign(grad_f_k - prev_grad_k) #Y_k = Matrix(len(Y[k]), 1, Y[k].value_type, address=Y[k].address) @@ -172,7 +177,7 @@ class L_BFGS_BOptimizer(Optimizer): def _(): break_loop() - return (guess_k, self.function.at(guess_k)) + return (guess_k, self.function(guess_k)) class LinearNonnegative: @@ -185,6 +190,7 @@ class LinearNonnegative: if i is False: raise Exception self.constants = constants + #return self.constants def conjugate(self, v): tmp = [] @@ -195,7 +201,8 @@ class LinearNonnegative: if i is False: raise Exception - zero_vec = Matrix(len(v), 1, sfix) + #zero_vec = Matrix(len(v), 1, sfix) + zero_vec = sfix.Array(2) zero_vec.assign_all(0) return zero_vec @@ -213,8 +220,8 @@ class LinearNonnegative: zero_vec.assign_all(0) return zero_vec - def at(self, x): - return self.constants + #def at(self, x): + # return self.constants def norm(self, x): s = sum(self.constants) diff --git a/router.mpc b/router.mpc index d84a82c..a0bd661 100644 --- a/router.mpc +++ b/router.mpc @@ -26,6 +26,8 @@ class ArbitrageRouter: def find_arb(self, price_vector): for i, cfmm in zip(list(range(len(self.cfmms))), self.cfmms): + #print("from router.find_arb") + #print(type(price_vector)) cfmm.find_arb(self.big_delta[i], self.big_lambda[i], price_vector) @@ -34,35 +36,77 @@ class ArbitrageRouter: self.v.assign_all(sfix(1/2)) # OG code says to use initial marginal price here else: self.v.assign(price_vector) + #print("from router.route") + #print(type(self.v)) - """ - NB: Might not need this as it's related to an implementation detail related to Julia's LBFGSB implementation. - NB: Maybe there might be a way to leverage this to make the computation faster. To be explored later. + + #NB: Might not need this as it's related to an implementation detail related to Julia's LBFGSB implementation. + #NB: Maybe there might be a way to leverage this to make the computation faster. To be explored later. def fn(v): - tmp = [] - @for_range(len(self.constants)) + tmp = Matrix(len(self.v), 1, sfix) + @for_range(len(self.v)) def _(i): - tmp.append((self.constants[i][0] <= v[i][0]).if_else(0, False)) + tmp[i].assign(((v[i][0] <= self.v[i][0]).if_else(0, 1))) - if !all(tmp): + @if_((tmp[:] == 0).reveal()) + def _(): self.find_arb(v) - self.price_vector.assign(v) + self.v.assign(v) accumulator = sfix(0) for (big_delta, big_lambda, cfmm) in zip(self.big_delta, self.big_lambda, self.cfmms): - accumulator += (big_lambda.dot(v[cfmm.Ai]) - big_delta.dot(v[cfmm.Ai])) + #print("from router.mpc") + #print(type(v)) + #print(type(v[cfmm.Ai])) + tmp_big_delta = Matrix(len(big_delta), 1, sfix) + tmp_big_lambda = Matrix(len(big_lambda), 1, sfix) + accumulator += (tmp_big_lambda.dot(v[cfmm.Ai]) - tmp_big_delta.dot(v[cfmm.Ai])) return self.linearnn_objective.conjugate(v) + accumulator def g(v): - pass - """ + G = Matrix(len(self.v), 1, sfix) + #G = sfix.Array(2) + + tmp = Matrix(len(self.v), 1, sfix) + #tmp = [] + @for_range(len(self.v)) + def _(i): + tmp[i].assign(((v[i][0] <= self.v[i][0]).if_else(0, 1))) + + @if_((tmp[:] == 0).reveal()) + def _(): + #tmp_v = Array.create_from(v) + self.find_arb(v) + self.v.assign(v) + + """ + if not all(tmp): + self.find_arb(v) + self.v.assign(v) + """ + + G.assign(self.linearnn_objective.grad(v)) + #print("from router.mpc") + #print(type(G)) + + for big_delta, big_lambda, c in zip(self.big_delta, self.big_lambda, self.cfmms): + #print("from router.mpc") + #print(type(c.Ai)) + tmp2 = G.get_slice_vector(c.Ai) + tmp2 += (big_lambda - big_delta) + G.assign(tmp2) + + return G + self.find_arb(self.v) - new_price_vector = optimizer.optimize(self.v, threshold=cfix(1))[0] + new_price_vector = optimizer.optimize(self.v, fn, g, threshold=cfix(1))[0] #print_ln("router.v: %s", new_price_vector.reveal()) self.v.assign(new_price_vector) + #print("from router.route") + #print(type(self.v)) self.find_arb(self.v) From dbc4c01f347017ab22619b1d9ca5d66b903122a4 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Sun, 28 Apr 2024 15:55:06 -0400 Subject: [PATCH 4/9] Transition to matrices compiles --- arbitrage.mpc | 24 ++++++++++++------- cfmms.mpc | 49 +++++++++++++++++++++----------------- optimizers.mpc | 64 ++++++++++++++++++++++++++++++++++++++++---------- router.mpc | 6 +++-- 4 files changed, 97 insertions(+), 46 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 88111f7..608f246 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -8,28 +8,34 @@ cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) # Create pools -reserves_eq = sfix.Array(2) +#reserves_eq = sfix.Array(2) +reserves_eq = Matrix(2, 1, sfix) reserves_eq.assign([1000000, 1000000]) globals_eq = Array.create_from([regint(0), regint(1)]) fee = sfix(1) equal_pool = ProductTwoCoinCFMM(reserves_eq, fee, globals_eq) -reserves_uneq = sfix.Array(2) -reserves_uneq.assign_vector([1000, 2000]) +#reserves_uneq = sfix.Array(2) +reserves_uneq = Matrix(2, 1, sfix) +reserves_uneq.assign([1000, 2000]) globals_uneq = Array.create_from([regint(0), regint(1)]) unequal_small_pool = ProductTwoCoinCFMM(reserves_uneq, fee, globals_uneq) -reserves_weighted = sfix.Array(2) -reserves_weighted.assign_vector([1000, 2000]) +#reserves_weighted = sfix.Array(2) +reserves_weighted = Matrix(2, 1, sfix) +reserves_weighted.assign([1000, 2000]) globals_weighted = Array.create_from([regint(0), regint(1)]) -weights = sfix.Array(2) -weights.assign_vector([0.4, 0.6]) +#weights = sfix.Array(2) +weights = Matrix(2, 1, sfix) +weights.assign([0.4, 0.6]) weighted_pool = GeometricMeanTwoCoinCFMM(reserves_weighted, weights, fee, globals_weighted) # Get market price vector # Just get the market price of assets from a player. Will change to MPCaaS setting client later. -price_vector = sfix.Array(2) -price_vector[0], price_vector[1] = sfix.get_input_from(0, size=2) +#price_vector = sfix.Array(2) +price_vector = Matrix(2, 1, sfix) +#price_vector[0][0], price_vector[0][1] = sfix.get_input_from(0, size=2) +price_vector.assign(sfix.get_input_from(0, size=2)) #print("from arbitrage.mpc") #print(type(price_vector[0])) lin_obj = LinearNonnegative(price_vector) diff --git a/cfmms.mpc b/cfmms.mpc index ab0e6dc..89576b5 100644 --- a/cfmms.mpc +++ b/cfmms.mpc @@ -6,9 +6,9 @@ from Compiler import mpc_math class CFMM: """ - reserves: list[sfix] + reserves: Matrix[sfix] gamma: sfix - Ai: Array[int] + Ai: Array[regint] """ def __init__(self, reserves, fee, Ai): self.R = reserves @@ -30,8 +30,8 @@ class CFMM: """ - price_vector: list[sfix] - reservers: list[sfix] + price_vector: Matrix[sfix] + reserves: Matrix[sfix] invariant: sfix fee: sfix """ @@ -39,8 +39,8 @@ class CFMM: pass """ - price_vector: list[sfix] - reservers: list[sfix] + price_vector: Matrix[sfix] + reserves: Matrix[sfix] invariant: sfix fee: sfix """ @@ -59,12 +59,16 @@ class ProductTwoCoinCFMM(CFMM): super().__init__(reserves, fee, Ai) def phi(self): - return self.R[0] * self.R[1] + #print(type(self.R[0][0] * self.R[1][0])) + return self.R[0][0] * self.R[1][0] def gradient_phi(self): - R_prime = sfix.Array(len(self.R)) - R_prime[0] = self.R[1] - R_prime[1] = self.R[0] + R_prime = Matrix(len(self.R), 1, sfix) + R_prime[0][0] = self.R[0][1] + R_prime[0][1]= self.R[0][0] + #R_prime = sfix.Array(len(self.R)) + #R_prime[0] = self.R[1] + #R_prime[1] = self.R[0] return R_prime def find_arb(self, tendered_baskets, received_baskets, price_vector): @@ -79,11 +83,11 @@ class ProductTwoCoinCFMM(CFMM): #print(type(tendered_baskets)) #print(type(received_baskets)) #print(type(price_vector)) - tendered_baskets[0] = (self.gamma > 0).if_else(self.arb_delta(price_vector[1]/price_vector[0], self.R[0], k, self.gamma), False) - tendered_baskets[1] = (self.gamma > 0).if_else(self.arb_delta(price_vector[0]/price_vector[1], self.R[1], k, self.gamma), False) + tendered_baskets[0] = (self.gamma > 0).if_else(self.arb_delta(price_vector[1][0]/price_vector[0][0], self.R[0][0], k, self.gamma), False) + tendered_baskets[1] = (self.gamma > 0).if_else(self.arb_delta(price_vector[0][0]/price_vector[1][0], self.R[1][0], k, self.gamma), False) - received_baskets[0] = (self.gamma > 0).if_else(self.arb_lambda(price_vector[0]/price_vector[1], self.R[0], k, self.gamma), False) - received_baskets[1] = (self.gamma > 0).if_else(self.arb_lambda(price_vector[1]/price_vector[0], self.R[1], k, self.gamma), False) + received_baskets[0] = (self.gamma > 0).if_else(self.arb_lambda(price_vector[0][0]/price_vector[1][0], self.R[0][0], k, self.gamma), False) + received_baskets[1] = (self.gamma > 0).if_else(self.arb_lambda(price_vector[1][0]/price_vector[0][0], self.R[1][0], k, self.gamma), False) # Can simply short circuit to k.reveal() == False # Leaving for learning + demonstration purposes @@ -118,9 +122,10 @@ class GeometricMeanTwoCoinCFMM(CFMM): return (self.R[0]**self.weights[0]) * (self.R[1]**self.weights[1]) def gradient_phi(self): - R_prime = sfix.Array(len(self.R)) - R_prime[0] = self.weights[0] * (self.R[1]/self.R[0])**self.weights[1] - R_prime[1] = self.weights[1] * (self.R[0]/self.R[1])**self.weights[0] + #R_prime = sfix.Array(len(self.R)) + R_prime = Matrix(len(self.R), 1, sfix) + R_prime[0][0] = self.weights[0][0] * (self.R[0][1]/self.R[0][0])**self.weights[0][1] + R_prime[1][0] = self.weights[1][0] * (self.R[0][0]/self.R[1][0])**self.weights[0][0] return R_prime def geom_arb_delta(self, market_reference_price, reserve1, reserve2, invariant, fee): @@ -132,13 +137,13 @@ class GeometricMeanTwoCoinCFMM(CFMM): def find_arb(self, tendered_baskets, received_baskets, price_vector): R = self.reserves fee = self.fee - eta = self.weights[1]/self.weights[0] + eta = self.weights[1][0]/self.weights[0][0] - tendered_baskets[0] = self.geom_arb_delta(price_vector[1]/price_vector[0], R[1], R[0], eta, fee) - tendered_baskets[1] = self.geom_arb_delta(price_vector[0]/price_vector[1], R[0], R[1], 1/eta, fee) + tendered_baskets[0] = self.geom_arb_delta(price_vector[1][0]/price_vector[0][0], R[1][0], R[0][0], eta, fee) + tendered_baskets[1] = self.geom_arb_delta(price_vector[0][0]/price_vector[1][0], R[0][0], R[1][0], 1/eta, fee) - received_baskets[0] = self.geom_arb_lambda(price_vector[0]/price_vector[1], R[0], R[1], 1/eta, fee) - received_baskets[1] = self.geom_arb_lambda(price_vector[1]/price_vector[0], R[1], R[0], eta, fee) + received_baskets[0] = self.geom_arb_lambda(price_vector[0][0]/price_vector[1][0], R[0][0], R[1][0], 1/eta, fee) + received_baskets[1] = self.geom_arb_lambda(price_vector[1][0]/price_vector[0][0], R[1][0], R[0][0], eta, fee) return (tendered_baskets, received_baskets) diff --git a/optimizers.mpc b/optimizers.mpc index 16883e4..0ed9f04 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -86,8 +86,8 @@ class L_BFGS_BOptimizer(Optimizer): def optimize(self, initial_guess, fn=None, g=None, memory_size=17, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): k = regint(0) - #guess_k = Matrix(len(initial_guess), 1, sfix) - guess_k = Array.create_from(initial_guess) + guess_k = Matrix(len(initial_guess), 1, sfix) + #guess_k = Array.create_from(initial_guess) guess_k.assign(initial_guess) #print("from optimizers.mpc") #print(type(initial_guess)) @@ -177,22 +177,27 @@ class L_BFGS_BOptimizer(Optimizer): def _(): break_loop() - return (guess_k, self.function(guess_k)) + return (guess_k, self.function.at(guess_k)) class LinearNonnegative: def __init__(self, constants): - tmp = [] - for i in range(len(constants)): - tmp.append((constants[i] > 0).if_else(True, False)) + #tmp = [] + #for i in range(len(constants)): + # tmp.append((constants[i] > 0).if_else(True, False)) - for i in tmp: - if i is False: - raise Exception + #for i in tmp: + # if i is False: + # raise Exception + + # NB: Probably shouldn't reveal this + runtime_error_if((constants[0][:] > 0).reveal(), "all elements must be strictly positive") + self.constants = constants #return self.constants def conjugate(self, v): + """ tmp = [] for i in range(len(constants)): tmp.append((self.constants[i][0] <= v[i][0]).if_else(True, False)) @@ -200,13 +205,31 @@ class LinearNonnegative: for i in tmp: if i is False: raise Exception + """ + + tmp = Matrix(len(v), 1, sfix) + tmp.assign(v[:][0] - self.constants[:][0]) + + zero_vec = Matrix(len(v), 1, sfix) + + @if_e((tmp[:] >= 0).reveal()) + def _(): + zero_vec.assign_all(0) + @else_ + def _(): + runtime_error("Constants must be at most coefficients in v") - #zero_vec = Matrix(len(v), 1, sfix) - zero_vec = sfix.Array(2) + return zero_vec + + """ + zero_vec = Matrix(len(v), 1, sfix) + #zero_vec = sfix.Array(2) zero_vec.assign_all(0) return zero_vec + """ def grad(self, v): + """ tmp = [] @for_range(len(self.constants)) def _(i): @@ -219,9 +242,24 @@ class LinearNonnegative: zero_vec = Matrix(len(v), 1, sfix) zero_vec.assign_all(0) return zero_vec + """ + + tmp = Matrix(len(v), 1, sfix) + tmp.assign(v[:][0] - self.constants[:][0]) + + zero_vec = Matrix(len(v), 1, sfix) + + @if_e((tmp[:] >= 0).reveal()) + def _(): + zero_vec.assign_all(0) + @else_ + def _(): + runtime_error("Constants must be at most coefficients in v") + + return zero_vec - #def at(self, x): - # return self.constants + def at(self, x): + return self.constants def norm(self, x): s = sum(self.constants) diff --git a/router.mpc b/router.mpc index a0bd661..c846224 100644 --- a/router.mpc +++ b/router.mpc @@ -61,9 +61,11 @@ class ArbitrageRouter: #print(type(v[cfmm.Ai])) tmp_big_delta = Matrix(len(big_delta), 1, sfix) tmp_big_lambda = Matrix(len(big_lambda), 1, sfix) - accumulator += (tmp_big_lambda.dot(v[cfmm.Ai]) - tmp_big_delta.dot(v[cfmm.Ai])) + #print(type(tmp_big_lambda)) + #print(type(v.get_vector_by_indices(*cfmm.Ai))) + accumulator += (tmp_big_lambda.dot(v.get_vector_by_indices(*cfmm.Ai)) - tmp_big_delta.dot(v.get_vector_by_indices(*cfmm.Ai))) - return self.linearnn_objective.conjugate(v) + accumulator + return self.linearnn_objective.conjugate(v)[:] + accumulator def g(v): G = Matrix(len(self.v), 1, sfix) From dcdd12932d63f5860a277def71e86abfe134db48 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Sun, 28 Apr 2024 18:43:23 -0400 Subject: [PATCH 5/9] Added several options to speed up computation --- arbitrage.mpc | 6 +++++- cfmms.mpc | 9 ++++++++- optimizers.mpc | 14 ++++++++++---- router.mpc | 12 ++++++++++-- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 608f246..297c91e 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -7,6 +7,9 @@ sfix.round_nearest = True cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) +program.use_edabit(True) +program.use_split(4) + # Create pools #reserves_eq = sfix.Array(2) reserves_eq = Matrix(2, 1, sfix) @@ -44,7 +47,8 @@ router = ArbitrageRouter([equal_pool, unequal_small_pool, weighted_pool], 2, lin router.route(l_bfgs_b_opt, price_vector=price_vector) net_trade = router.netflows() price_vector_tmp = Matrix(1, 2, sfix) -price_vector_tmp.assign(price_vector) +price_vector_tmp.assign(router.v) +print_ln("%s", router.v.reveal()) print_ln("Profit: %s", price_vector_tmp.dot(net_trade).reveal()) #print_ln_to(0, "Net trade: ") #net_trade.print_reveal_nested() diff --git a/cfmms.mpc b/cfmms.mpc index 89576b5..c03b782 100644 --- a/cfmms.mpc +++ b/cfmms.mpc @@ -4,6 +4,13 @@ from Compiler import mpc_math +sfix.round_nearest = True +cfix.set_precision(f=16, k=40) +sfix.set_precision(f=16, k=40) + +program.use_edabit(True) +program.use_split(4) + class CFMM: """ reserves: Matrix[sfix] @@ -95,7 +102,7 @@ class ProductTwoCoinCFMM(CFMM): #def _(): # runtime_error("gamma is not nonnegative") - # return (tendered_baskets, received_baskets) + #return (tendered_baskets, received_baskets) diff --git a/optimizers.mpc b/optimizers.mpc index 0ed9f04..08c23f8 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -1,6 +1,8 @@ from Compiler import mpc_math, ml program.use_edabit(True) +program.use_split(4) + sfix.round_nearest = True cfix.set_precision(f=16, k=40) @@ -116,7 +118,7 @@ class L_BFGS_BOptimizer(Optimizer): tmp_grad_f_k.assign(grad_f_k) p_vec_k.assign(invH.dot(tmp_grad_f_k)) #print_ln("%s", invH.dot(grad_f_k).reveal()) - #print_ln("%s", p_vec_k.reveal()) + print_ln("%s", p_vec_k.reveal()) #invH.dot(grad_f_k).print_reveal_nested() # Compute line search using backtracking @@ -191,7 +193,8 @@ class LinearNonnegative: # raise Exception # NB: Probably shouldn't reveal this - runtime_error_if((constants[0][:] > 0).reveal(), "all elements must be strictly positive") + #print_ln("%s", constants[:].reveal()) + runtime_error_if((constants[:] <= 0).reveal(), "all elements must be strictly positive") self.constants = constants #return self.constants @@ -209,6 +212,7 @@ class LinearNonnegative: tmp = Matrix(len(v), 1, sfix) tmp.assign(v[:][0] - self.constants[:][0]) + #tmp.print_reveal_nested() zero_vec = Matrix(len(v), 1, sfix) @@ -217,7 +221,8 @@ class LinearNonnegative: zero_vec.assign_all(0) @else_ def _(): - runtime_error("Constants must be at most coefficients in v") + # runtime_error("Constants must be at most coefficients in v") + return zero_vec return zero_vec @@ -254,7 +259,8 @@ class LinearNonnegative: zero_vec.assign_all(0) @else_ def _(): - runtime_error("Constants must be at most coefficients in v") + # runtime_error("Constants must be at most coefficients in v") + return zero_vec return zero_vec diff --git a/router.mpc b/router.mpc index c846224..edf4754 100644 --- a/router.mpc +++ b/router.mpc @@ -1,6 +1,13 @@ exec(open("../cfmms.mpc").read()) exec(open("../optimizers.mpc").read()) +sfix.round_nearest = True +cfix.set_precision(f=16, k=40) +sfix.set_precision(f=16, k=40) + +program.use_edabit(True) +program.use_split(4) + class ArbitrageRouter: """ @@ -116,6 +123,7 @@ class ArbitrageRouter: psi = Matrix(len(self.v), 1, sfix) for (big_delta, big_lambda, cfmm) in zip(self.big_delta, self.big_lambda, self.cfmms): tmp = big_lambda - big_delta - for i in cfmm.Ai: - psi[i] = tmp[i] + psi.assign_vector_by_indices(tmp, *cfmm.Ai) + #for i in cfmm.Ai: + # psi[i] = tmp[i] return psi \ No newline at end of file From b6a116a53d7b163071fcd1f3839a616781bb0f91 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Sun, 28 Apr 2024 22:42:19 -0400 Subject: [PATCH 6/9] Still getting different ouputs for different executions of arbitrage.mpc --- arbitrage.mpc | 3 +++ cfmms.mpc | 3 ++- optimizers.mpc | 27 +++++++++------------------ router.mpc | 8 +++++--- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 297c91e..8262894 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -10,6 +10,8 @@ sfix.set_precision(f=16, k=40) program.use_edabit(True) program.use_split(4) +program.use_trunc_pr = False + # Create pools #reserves_eq = sfix.Array(2) reserves_eq = Matrix(2, 1, sfix) @@ -49,6 +51,7 @@ net_trade = router.netflows() price_vector_tmp = Matrix(1, 2, sfix) price_vector_tmp.assign(router.v) print_ln("%s", router.v.reveal()) +net_trade.print_reveal_nested() print_ln("Profit: %s", price_vector_tmp.dot(net_trade).reveal()) #print_ln_to(0, "Net trade: ") #net_trade.print_reveal_nested() diff --git a/cfmms.mpc b/cfmms.mpc index c03b782..9b05171 100644 --- a/cfmms.mpc +++ b/cfmms.mpc @@ -3,13 +3,14 @@ """ from Compiler import mpc_math - +""" sfix.round_nearest = True cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) program.use_edabit(True) program.use_split(4) +""" class CFMM: """ diff --git a/optimizers.mpc b/optimizers.mpc index 08c23f8..a508ff7 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -1,14 +1,15 @@ from Compiler import mpc_math, ml - +sfix.round_nearest = True +""" program.use_edabit(True) program.use_split(4) +""" - -sfix.round_nearest = True cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) - +""" program.use_edabit(True) +""" class Optimizer: @@ -34,7 +35,7 @@ class SGDOptimizer(Optimizer): def optimize(self, initial_guess, learning_rate=sfix(0.01), n_iter=10): guess = sfix(initial_guess) - @for_range(n_iter) + @for_range_opt(n_iter) def _(i): guess.update(guess - learning_rate * self.function.grad(guess)) @@ -85,15 +86,13 @@ class L_BFGS_BOptimizer(Optimizer): def _(): break_loop() - def optimize(self, initial_guess, fn=None, g=None, memory_size=17, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): + def optimize(self, initial_guess, fn=None, g=None, threshold=cfix(0.0001), alpha=MemValue(cfix(0.5)), rho=cfix(0.5), armijo_c=cfix(0.0001), c_2=cfix(0.9), n_iter=10): k = regint(0) guess_k = Matrix(len(initial_guess), 1, sfix) - #guess_k = Array.create_from(initial_guess) guess_k.assign(initial_guess) - #print("from optimizers.mpc") - #print(type(initial_guess)) - grad_f_k = g(guess_k) + grad_f_k = Matrix(len(initial_guess), 1, sfix) + grad_f_k.assign(g(guess_k)) prev_guess_k = Matrix(len(initial_guess), 1, sfix) prev_grad_k = Matrix(len(initial_guess), 1, sfix) @@ -103,8 +102,6 @@ class L_BFGS_BOptimizer(Optimizer): @for_range_opt(n_iter) def _(i): - #print_ln("%s", self.function.grad(guess_k).reveal()) - #print_ln("%s", threshold) @if_e((self.function.norm(g(guess_k)) > threshold).reveal()) def _(): #print_ln("%s", (self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) @@ -117,12 +114,6 @@ class L_BFGS_BOptimizer(Optimizer): tmp_grad_f_k = Matrix(len(grad_f_k), 1, sfix) tmp_grad_f_k.assign(grad_f_k) p_vec_k.assign(invH.dot(tmp_grad_f_k)) - #print_ln("%s", invH.dot(grad_f_k).reveal()) - print_ln("%s", p_vec_k.reveal()) - #invH.dot(grad_f_k).print_reveal_nested() - - # Compute line search using backtracking - # It's okay to reveal here since we aren't revealing guess_k itself self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10, fn=fn, g=g) diff --git a/router.mpc b/router.mpc index edf4754..545d57c 100644 --- a/router.mpc +++ b/router.mpc @@ -1,12 +1,14 @@ exec(open("../cfmms.mpc").read()) exec(open("../optimizers.mpc").read()) +""" sfix.round_nearest = True cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) program.use_edabit(True) program.use_split(4) +""" class ArbitrageRouter: @@ -51,7 +53,7 @@ class ArbitrageRouter: #NB: Maybe there might be a way to leverage this to make the computation faster. To be explored later. def fn(v): tmp = Matrix(len(self.v), 1, sfix) - @for_range(len(self.v)) + @for_range_opt(len(self.v)) def _(i): tmp[i].assign(((v[i][0] <= self.v[i][0]).if_else(0, 1))) @@ -80,7 +82,7 @@ class ArbitrageRouter: tmp = Matrix(len(self.v), 1, sfix) #tmp = [] - @for_range(len(self.v)) + @for_range_opt(len(self.v)) def _(i): tmp[i].assign(((v[i][0] <= self.v[i][0]).if_else(0, 1))) @@ -111,7 +113,7 @@ class ArbitrageRouter: self.find_arb(self.v) - new_price_vector = optimizer.optimize(self.v, fn, g, threshold=cfix(1))[0] + new_price_vector = optimizer.optimize(self.v, fn, g)[0] #print_ln("router.v: %s", new_price_vector.reveal()) self.v.assign(new_price_vector) #print("from router.route") From a8fa7bdeb12cf2fb1f841d3075c51d19c68584c6 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Mon, 29 Apr 2024 10:22:28 -0400 Subject: [PATCH 7/9] Playing with f and k --- arbitrage.mpc | 2 +- optimizers.mpc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 8262894..43a783b 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -8,7 +8,7 @@ cfix.set_precision(f=16, k=40) sfix.set_precision(f=16, k=40) program.use_edabit(True) -program.use_split(4) +#program.use_split(4) program.use_trunc_pr = False diff --git a/optimizers.mpc b/optimizers.mpc index a508ff7..c2330e8 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -1,5 +1,8 @@ from Compiler import mpc_math, ml sfix.round_nearest = True +#cfix.set_precision(f=16, k=50) +#sfix.set_precision(f=16, k=50) + """ program.use_edabit(True) program.use_split(4) From b6b03146065c798b393cffe977fb78daa1c80c21 Mon Sep 17 00:00:00 2001 From: Mikerah Date: Mon, 29 Apr 2024 13:57:39 -0400 Subject: [PATCH 8/9] Playing with f and k --- arbitrage.mpc | 4 ++-- optimizers.mpc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index 43a783b..a11fa30 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -4,8 +4,8 @@ exec(open("../optimizers.mpc").read()) # Skeleton code. Doesn't compile as of Nov 18 sfix.round_nearest = True -cfix.set_precision(f=16, k=40) -sfix.set_precision(f=16, k=40) +cfix.set_precision(f=32, k=80) +sfix.set_precision(f=32, k=80) program.use_edabit(True) #program.use_split(4) diff --git a/optimizers.mpc b/optimizers.mpc index c2330e8..8ec93c2 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -8,8 +8,8 @@ program.use_edabit(True) program.use_split(4) """ -cfix.set_precision(f=16, k=40) -sfix.set_precision(f=16, k=40) +cfix.set_precision(f=32, k=80) +sfix.set_precision(f=32, k=80) """ program.use_edabit(True) """ From d25dcaca4ef8cde79346e6c73ebb40933821e63d Mon Sep 17 00:00:00 2001 From: Mikerah Date: Fri, 10 May 2024 08:29:04 -0400 Subject: [PATCH 9/9] Results don't converge because of linear function --- arbitrage.mpc | 25 +++++++++++++++---------- optimizers.mpc | 22 ++++++++++++---------- router.mpc | 8 ++++++-- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/arbitrage.mpc b/arbitrage.mpc index a11fa30..7dc0c9f 100644 --- a/arbitrage.mpc +++ b/arbitrage.mpc @@ -3,14 +3,18 @@ exec(open("../router.mpc").read()) exec(open("../optimizers.mpc").read()) # Skeleton code. Doesn't compile as of Nov 18 -sfix.round_nearest = True -cfix.set_precision(f=32, k=80) -sfix.set_precision(f=32, k=80) +program.use_split(4) +#sfix.round_nearest = True +cfix.set_precision(f=32, k=128) +sfix.set_precision(f=32, k=128) -program.use_edabit(True) -#program.use_split(4) +#program.use_edabit(True) + + +#program.use_trunc_pr = False + +#program.use_trunc_pr = True -program.use_trunc_pr = False # Create pools #reserves_eq = sfix.Array(2) @@ -32,7 +36,8 @@ reserves_weighted.assign([1000, 2000]) globals_weighted = Array.create_from([regint(0), regint(1)]) #weights = sfix.Array(2) weights = Matrix(2, 1, sfix) -weights.assign([0.4, 0.6]) +# Weights = [0.4, 0.6] +weights.assign([round(0.4 * (2**32)), round(0.6 * (2**32))]) weighted_pool = GeometricMeanTwoCoinCFMM(reserves_weighted, weights, fee, globals_weighted) # Get market price vector @@ -50,9 +55,9 @@ router.route(l_bfgs_b_opt, price_vector=price_vector) net_trade = router.netflows() price_vector_tmp = Matrix(1, 2, sfix) price_vector_tmp.assign(router.v) -print_ln("%s", router.v.reveal()) -net_trade.print_reveal_nested() -print_ln("Profit: %s", price_vector_tmp.dot(net_trade).reveal()) +#print_ln("%s", router.v.reveal()) +#net_trade.print_reveal_nested() +#print_ln("Profit: %s", price_vector_tmp.dot(net_trade).reveal()) #print_ln_to(0, "Net trade: ") #net_trade.print_reveal_nested() #print(type(price_vector)) diff --git a/optimizers.mpc b/optimizers.mpc index 8ec93c2..b6cd0c5 100644 --- a/optimizers.mpc +++ b/optimizers.mpc @@ -1,5 +1,5 @@ from Compiler import mpc_math, ml -sfix.round_nearest = True +#sfix.round_nearest = True #cfix.set_precision(f=16, k=50) #sfix.set_precision(f=16, k=50) @@ -8,8 +8,8 @@ program.use_edabit(True) program.use_split(4) """ -cfix.set_precision(f=32, k=80) -sfix.set_precision(f=32, k=80) +cfix.set_precision(f=32, k=128) +sfix.set_precision(f=32, k=128) """ program.use_edabit(True) """ @@ -84,7 +84,7 @@ class L_BFGS_BOptimizer(Optimizer): #print_ln("rho * alpha: %s", rho * alpha) alpha.write(rho * alpha) alpha_p_vec_k.assign(alpha * p_vec_k[:]) - alpha_armijo_c_p_vec_k.assign(-armijo_c * alpha_p_vec_k[:]) + alpha_armijo_c_p_vec_k.assign((-armijo_c * alpha_p_vec_k[:])) @else_ def _(): break_loop() @@ -95,7 +95,6 @@ class L_BFGS_BOptimizer(Optimizer): guess_k = Matrix(len(initial_guess), 1, sfix) guess_k.assign(initial_guess) grad_f_k = Matrix(len(initial_guess), 1, sfix) - grad_f_k.assign(g(guess_k)) prev_guess_k = Matrix(len(initial_guess), 1, sfix) prev_grad_k = Matrix(len(initial_guess), 1, sfix) @@ -105,6 +104,8 @@ class L_BFGS_BOptimizer(Optimizer): @for_range_opt(n_iter) def _(i): + + #for i in range(n_iter): @if_e((self.function.norm(g(guess_k)) > threshold).reveal()) def _(): #print_ln("%s", (self.function.norm(self.function.grad(guess_k)) > threshold).reveal()) @@ -113,12 +114,13 @@ class L_BFGS_BOptimizer(Optimizer): def _(): invH.assign(self._identity()) - grad_f_k = g(guess_k) - tmp_grad_f_k = Matrix(len(grad_f_k), 1, sfix) - tmp_grad_f_k.assign(grad_f_k) - p_vec_k.assign(invH.dot(tmp_grad_f_k)) + invH.print_reveal_nested() + + grad_f_k.assign(g(guess_k)) + p_vec_k.assign(invH.dot(grad_f_k)) self.update_alpha(guess_k, p_vec_k, rho, alpha, armijo_c, c_2, n_iter=10, fn=fn, g=g) + #print_ln("alpha: %s", alpha.reveal()) prev_guess_k.assign(guess_k) tmp = Matrix(self.dimension, 1, sfix) @@ -263,7 +265,7 @@ class LinearNonnegative: def norm(self, x): s = sum(self.constants) - norm = mpc_math.sqrt(s) + norm = mpc_math.sqrt(sfix(s)) return norm """ diff --git a/router.mpc b/router.mpc index 545d57c..317722e 100644 --- a/router.mpc +++ b/router.mpc @@ -14,7 +14,7 @@ class ArbitrageRouter: """ cfmms: List[CFMM] - n_tokens: sint + n_tokens: sfix tendered_assets: List[List[sfix]] received_assets: List[List[sfix]] price_vector: List[sfix] @@ -38,6 +38,8 @@ class ArbitrageRouter: #print("from router.find_arb") #print(type(price_vector)) cfmm.find_arb(self.big_delta[i], self.big_lambda[i], price_vector) + #self.big_delta.print_reveal_nested() + #self.big_lambda.print_reveal_nested() def route(self, optimizer, price_vector=None): @@ -48,6 +50,8 @@ class ArbitrageRouter: #print("from router.route") #print(type(self.v)) + #price_vector.print_reveal_nested() + #NB: Might not need this as it's related to an implementation detail related to Julia's LBFGSB implementation. #NB: Maybe there might be a way to leverage this to make the computation faster. To be explored later. @@ -113,7 +117,7 @@ class ArbitrageRouter: self.find_arb(self.v) - new_price_vector = optimizer.optimize(self.v, fn, g)[0] + new_price_vector = optimizer.optimize(self.v, fn, g, n_iter=2)[0] #print_ln("router.v: %s", new_price_vector.reveal()) self.v.assign(new_price_vector) #print("from router.route")