Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions python/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ void bind_tensor_creation_functions(pybind11::module &m) {
bind_tensor_creation_overloads(m, "zeros", ZerosFunctor{});
bind_tensor_creation_overloads(m, "ones", OnesFunctor{});
bind_tensor_creation_overloads(m, "empty", EmptyFunctor{});

m.def("full_like", &smollnet::full_like, "tensor"_a, "value"_a,
"requires_grad"_a = false);
m.def("zeros_like", &smollnet::zeros_like, "tensor"_a,
"requires_grad"_a = false);
m.def("ones_like", &smollnet::ones_like, "tensor"_a,
"requires_grad"_a = false);
m.def("rand_like", &smollnet::rand_like, "tensor"_a,
"requires_grad"_a = false);
}

// Single module definition
Expand Down
47 changes: 43 additions & 4 deletions src/tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <random>

#include <cuda_runtime.h>

Expand All @@ -14,6 +15,16 @@

namespace smollnet {

namespace {

std::mt19937 &cpu_random_generator() {
// NOLINTNEXTLINE
static std::mt19937 generator(1234U);
return generator;
}

} // namespace

template <typename GradF>
void SetupAutograd(const Tensor &l, const Tensor &r, const Tensor &n) {
if (n.requires_grad()) {
Expand All @@ -34,8 +45,9 @@ void SetupAutograd(const Tensor &n, const Tensor &other) {
}
}

Tensor full_like(const Tensor &t, float value) {
Tensor out = empty(t.dims().data(), t.ndims(), t.dtype(), t.device());
Tensor full_like(const Tensor &t, float value, bool requires_grad) {
Tensor out = empty(t.dims().data(), t.ndims(), t.dtype(), t.device(),
requires_grad);
if (t.device() == Device::CUDA) {
launch_fill(static_cast<float *>(out.data()), out.numel(), value);
} else {
Expand Down Expand Up @@ -862,22 +874,49 @@ Tensor ones(const int64_t *dims, size_t rank, DataType t, Device d,
bool requires_grad) {
auto tensor = empty(dims, rank, t, d, requires_grad);

launch_fill(static_cast<float *>(tensor.data()), tensor.numel(), 1.0f);
if (d == Device::CUDA) {
launch_fill(static_cast<float *>(tensor.data()), tensor.numel(), 1.0f);
} else {
std::fill_n(static_cast<float *>(tensor.data()), tensor.numel(), 1.0f);
}

return Tensor{tensor};
}

void manual_seed(unsigned long long seed) {
cpu_random_generator().seed(static_cast<std::mt19937::result_type>(seed));
launch_random_init(seed);
}

Tensor rand(const int64_t *dims, size_t rank, DataType t, Device d,
bool requires_grad) {
auto tensor = empty(dims, rank, t, d, requires_grad);

launch_random_fill(tensor.data(), tensor.numel());
if (d == Device::CUDA) {
launch_random_fill(tensor.data(), tensor.numel());
} else {
auto *data = static_cast<float *>(tensor.data());
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
auto &generator = cpu_random_generator();
for (size_t i = 0; i < tensor.numel(); ++i) {
data[i] = dist(generator);
}
}

return Tensor{tensor};
}

Tensor zeros_like(const Tensor &t, bool requires_grad) {
return zeros(t.dims().data(), t.ndims(), t.dtype(), t.device(),
requires_grad);
}

Tensor ones_like(const Tensor &t, bool requires_grad) {
return full_like(t, 1.0f, requires_grad);
}

Tensor rand_like(const Tensor &t, bool requires_grad) {
return rand(t.dims().data(), t.ndims(), t.dtype(), t.device(), requires_grad);
}

} // namespace smollnet
4 changes: 4 additions & 0 deletions src/tensor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ Tensor ones(const int64_t *dims, size_t rank, DataType t, Device d,
bool requires_grad = false);
Tensor rand(const int64_t *dims, size_t rank, DataType t, Device d,
bool requires_grad = false);
Tensor full_like(const Tensor &t, float value, bool requires_grad = false);
Tensor zeros_like(const Tensor &t, bool requires_grad = false);
Tensor ones_like(const Tensor &t, bool requires_grad = false);
Tensor rand_like(const Tensor &t, bool requires_grad = false);
void manual_seed(unsigned long long seed);

template <size_t N>
Expand Down
46 changes: 46 additions & 0 deletions utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ def test_tensor_creation():
print(f"2D ones shape: {t2d_ones.dims()}")
print(f"2D rand shape: {t2d_rand.dims()}")

print("\nCreating like tensors...")
t2d_full_like = smollnet.full_like(t2d_rand, 3.0)
t2d_zeros_like = smollnet.zeros_like(t2d_rand)
t2d_ones_like = smollnet.ones_like(t2d_rand, requires_grad=True)
t2d_rand_like = smollnet.rand_like(t2d_rand)

assert t2d_full_like.dims() == t2d_rand.dims()
assert t2d_zeros_like.dims() == t2d_rand.dims()
assert t2d_ones_like.dims() == t2d_rand.dims()
assert t2d_rand_like.dims() == t2d_rand.dims()
assert t2d_full_like.dtype() == t2d_rand.dtype()
assert t2d_zeros_like.dtype() == t2d_rand.dtype()
assert t2d_ones_like.device() == t2d_rand.device()
assert not t2d_zeros_like.requires_grad()
assert t2d_ones_like.requires_grad()

print(f"2D full_like shape: {t2d_full_like.dims()}")
print(f"2D zeros_like shape: {t2d_zeros_like.dims()}")
print(f"2D ones_like shape: {t2d_ones_like.dims()}")
print(f"2D rand_like shape: {t2d_rand_like.dims()}")

# Test 3D tensors
print("\nCreating 3D tensors...")
t3d_zeros = smollnet.zeros(2, 3, 4)
Expand All @@ -52,6 +73,30 @@ def test_tensor_creation():

print("✓ Tensor creation tests passed!\n")

def test_manual_seed():
"""Test that manual_seed changes and resets CPU random state."""
print("=== Testing Manual Seed ===")

reference = smollnet.zeros(2, 3, device=smollnet.Device.CPU)

smollnet.manual_seed(2025)
first = smollnet.rand_like(reference)
second = smollnet.rand_like(reference)

smollnet.manual_seed(2025)
first_replay = smollnet.rand_like(reference)
second_replay = smollnet.rand_like(reference)

assert repr(first) == repr(first_replay)
assert repr(second) == repr(second_replay)

smollnet.manual_seed(2026)
different_seed = smollnet.rand_like(reference)

assert repr(first) != repr(different_seed)

print("✓ Manual seed tests passed!\n")

def test_tensor_operations():
"""Test basic tensor operations."""
print("=== Testing Tensor Operations ===")
Expand Down Expand Up @@ -312,6 +357,7 @@ def main():

try:
test_tensor_creation()
test_manual_seed()
test_tensor_operations()
test_activation_functions()
test_neural_network_layers()
Expand Down
Loading