synor/sdk/python/synor_compute/tensor.py
Gulshan Yadav a808bb37a6 feat(sdk): add consumer SDKs for JavaScript, Python, and Go
Add complete SDK implementations for accessing Synor Compute:

JavaScript/TypeScript SDK (sdk/js/):
- Full async/await API with TypeScript types
- Tensor operations: matmul, conv2d, attention
- Model inference with streaming support
- WebSocket-based job monitoring
- Browser and Node.js compatible

Python SDK (sdk/python/):
- Async/await with aiohttp
- NumPy integration for tensors
- Context managers for cleanup
- Type hints throughout
- PyPI-ready package structure

Go SDK (sdk/go/):
- Idiomatic Go with context support
- Efficient binary tensor serialization
- HTTP client with configurable timeouts
- Zero external dependencies (stdlib only)

All SDKs support:
- Matrix multiplication (FP64 to INT4 precision)
- Convolution operations (2D, 3D)
- Flash attention
- LLM inference
- Spot pricing queries
- Job polling and cancellation
- Heterogeneous compute targeting (CPU/GPU/TPU/NPU/LPU)
2026-01-11 14:11:58 +05:30

153 lines
4.8 KiB
Python

"""Tensor utilities for Synor Compute SDK."""
from typing import Any
import base64
import numpy as np
from .types import Precision
class Tensor:
"""
Tensor wrapper for compute operations.
Example:
>>> tensor = Tensor.from_numpy(np.random.randn(1024, 1024))
>>> tensor = Tensor.zeros((1024, 1024), dtype=Precision.FP16)
"""
def __init__(
self,
data: np.ndarray,
dtype: Precision = Precision.FP32,
):
self._data = data
self._dtype = dtype
@classmethod
def from_numpy(cls, array: np.ndarray, dtype: Precision | None = None) -> "Tensor":
"""Create a tensor from a numpy array."""
if dtype is None:
dtype = cls._infer_dtype(array.dtype)
return cls(array, dtype)
@classmethod
def zeros(cls, shape: tuple[int, ...], dtype: Precision = Precision.FP32) -> "Tensor":
"""Create a tensor of zeros."""
np_dtype = cls._to_numpy_dtype(dtype)
return cls(np.zeros(shape, dtype=np_dtype), dtype)
@classmethod
def ones(cls, shape: tuple[int, ...], dtype: Precision = Precision.FP32) -> "Tensor":
"""Create a tensor of ones."""
np_dtype = cls._to_numpy_dtype(dtype)
return cls(np.ones(shape, dtype=np_dtype), dtype)
@classmethod
def random(cls, shape: tuple[int, ...], dtype: Precision = Precision.FP32) -> "Tensor":
"""Create a tensor with random values."""
np_dtype = cls._to_numpy_dtype(dtype)
return cls(np.random.random(shape).astype(np_dtype), dtype)
@classmethod
def randn(cls, shape: tuple[int, ...], dtype: Precision = Precision.FP32) -> "Tensor":
"""Create a tensor with normal distribution."""
np_dtype = cls._to_numpy_dtype(dtype)
return cls(np.random.randn(*shape).astype(np_dtype), dtype)
@property
def data(self) -> np.ndarray:
"""Get the underlying numpy array."""
return self._data
@property
def shape(self) -> tuple[int, ...]:
"""Get tensor shape."""
return self._data.shape
@property
def dtype(self) -> Precision:
"""Get tensor dtype."""
return self._dtype
@property
def size(self) -> int:
"""Get total number of elements."""
return self._data.size
@property
def ndim(self) -> int:
"""Get number of dimensions."""
return self._data.ndim
@property
def nbytes(self) -> int:
"""Get byte size."""
return self._data.nbytes
def reshape(self, shape: tuple[int, ...]) -> "Tensor":
"""Reshape tensor."""
return Tensor(self._data.reshape(shape), self._dtype)
def to(self, dtype: Precision) -> "Tensor":
"""Convert to different precision."""
if dtype == self._dtype:
return self
np_dtype = self._to_numpy_dtype(dtype)
return Tensor(self._data.astype(np_dtype), dtype)
def numpy(self) -> np.ndarray:
"""Convert to numpy array."""
return self._data
def serialize(self) -> dict[str, Any]:
"""Serialize tensor for transmission."""
data_bytes = self._data.tobytes()
data_b64 = base64.b64encode(data_bytes).decode("ascii")
return {
"data": data_b64,
"shape": list(self._data.shape),
"dtype": self._dtype.value,
}
@classmethod
def deserialize(cls, data: dict[str, Any]) -> "Tensor":
"""Deserialize tensor from transmission format."""
data_bytes = base64.b64decode(data["data"])
dtype = Precision(data["dtype"])
np_dtype = cls._to_numpy_dtype(dtype)
array = np.frombuffer(data_bytes, dtype=np_dtype).reshape(data["shape"])
return cls(array, dtype)
@staticmethod
def _infer_dtype(np_dtype: np.dtype) -> Precision:
"""Infer Precision from numpy dtype."""
if np_dtype == np.float64:
return Precision.FP64
elif np_dtype == np.float32:
return Precision.FP32
elif np_dtype == np.float16:
return Precision.FP16
elif np_dtype == np.int8:
return Precision.INT8
else:
return Precision.FP32
@staticmethod
def _to_numpy_dtype(dtype: Precision) -> np.dtype:
"""Convert Precision to numpy dtype."""
mapping = {
Precision.FP64: np.float64,
Precision.FP32: np.float32,
Precision.FP16: np.float16,
Precision.BF16: np.float16, # Approximate
Precision.INT8: np.int8,
Precision.INT4: np.int8, # Approximate
}
return np.dtype(mapping.get(dtype, np.float32))
def __repr__(self) -> str:
return f"Tensor(shape={self.shape}, dtype={self._dtype.value})"
def __str__(self) -> str:
return f"Tensor({self._data}, dtype={self._dtype.value})"