Spaces:
Running on Zero
Running on Zero
File size: 4,955 Bytes
48ee375 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | """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
|