ProCreations's picture
Publish six-claim native-scale FFOLayer reproduction
9860743 verified
Raw
History Blame Contribute Delete
4.09 kB
import cvxpy as cp
import time
import numpy as np
def get_solver_stats(problem):
"""Get solver stats for both OSQP and SCS robustly."""
stats = problem.solver_stats.extra_stats
return stats
def get_solver_stats(problem, solver_name, extra_stats=False):
if solver_name=="SCS":
if extra_stats:
stats = problem.solver_stats.extra_stats
solve_time = stats["info"]["solve_time"]
setup_time = stats["info"]["setup_time"]
num_iters = problem.solver_stats.num_iters
return setup_time, solve_time, num_iters
stats = problem.solver_stats
solve_time = stats.solve_time
setup_time = stats.setup_time
num_iters = stats.num_iters
return setup_time, solve_time, num_iters
elif solver_name=="OSQP":
if extra_stats:
stats = problem.solver_stats.extra_stats
solve_time = stats.info.solve_time
setup_time = stats.info.setup_time
num_iters = problem.solver_stats.num_iters
return setup_time, solve_time, num_iters
stats = problem.solver_stats
solve_time = stats.solve_time
setup_time = stats.extra_stats.info.setup_time
num_iters = stats.num_iters
return setup_time, solve_time, num_iters
# -------------------------
# Problem dimensions
extra_stats = False
verbose = False
solver_name = "SCS"
if solver_name=="SCS":
solver = cp.SCS
elif solver_name=="OSQP":
solver = cp.OSQP
n = 1000
m = 2*n + 1
print(f"dim: {n}, num inequalities: {m}")
Q = np.eye(n)
q = np.random.randn(n)
G = np.vstack([np.eye(n), -np.eye(n), np.ones((1, n))])
h = np.hstack([np.zeros(n), np.ones(n), np.array([3])])
# Forward pass
x = cp.Variable(n)
objective = 0.5 * cp.quad_form(x, Q) + q.T @ x
constraints = [G @ x <= h]
# Canonicalization time
t0 = time.perf_counter()
problem_forward = cp.Problem(cp.Minimize(objective), constraints)
t1 = time.perf_counter()
canonicalization_time_forward = t1 - t0
print("Forward pass canonicalization time:", canonicalization_time_forward, "s")
t0 = time.perf_counter()
problem_forward.solve(solver=solver, warm_start=False, verbose=verbose)
t1 = time.perf_counter()
total_time_scs = t1 - t0
solver_stats = get_solver_stats(problem_forward, solver_name=solver_name, extra_stats=extra_stats)
print(f"\nForward pass {solver_name}:")
print(f"Total wall-clock: {total_time_scs:.6f} s")
print("Solver setup_time:", solver_stats[0])
print("Solver solve_time:", solver_stats[1])
print("Iterations:", solver_stats[2])
# -------------------------
# Backward pass: convert k active inequalities to equalities
ineq_dual = constraints[0].dual_value
g_val = constraints[0].expr.value
s_val = -g_val
s_val = np.maximum(s_val, 0.0)
ineq_slack_residual = s_val
active_mask = ineq_slack_residual < 1e-6
num_active = np.sum(active_mask)
print(f"\nNumber of active inequalities: {num_active} out of {m}")
np.save("../active_mask.npy", active_mask)
# active_mask = np.load("../active_mask.npy")
A_eq = G[active_mask, :]
b_eq = h[active_mask]
# k = (m-1)//2 # number of active inequalities
# A_eq = G[:k, :]
# b_eq = h[:k]
x_bwd = cp.Variable(n)
objective_bwd = 0.5 * cp.quad_form(x_bwd, Q) + q.T @ x_bwd
constraints_bwd = [A_eq @ x_bwd == b_eq]
# Canonicalization
t0 = time.perf_counter()
problem_backward = cp.Problem(cp.Minimize(objective_bwd), constraints_bwd)
t1 = time.perf_counter()
canonicalization_time_backward = t1 - t0
print("\nBackward pass canonicalization time:", canonicalization_time_backward, "s")
# Solve backward with OSQP
t0 = time.perf_counter()
problem_backward.solve(solver=solver, warm_start=False, verbose=verbose)
t1 = time.perf_counter()
osqp_stats_bwd = get_solver_stats(problem_backward, solver_name=solver_name, extra_stats=extra_stats)
total_time_osqp_bwd = t1 - t0
print(f"\nBackward pass {solver_name}:")
print(f"Total wall-clock: {total_time_osqp_bwd:.6f} s")
print("Solver setup_time:", osqp_stats_bwd[0])
print("Solver solve_time:", osqp_stats_bwd[1])
print("Iterations:", osqp_stats_bwd[2])