Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4afc0a0
Now implementing CUDA backend. WIP.
Ra4ster Sep 5, 2026
3b03e29
See last commit.
Ra4ster Sep 5, 2026
11a6f8d
Implemented CUDABackend.cpp
Ra4ster Sep 5, 2026
545b137
CUDA: Fixing AXPY fails.
Ra4ster Sep 6, 2026
9570137
Replaced deprecated DEEPITY flag
Ra4ster Sep 6, 2026
3d5559a
Now tested: current CUDA implementation evaluates MNIST SimplePCN in …
Ra4ster Sep 6, 2026
f030627
Preparing for CUDA graphs.
Ra4ster Sep 6, 2026
4b991eb
currently fixing sgemm pre-graph errors.
Ra4ster Sep 6, 2026
fcfb71b
Now moving on to DKP-PCN CUDA
Ra4ster Sep 7, 2026
2d56025
Now moved DKP-PCN to CUDA. Next up, training on ImageNet!
Ra4ster Sep 7, 2026
582cf57
Added GELU.
Ra4ster Sep 11, 2026
c31121d
Added CUDA GELU
Ra4ster Sep 11, 2026
8fa31c5
Fixed switch-break
Ra4ster Sep 11, 2026
937246e
Fixed a Gelu bug and updated Relu! The new results are superior to th…
Ra4ster Sep 12, 2026
144f227
Stop tracking generated cache/log files
RA4-JACK Sep 13, 2026
af7a512
Removed pycaches.
RA4-JACK Sep 13, 2026
66b8dce
Fixed some typing issues.
RA4-JACK Sep 13, 2026
68f02c1
Removed venv fragments.
RA4-JACK Sep 13, 2026
063caae
Fixed CMakePresets version.
Ra4ster Sep 13, 2026
8ff1063
Added test case.
Ra4ster Sep 13, 2026
d1519db
Fixed some preset issues.
Ra4ster Sep 14, 2026
d91daa0
Add SimpleConvPCVerify test
Ra4ster Sep 14, 2026
273d0c3
Add gpu preset for this instance
Ra4ster Sep 14, 2026
fce003f
Resolve preset conflict, keep venv-based Python path for this instance
Ra4ster Sep 14, 2026
954f2c0
Added nlohmann support.
Ra4ster Sep 14, 2026
883a442
Removed failing test; it was measuring inadequately.
Ra4ster Sep 14, 2026
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode/*
.venv/*
build/
bin/
__pycache__/*
Expand Down Expand Up @@ -28,3 +29,6 @@ wheelhouse/
dist/
*.egg-info/
_skbuild/
__pycache__/
*.pyc
logs/
36 changes: 31 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ endif()
if(DEEPITY_ENABLE_CUDA)
find_package(CUDAToolkit QUIET)
if(CUDAToolkit_FOUND)
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_ARCHITECTURES 86)
message(STATUS "CUDA support enabled.")
else()
message(STATUS "CUDA toolkit not found. Building CPU-only.")
Expand Down Expand Up @@ -199,10 +203,6 @@ else()
set(DEEPITY_BLAS_RESOLVED FALSE)

if(DEEPITY_USE_MKL)
# Modern, recommended integration: Intel's own MKLConfig.cmake,
# present if oneAPI/MKL is properly installed and sourced (e.g.
# via `source /opt/intel/oneapi/setvars.sh`). Exposes a single
# target (MKL::MKL) with both include dirs and link libraries.
find_package(MKL CONFIG QUIET)
if(MKL_FOUND)
message(STATUS "Using Intel MKL (found via MKLConfig.cmake).")
Expand Down Expand Up @@ -273,8 +273,11 @@ add_library(Deepity
src/GaussSeidelPCNetwork.cpp
src/SimpleConvPCNetwork.cpp
src/DirectKPPCNetwork.cpp
src/ModelIO.cpp
src/StreamAlignedBatcher.cpp
src/backend/Backend.cpp
src/backend/CPUBackend.cpp
src/backend/CUDABackend.cu
src/backend/Tensor.cpp
)

if(DEEPITY_USE_MKL AND DEEPITY_BLAS_RESOLVED)
Expand Down Expand Up @@ -304,6 +307,29 @@ endif()

target_compile_definitions(Deepity PUBLIC SLEEF_STATIC_LIBS)

# --- Optiona: Use Google Benchmark -----------------------------------

include(FetchContent)
FetchContent_Declare(
benchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.9.1
)
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(benchmark)

# --- Tests -----------------------------------------------------------

option(DEEPITY_BUILD_TESTS "Build test/verification executables" ON)

add_executable(ActivationBenchmark tests/tActivations.cpp)
target_link_libraries(ActivationBenchmark PRIVATE Deepity benchmark::benchmark)
set_target_properties(ActivationBenchmark PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

add_test(NAME ActivationBenchmark COMMAND ActivationBenchmark)

# --- Compiler flags -------------------------------------------------

if(MSVC)
Expand Down
21 changes: 20 additions & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 6,
"version": 3,
"configurePresets": [
{
"name": "windows-clang",
Expand Down Expand Up @@ -35,6 +35,25 @@
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "gpu",
"displayName": "Linux GPU (CUDA + Python 3.12)",
"description": "Cloud GPU instance configuration -- Python 3.12 pinned explicitly (avoids Ubuntu 22.04's default python3 resolving to the system's 3.10, which has no nanobind installed and no ABI match with anything built here). CMAKE_CUDA_ARCHITECTURES set for Ampere (compute_86, matches RTX 3090/A4000/etc) -- change to 89 for Ada Lovelace (RTX 4060 Ti/4070/4090), 80 for A100, 90 for H100.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/Release",
"condition": {
"type": "notEquals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"Python_EXECUTABLE": "${sourceDir}/.venv/bin/python3.12",
"DEEPITY_ENABLE_CUDA": "ON",
"CMAKE_CUDA_ARCHITECTURES": "86",
"CMAKE_CUDA_STANDARD": "17"
}
}
]
}
60 changes: 43 additions & 17 deletions bindings/pybinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ namespace
return Deep::tanh;
if (act == "sigmoid")
return Deep::sigmoid;
if (act == "esigmoid")
return Deep::e_sigmoid;
if (act == "relu")
return Deep::relu;
if (act == "gelu")
return Deep::gelu;
if (act == "linear")
return Deep::linear;
return Deep::relu;
Expand All @@ -65,8 +69,12 @@ namespace
return Deep::dTanh;
if (act == "dsigmoid")
return Deep::dSigmoid;
if (act == "d_esigmoid")
return Deep::d_eSigmoid;
if (act == "drelu")
return Deep::dRelu;
if (act == "dgelu")
return Deep::dGelu;
if (act == "dLinear")
return Deep::dLinear;
return Deep::dRelu;
Expand All @@ -82,12 +90,18 @@ namespace
return Deep::ActivationType::RELU;
if (act == "drelu")
return Deep::ActivationType::dRELU;
if (act == "gelu")
return Deep::ActivationType::GELU;
if (act == "dgelu")
return Deep::ActivationType::dGELU;
if (act == "sigmoid")
return Deep::ActivationType::SIGMOID;
if (act == "dsigmoid")
return Deep::ActivationType::dSIGMOID;
if (act == "esigmoid")
return Deep::ActivationType::eSIGMOID;
if (act == "d_esigmoid")
return Deep::ActivationType::d_eSIGMOID;
if (act == "dlinear")
return Deep::ActivationType::dLINEAR;
return Deep::ActivationType::LINEAR;
Expand Down Expand Up @@ -136,7 +150,7 @@ namespace
template <typename LayerT>
void BindCommonPCLayer(nb::class_<LayerT, Deep::Layer> &cls, const char *className)
{
cls.def("calculate_state", &LayerT::CalculateState)
cls.def("calculate_state", static_cast<float (LayerT::*)() noexcept>(&LayerT::CalculateState))
.def("update_state", &LayerT::UpdateState)
.def("update_weights", &LayerT::UpdateWeights)
.def("flush", &LayerT::Flush)
Expand Down Expand Up @@ -420,30 +434,39 @@ void bind_networks(nb::module_ &m)

auto simpleNetCls = nb::class_<Deep::SimplePCNetwork>(m, "SimplePCNetwork", "Predictive Coding Network built from SimplePCLayers.");
BindCommonPCNetwork<Deep::SimplePCNetwork>(simpleNetCls, "SimplePCNetwork");
simpleNetCls.def("__init__", [](Deep::SimplePCNetwork *self, int batch_size, const std::string &device)
{
Deep::DeviceType dt = (device == "cuda" || device == "gpu")
? Deep::DeviceType::DEVICE_GPU
: Deep::DeviceType::DEVICE_CPU;
new (self) Deep::SimplePCNetwork(batch_size, dt); }, nb::arg("batch_size"), nb::arg("device") = "cpu", "Construct a network with a fixed batch size and device (\"cpu\" or \"cuda\"/\"gpu\").");
simpleNetCls.def("add_layer", [](Deep::SimplePCNetwork &self, int size, int next_size, float lr, float ir, float lmbda, const std::string &activation, const std::string &activation_deriv)
{ self.AddLayer(size, next_size, lr, ir, lmbda, resolveActEnum(activation), resolveActEnum(activation_deriv)); }, nb::arg("size"), nb::arg("next_size"), nb::arg("lr") = 1e-6f, nb::arg("ir") = 0.1f, nb::arg("lmbda") = 1e-2f, nb::arg("activation") = "relu", nb::arg("activation_deriv") = "drelu", "Add a layer to the network.")
.def("set_optimizer", [](Deep::SimplePCNetwork &self, const std::string &opt)
{
if (opt == "ADAM") self.SetOptimizer(Deep::OptimizerType::ADAM);
else if (opt == "ADAMW") self.SetOptimizer(Deep::OptimizerType::ADAMW);
else self.SetOptimizer(Deep::OptimizerType::SGD); }, nb::arg("optimizer"), "Sets the optimizer: ADAM, ADAMW, or SGD.")

if (opt == "ADAM") self.SetOptimizer(Deep::OptimizerType::ADAM);
else if (opt == "ADAMW") self.SetOptimizer(Deep::OptimizerType::ADAMW);
else self.SetOptimizer(Deep::OptimizerType::SGD); }, nb::arg("optimizer"), "Sets the optimizer: ADAM, ADAMW, or SGD.")
.def("project_forward", &Deep::SimplePCNetwork::ProjectForward, "Seeds hidden layers from a genuine forward pass through current "
"weights, instead of zero-init. Call AFTER clamp_input(), BEFORE "
"the settling loop.")

.def("train_step_with_projection", [](Deep::SimplePCNetwork &self, FloatArray x, FloatArray y, int steps)
.def("train_step_with_projection", [](Deep::SimplePCNetwork &self, FloatArray x, FloatArray y, int steps, bool computeEnergy)
{
std::vector<float> xvec(x.data(), x.data() + x.size());
std::vector<float> yvec(y.data(), y.data() + y.size());
return self.TrainStepWithProjection(xvec, yvec, steps); }, nb::arg("x"), nb::arg("y"), nb::arg("steps"))
std::vector<float> xvec(x.data(), x.data() + x.size());
std::vector<float> yvec(y.data(), y.data() + y.size());
return self.TrainStepWithProjection(xvec, yvec, steps, computeEnergy); }, nb::arg("x"), nb::arg("y"), nb::arg("steps"), nb::arg("computeEnergy") = true)
.def("predict_with_projection", [](Deep::SimplePCNetwork &self, FloatArray x, int steps)
{
std::vector<float> xvec(x.data(), x.data() + x.size());

std::vector<float> out_beliefs = self.PredictWithProjection(xvec, steps);
std::vector<float> xvec(x.data(), x.data() + x.size());
std::vector<float> out_beliefs = self.PredictWithProjection(xvec, steps);
return CopyToNewArray(out_beliefs.data(), {out_beliefs.size()}); }, nb::arg("x"), nb::arg("steps"), "Runs forward-projection init and settling loop entirely in C++, returning terminal beliefs.")
.def("randomize_weights", [](Deep::SimplePCNetwork &self, const std::string &distribution)
{
std::random_device rd;
std::mt19937 rng(rd());
self.RandomizeWeights(rng, distribution.c_str()); }, nb::arg("distribution"), "Initialize every layer's weights using a distribution string, "
"e.g. \"normal(0, 1)\" or \"uniform(-0.3, 0.3)\".");

return CopyToNewArray(out_beliefs.data(), {out_beliefs.size()}); }, nb::arg("x"), nb::arg("steps"), "Runs forward-projection init and settling loop entirely in C++, returning terminal beliefs.");
nb::class_<Deep::GaussSeidelPCNetwork>(m, "GaussSeidelPCNetwork", "Predictive Coding Network with Gauss-Seidel settling dynamics.")
.def(nb::init<int>(), nb::arg("batch_size"))
.def("add_layer", [](Deep::GaussSeidelPCNetwork &self, int size, int next_size, float lr, float ir, float lmbda, const std::string &activation, const std::string &activation_deriv)
Expand Down Expand Up @@ -490,7 +513,12 @@ void bind_networks(nb::module_ &m)
return layers[index].get(); }, nb::rv_policy::reference_internal);

nb::class_<Deep::DirectKPPCNetwork>(m, "DirectKPPCNetwork", "Predictive Coding Network with Direct Kolen-Pollack feedback alignment.")
.def(nb::init<int>(), nb::arg("batch_size"))
.def("__init__", [](Deep::DirectKPPCNetwork *self, int batch_size, const std::string &device)
{
Deep::DeviceType dt = (device == "cuda" || device == "gpu")
? Deep::DeviceType::DEVICE_GPU
: Deep::DeviceType::DEVICE_CPU;
new (self) Deep::DirectKPPCNetwork(batch_size, dt); }, nb::arg("batch_size"), nb::arg("device") = "cpu")
.def("add_layer", [](Deep::DirectKPPCNetwork &self, size_t size, size_t next_size, size_t terminal_size, float lr, float ir, float fl, float lmbda, const std::string &activation, const std::string &activation_deriv)
{ self.AddLayer(size, next_size, terminal_size, lr, ir, fl, lmbda, resolveActEnum(activation), resolveActEnum(activation_deriv)); }, nb::arg("size"), nb::arg("next_size"), nb::arg("terminal_size"), nb::arg("lr") = 1e-6f, nb::arg("ir") = 0.1f, nb::arg("fl") = 1e-4f, nb::arg("lmbda") = 1e-2f, nb::arg("activation") = "relu", nb::arg("activation_deriv") = "drelu")
.def("compile", &Deep::DirectKPPCNetwork::Compile)
Expand Down Expand Up @@ -700,9 +728,7 @@ void bind_utilities(nb::module_ &m)
{ Deep::dSigmoid(x.data(), x.size()); });
}

// ============================================================================
// Main Module Entry
// ============================================================================
NB_MODULE(pydeepity, m)
{
m.doc() = "Deepity: A high-performance Predictive Coding library.";
Expand Down
Binary file removed deepity_build/__pycache__/cli.cpython-312.pyc
Binary file not shown.
Binary file removed deepity_build/__pycache__/cli.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed deepity_build/__pycache__/config.cpython-312.pyc
Binary file not shown.
Binary file removed deepity_build/__pycache__/config.cpython-314.pyc
Binary file not shown.
Binary file removed deepity_build/__pycache__/git_info.cpython-312.pyc
Binary file not shown.
Binary file removed deepity_build/__pycache__/git_info.cpython-314.pyc
Binary file not shown.
Binary file removed deepity_build/__pycache__/process.cpython-312.pyc
Binary file not shown.
Binary file not shown.
15 changes: 7 additions & 8 deletions deepity_build/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _merge_pgo_profiles(config):

print(f"--- PGO: profile ready: {output} ---")

def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
def parse_args(argv: list[str] | None = None) -> tuple[argparse.Namespace, list[str]]:
parser = argparse.ArgumentParser(
description="Deepity Cross-Platform Build & Test Runner"
)
Expand Down Expand Up @@ -190,7 +190,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
),
)

args = parser.parse_args(argv)
args, unknown_args = parser.parse_known_args(argv)

if args.list_profiles:
for profile in ARCH_PROFILES.values():
Expand All @@ -206,14 +206,12 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
args.arch_profile = DEFAULT_ARCH_PROFILE

if args.cuda is None:
# Distributed builds default to CPU-only, since a CUDA-linked binary
# isn't portable either. Everything else keeps the old default (ON).
args.cuda = args.arch_profile != "distributed"

return args
return args, unknown_args


def build_config_from_args(args: argparse.Namespace) -> BuildConfig:
def build_config_from_args(args: argparse.Namespace, extra_args: list[str]) -> BuildConfig:
return BuildConfig(
build_type=args.build_type,
jobs=args.jobs,
Expand All @@ -226,6 +224,7 @@ def build_config_from_args(args: argparse.Namespace) -> BuildConfig:
clean=args.clean,
verbose=args.verbose,
pgo=args.pgo,
extra_cmake_args=tuple(extra_args)
)


Expand Down Expand Up @@ -349,8 +348,8 @@ def _run_pgo_workload(config: BuildConfig) -> None:


def main(argv: list[str] | None = None) -> None:
args = parse_args(argv)
config = build_config_from_args(args)
args, extra_args = parse_args(argv)
config = build_config_from_args(args, extra_args)

if config.clean and config.build_dir.exists():
shutil.rmtree(config.build_dir, onexc=_rmtree_onexc)
Expand Down
3 changes: 3 additions & 0 deletions deepity_build/cmake_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def configure_command(config: BuildConfig, ninja: str | None, pgo_phase: str | N
if profile.msvc_flags:
cmd.append(f"-DDEEPITY_MSVC_ARCH_FLAGS={profile.msvc_flags}")

if config.extra_cmake_args:
cmd.extend(config.extra_cmake_args)

if sys.platform == "win32" and ninja:
# CC being unset doesn't mean clang isn't in play -- CMake can
# auto-detect and pick it up on its own (as this project's own
Expand Down
1 change: 1 addition & 0 deletions deepity_build/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class BuildConfig:
clean: bool
verbose: bool
pgo: bool = False
extra_cmake_args: tuple[str,...] = ()
build_root: Path = Path("build")

@property
Expand Down
Binary file modified deepity_build/reporting/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file modified deepity_build/reporting/__pycache__/base.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file modified deepity_build/reporting/__pycache__/rich_reporter.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Loading
Loading