build(agent): r2d2#deee02 iteration
This commit is contained in:
parent
acf4400d47
commit
9a7ff97e12
|
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules/
|
||||||
|
.npmrc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__tests__/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
tmp/
|
||||||
|
.tmp/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
READY_TO_PUBLISH
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
Repository: CatOpt-Play (prototype)
|
||||||
|
|
||||||
|
Architecture
|
||||||
|
- Language: Python 3.8+
|
||||||
|
- Layout: src/ package layout (src/idea36_catopt_play_category)
|
||||||
|
- Core components:
|
||||||
|
- contracts: pydantic models for LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog
|
||||||
|
- solver: an ADMM-lite consensus solver (prototype)
|
||||||
|
|
||||||
|
Tech stack
|
||||||
|
- Python with pydantic for data contracts
|
||||||
|
- pytest for tests
|
||||||
|
- setuptools/pyproject for packaging
|
||||||
|
|
||||||
|
Testing & Commands
|
||||||
|
- Run tests: `pytest`
|
||||||
|
- Build package: `python3 -m build`
|
||||||
|
- Full automation: `bash test.sh` (installs build+pytest in the environment, installs package editable, runs tests, then builds)
|
||||||
|
|
||||||
|
Rules for AI agents and contributors
|
||||||
|
- Make minimal, well-scoped edits. Prefer small changes.
|
||||||
|
- Follow src/ layout and put package code under `src/idea36_catopt_play_category`.
|
||||||
|
- Add tests for new behaviour. CI expects `pytest` to pass.
|
||||||
|
- Do not create READY_TO_PUBLISH unless the full original spec is implemented and tests pass.
|
||||||
18
README.md
18
README.md
|
|
@ -1,3 +1,17 @@
|
||||||
# idea36-catopt-play-category
|
# CatOpt-Play (prototype)
|
||||||
|
|
||||||
Source logic for Idea #36
|
This repository contains a Python prototype for CatOpt-Play — a category-theory-inspired compositional optimizer for distributed multi-agent coordination. The goal of this prototype is to provide a canonical IR for local problems and data contracts, plus a small ADMM-lite solver demonstrating distributed consensus and delta-style plan deltas.
|
||||||
|
|
||||||
|
Contents
|
||||||
|
- src/idea36_catopt_play_category: core library (contracts, solver)
|
||||||
|
- tests: basic tests for solver convergence and schema generation
|
||||||
|
- AGENTS.md: repository architecture and contribution rules
|
||||||
|
- test.sh: runs tests and builds the package
|
||||||
|
|
||||||
|
Quickstart
|
||||||
|
1. Install dev tools: `pip install -U build pytest`
|
||||||
|
2. Install package in editable mode: `pip install -e .`
|
||||||
|
3. Run tests: `pytest`
|
||||||
|
4. Build distribution: `python3 -m build`
|
||||||
|
|
||||||
|
This prototype focuses on a small, well-tested chunk: the canonical data contracts and an ADMM-lite consensus solver. It is intentionally minimal and designed to be extended with engine adapters, transports, and governance ledgers in follow-up work.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61.0", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
[metadata]
|
||||||
|
name = idea36-catopt-play-category
|
||||||
|
version = 0.1.0
|
||||||
|
description = CatOpt-Play: Category-Theoretic Compositional Optimizer (prototype)
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
author = OpenCode
|
||||||
|
license = MIT
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir =
|
||||||
|
= src
|
||||||
|
packages = find:
|
||||||
|
python_requires = >=3.8
|
||||||
|
install_requires =
|
||||||
|
pydantic>=1.10
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
"""CatOpt-Play prototype package."""
|
||||||
|
from . import contracts, solver
|
||||||
|
|
||||||
|
__all__ = ["contracts", "solver"]
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class LocalProblem(BaseModel):
|
||||||
|
"""Canonical representation of a local agent planning problem.
|
||||||
|
|
||||||
|
For the prototype we model simple quadratic objectives with coefficients
|
||||||
|
so the ADMM updates can be computed analytically in tests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
# objective: 0.5 * a * x^2 + b * x
|
||||||
|
a: float = Field(..., description="Quadratic coefficient (>=0)")
|
||||||
|
b: float = Field(..., description="Linear coefficient")
|
||||||
|
constraints: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SharedVariables(BaseModel):
|
||||||
|
values: Dict[str, float]
|
||||||
|
version: str
|
||||||
|
timestamp: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class DualVariables(BaseModel):
|
||||||
|
values: Dict[str, float]
|
||||||
|
version: str
|
||||||
|
timestamp: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class PlanDelta(BaseModel):
|
||||||
|
agent_id: str
|
||||||
|
delta: Dict[str, float]
|
||||||
|
version: str
|
||||||
|
timestamp: datetime
|
||||||
|
nonce: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class PrivacyBudget(BaseModel):
|
||||||
|
remaining: float
|
||||||
|
used: float = 0.0
|
||||||
|
budget_id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class AuditLog(BaseModel):
|
||||||
|
event_id: str
|
||||||
|
actor: str
|
||||||
|
action: str
|
||||||
|
details: Optional[Dict[str, Any]] = None
|
||||||
|
timestamp: datetime
|
||||||
|
|
||||||
|
|
||||||
|
def export_json_schemas() -> Dict[str, Any]:
|
||||||
|
"""Return JSON schemas for canonical contracts."""
|
||||||
|
models = [LocalProblem, SharedVariables, DualVariables, PlanDelta, PrivacyBudget, AuditLog]
|
||||||
|
return {m.__name__: m.schema() for m in models}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
from typing import List, Dict
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
def admm_consensus(local_problems: List[Dict[str, float]], rho: float = 1.0, max_iter: int = 200, tol: float = 1e-4):
|
||||||
|
"""
|
||||||
|
Simple ADMM consensus solver for scalar variables.
|
||||||
|
|
||||||
|
local_problems: list of dicts with keys 'a' and 'b' representing local objective
|
||||||
|
0.5 * a * x^2 + b * x
|
||||||
|
|
||||||
|
Returns tuple (z, history) where z is consensus variable and history a list of z over iterations.
|
||||||
|
"""
|
||||||
|
n = len(local_problems)
|
||||||
|
# initialize
|
||||||
|
x = [0.0 for _ in range(n)]
|
||||||
|
u = [0.0 for _ in range(n)]
|
||||||
|
z = 0.0
|
||||||
|
history = []
|
||||||
|
|
||||||
|
for k in range(max_iter):
|
||||||
|
# x-update (closed-form for quadratic)
|
||||||
|
for i, p in enumerate(local_problems):
|
||||||
|
a = p.get("a", 0.0)
|
||||||
|
b = p.get("b", 0.0)
|
||||||
|
denom = a + rho
|
||||||
|
# minimize 0.5*a*x^2 + b*x + (rho/2)*(x - z + u)^2
|
||||||
|
x[i] = (-b + rho * (z - u[i])) / denom
|
||||||
|
|
||||||
|
# z-update: average of x + u
|
||||||
|
z_old = z
|
||||||
|
z = sum(x[i] + u[i] for i in range(n)) / n
|
||||||
|
|
||||||
|
# u-update
|
||||||
|
for i in range(n):
|
||||||
|
u[i] = u[i] + x[i] - z
|
||||||
|
|
||||||
|
history.append(z)
|
||||||
|
|
||||||
|
# check convergence (primal residual)
|
||||||
|
r_norm = math.sqrt(sum((x[i] - z) ** 2 for i in range(n)))
|
||||||
|
s_norm = math.sqrt(n) * abs(rho * (z - z_old))
|
||||||
|
if r_norm < tol and s_norm < tol:
|
||||||
|
break
|
||||||
|
|
||||||
|
return z, history
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Installing dev dependencies (build, pytest)..."
|
||||||
|
pip install -U build pytest >/dev/null
|
||||||
|
|
||||||
|
echo "Installing package in editable mode..."
|
||||||
|
pip install -e . >/dev/null
|
||||||
|
|
||||||
|
echo "Running pytest..."
|
||||||
|
pytest -q
|
||||||
|
|
||||||
|
echo "Building distribution..."
|
||||||
|
python3 -m build
|
||||||
|
|
||||||
|
echo "All done."
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from idea36_catopt_play_category import contracts
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_schemas_contains_models():
|
||||||
|
schemas = contracts.export_json_schemas()
|
||||||
|
expected = ["LocalProblem", "PlanDelta", "AuditLog"]
|
||||||
|
for name in expected:
|
||||||
|
assert name in schemas
|
||||||
|
assert isinstance(schemas[name], dict)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from idea36_catopt_play_category.solver import admm_consensus
|
||||||
|
|
||||||
|
|
||||||
|
def test_admm_consensus_converges_to_average():
|
||||||
|
# two agents with objectives 0.5*(x - c)^2 => a=1, b=-c
|
||||||
|
c1 = 2.0
|
||||||
|
c2 = -1.0
|
||||||
|
local = [
|
||||||
|
{"a": 1.0, "b": -c1},
|
||||||
|
{"a": 1.0, "b": -c2},
|
||||||
|
]
|
||||||
|
|
||||||
|
z, history = admm_consensus(local, rho=1.0, max_iter=500, tol=1e-6)
|
||||||
|
# analytic centralized optimum is average of c1 and c2
|
||||||
|
expected = (c1 + c2) / 2.0
|
||||||
|
assert abs(z - expected) < 1e-3
|
||||||
Loading…
Reference in New Issue