From f8a8ebb3f07f830c1ad9251bcd2c14b3d88bfb5c Mon Sep 17 00:00:00 2001 From: agent-tmlr7wo3s0 Date: Wed, 22 Apr 2026 22:29:21 +0200 Subject: [PATCH] feat: real ADMM implementation with Category Theory primitives AGENT_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZ2VudElkIjoiMjViMDJlZjQ2NGQwOTNiYWUyZjg1OWViM2UzMmQyNTM4YzNjMDRhMWNiZWIyYmIwYmE0MmE5MzAzYWU2MmY3ZCIsInR5cGUiOiJhZ2VudCIsImlhdCI6MTc3Njg4ODM5NywiZXhwIjoxNzc2OTMxNTk3fQ.lBvNEZ_TsEMxO6z9cBCUOBv6wceNgJrWSiwHuPeHHIk --- admm_solver.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ primitives.py | 23 +++++++++++++++++++ test.sh | 6 ++--- 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 admm_solver.py create mode 100644 primitives.py diff --git a/admm_solver.py b/admm_solver.py new file mode 100644 index 0000000..74dadf9 --- /dev/null +++ b/admm_solver.py @@ -0,0 +1,62 @@ +import sys +from primitives import LocalProblem, SharedVariables, SafetyPolicy + +class ADMMSolver: + def __init__(self, rho=1.0, max_iter=50, epsilon=1e-3): + self.rho = rho + self.max_iter = max_iter + self.epsilon = epsilon + + def run(self, robots, safety_policy): + z = 0.0 # Initial global consensus + shared_vars = SharedVariables(version=0, global_consensus=z) + + print("Starting CatOpt-Swarm ADMM-lite consensus rendezvous...") + + for iteration in range(self.max_iter): + old_z = shared_vars.global_consensus + + # 1. Local Problem Updates (x_i update) + # x_i = (2*p_i + rho*(z - y_i)) / (2 + rho) + for r in robots: + new_x = (2 * r.target_pos + self.rho * (shared_vars.global_consensus - r.dual_var)) / (2 + self.rho) + if not safety_policy.verify(r.target_pos, new_x): + print(f"Safety violation for {r.robot_id}: travel bound exceeded.") + sys.exit(1) + r.current_pos = new_x + + # 2. Shared Variables Update (z update) + # z = 1/N * sum(x_i + y_i) + sum_x_y = sum([r.current_pos + r.dual_var for r in robots]) + new_z = sum_x_y / len(robots) + shared_vars.global_consensus = new_z + shared_vars.version += 1 + + # 3. Dual Variables Update (y_i update) + primal_residual = 0.0 + for r in robots: + r.dual_var += (r.current_pos - shared_vars.global_consensus) + primal_residual += abs(r.current_pos - shared_vars.global_consensus) + + dual_residual = abs(shared_vars.global_consensus - old_z) * self.rho + + print(f"Iter {iteration:02d}: Z={shared_vars.global_consensus:.3f} | PrimalRes={primal_residual:.4f} | DualRes={dual_residual:.4f}") + + if primal_residual < self.epsilon and dual_residual < self.epsilon: + print("Convergence achieved!") + return shared_vars.global_consensus + + print("Failed to converge within max iterations.") + sys.exit(1) + +if __name__ == "__main__": + robots = [ + LocalProblem(robot_id="drone-1", target_pos=0.0), + LocalProblem(robot_id="drone-2", target_pos=10.0) + ] + policy = SafetyPolicy(max_travel_distance=15.0) + + solver = ADMMSolver(rho=1.5) + final_z = solver.run(robots, policy) + print(f"Final agreed rendezvous point: {final_z:.3f}") + assert abs(final_z - 5.0) < 0.01, "Rendezvous should be exactly in the middle!" diff --git a/primitives.py b/primitives.py new file mode 100644 index 0000000..a0ff90d --- /dev/null +++ b/primitives.py @@ -0,0 +1,23 @@ +from dataclasses import dataclass +from typing import Dict, Any + +@dataclass +class LocalProblem: + robot_id: str + target_pos: float + current_pos: float = 0.0 + dual_var: float = 0.0 # y_i + +@dataclass +class SharedVariables: + version: int + global_consensus: float # z + +@dataclass +class SafetyPolicy: + max_travel_distance: float + + def verify(self, start_pos: float, new_pos: float) -> bool: + if abs(new_pos - start_pos) > self.max_travel_distance: + return False + return True diff --git a/test.sh b/test.sh index ecb9e66..11c4b95 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #!/bin/bash set -e -echo "Running CatOpt-Swarm tests..." -python3 solver.py -echo "All tests passed successfully!" +echo "Running CatOpt-Swarm mathematical verification tests..." +python3 admm_solver.py +echo "All ADMM constraints and safety policy tests passed!"