atakan
fix: Enforce parameter provenance, fix rendering/citations, add tools, harden agent loop
48ee375
Raw
History Blame Contribute Delete
4.96 kB
"""Deterministic linear-algebra tool: the basic matrix operations every other
analysis builds on. Direct requests like "A times B", "invert this matrix", or
"eigenvalues of A" must never depend on model-written freehand code -- they get
one schema-validated tool call."""
from __future__ import annotations
from typing import Any
import numpy as np
from controlai_agent.registry import registry
@registry.register(
name="matrix_arithmetic",
description=(
"Exact matrix arithmetic on one or two numeric matrices. THE tool for any direct matrix "
"computation the user states explicitly: 'A times B' / 'A*B' (operation='multiply'), "
"addition, subtraction, transpose, inverse, determinant, rank, trace, eigenvalues, or "
"elementwise multiply. Use this instead of writing Python code for plain matrix math. "
"This is NOT a controller-design tool: a request to multiply or invert matrices is a "
"linear-algebra question, not an LQR/pole-placement problem."
),
parameters_schema={
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": [
"multiply",
"add",
"subtract",
"elementwise_multiply",
"transpose",
"inverse",
"determinant",
"rank",
"trace",
"eigenvalues",
],
"description": "Which operation to perform. Binary operations use matrix_a (op) matrix_b in that order.",
},
"matrix_a": {
"type": "array",
"items": {"type": "array", "items": {"type": "number"}},
"description": "First matrix (2D, row-major). For unary operations this is the only operand.",
},
"matrix_b": {
"type": "array",
"items": {"type": "array", "items": {"type": "number"}},
"description": "Second matrix, required for multiply / add / subtract / elementwise_multiply.",
},
},
"required": ["operation", "matrix_a"],
},
)
def matrix_arithmetic(
operation: str,
matrix_a: list[list[float]],
matrix_b: list[list[float]] | None = None,
) -> dict[str, Any]:
A = np.array(matrix_a, dtype=float)
binary_ops = {"multiply", "add", "subtract", "elementwise_multiply"}
if operation in binary_ops:
if matrix_b is None:
return {"status": "error", "error": f"operation '{operation}' requires matrix_b."}
B = np.array(matrix_b, dtype=float)
if operation == "multiply":
if A.shape[1] != B.shape[0]:
return {
"status": "error",
"error": f"Cannot multiply: matrix_a is {A.shape[0]}x{A.shape[1]} but matrix_b is {B.shape[0]}x{B.shape[1]} (inner dimensions must match).",
}
result = A @ B
elif operation == "elementwise_multiply":
if A.shape != B.shape:
return {"status": "error", "error": f"Elementwise multiply needs equal shapes, got {A.shape} and {B.shape}."}
result = A * B
else:
if A.shape != B.shape:
return {"status": "error", "error": f"'{operation}' needs equal shapes, got {A.shape} and {B.shape}."}
result = A + B if operation == "add" else A - B
return {
"operation": operation,
"shape_a": list(A.shape),
"shape_b": list(B.shape),
"result": result.tolist(),
"result_shape": list(result.shape),
}
# Unary operations
out: dict[str, Any] = {"operation": operation, "shape_a": list(A.shape)}
if operation == "transpose":
out["result"] = A.T.tolist()
elif operation == "rank":
out["result"] = int(np.linalg.matrix_rank(A))
elif operation in ("inverse", "determinant", "trace", "eigenvalues"):
if A.shape[0] != A.shape[1]:
return {"status": "error", "error": f"'{operation}' requires a square matrix, got {A.shape[0]}x{A.shape[1]}."}
if operation == "inverse":
det = float(np.linalg.det(A))
if abs(det) < 1e-12:
return {"status": "error", "error": f"Matrix is singular (determinant = {det:g}); no inverse exists."}
out["result"] = np.linalg.inv(A).tolist()
out["determinant"] = det
elif operation == "determinant":
out["result"] = float(np.linalg.det(A))
elif operation == "trace":
out["result"] = float(np.trace(A))
else: # eigenvalues
eig = np.linalg.eigvals(A)
out["result"] = [[float(v.real), float(v.imag)] for v in eig]
out["spectral_radius"] = float(np.max(np.abs(eig)))
return out