diff --git a/README.md b/README.md index c99db2d..d4f2bc2 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,20 @@ ## Work In Progress... 🚧 Enigma is a C/C++ based tensor framework designed for building dynamic neural networks with optimized tensor computations and GPU acceleration, featuring seamless **Python** bindings for easy integration and usage through a simple `enigma` import. +

Description

### Language Support: -![C++](https://img.shields.io/badge/C%2B%2B-orange.svg) ![Python](https://img.shields.io/badge/Python-blue.svg) +![C++](https://img.shields.io/badge/C%2B%2B-orange.svg) ![Python](https://img.shields.io/badge/Python-blue.svg) + +## Installation + +```bash +pip install enigma +```
Project Roadmap(Click Me) @@ -150,4 +157,163 @@ Enigma is a C/C++ based tensor framework designed for building dynamic neural ne - [ ] Include guidelines for contributing to the project. --- +
+ +## Quick Start + +```python +import enigma + +# Create scalars +x = enigma.Scalar(42) # Integer +y = enigma.Scalar(3.14) # Float +z = enigma.Scalar(1 + 2j) # Complex +b = enigma.Scalar(True) # Boolean + +# Basic arithmetic +result = x + y # Automatic type promotion +print(result) # 45.14 +``` + +## Basic Usage + +### Creating Scalars + +```python +import enigma + +# Different ways to create scalars +i = enigma.Scalar(42) # Integer type +f = enigma.Scalar(3.14) # Float type +c = enigma.Scalar(1 + 2j) # Complex type +b = enigma.Scalar(True) # Boolean type + +# Check types +print(i.dtype) # ScalarType.Int64 +print(f.is_floating_point()) # True +print(c.is_complex()) # True +print(b.is_bool()) # True +``` + +### Arithmetic Operations + +```python +# Basic arithmetic with automatic type promotion +x = enigma.Scalar(10) +y = enigma.Scalar(3) + +addition = x + y # 13 +subtraction = x - y # 7 +multiplication = x * y # 30 +division = x / y # 3.333... (promotes to float) + +# Mixed-type operations +f = enigma.Scalar(3.14) +result = x * f # 31.4 (float result) +``` + +### Type Conversion + +```python +# Safe type conversions +x = enigma.Scalar(42) +as_float = x.to_float() # 42.0 +as_int = x.to_int() # 42 +as_bool = x.to_bool() # True + +# Error handling for invalid conversions +try: + enigma.Scalar(3.14).to_int() # Will raise ScalarTypeError +except enigma.ScalarTypeError as e: + print(f"Cannot convert: {e}") +``` + +### Type Promotion Rules + +```python +# Check type promotion +int_type = enigma.int64 +float_type = enigma.float64 +result_type = enigma.promote_types(int_type, float_type) +print(result_type) # ScalarType.Float64 + +# Automatic promotion in operations +i = enigma.Scalar(5) # Int64 +f = enigma.Scalar(2.5) # Float64 +result = i + f # Result is Float64 +print(result.dtype) # ScalarType.Float64 +``` + +### Error Handling + +```python +try: + # Division by zero + result = enigma.Scalar(1) / enigma.Scalar(0) +except enigma.ScalarError as e: + print(f"Error: {e}") + +try: + # Invalid type conversion + float_val = enigma.Scalar(3.14) + int_val = float_val.to_int() # Will raise ScalarTypeError +except enigma.ScalarTypeError as e: + print(f"Conversion error: {e}") +``` + +### Complex Numbers + +```python +# Working with complex numbers +c1 = enigma.Scalar(1 + 2j) +c2 = enigma.Scalar(2 - 1j) + +# Complex arithmetic +sum_c = c1 + c2 # 3 + 1j +prod_c = c1 * c2 # 4 + 3j + +# Converting to Python complex +py_complex = c1.to_complex() # Get Python complex number +print(py_complex.real) # 1.0 +print(py_complex.imag) # 2.0 +``` + +### Type Safety + +```python +# Strict type checking +bool_val = enigma.Scalar(True) +int_val = enigma.Scalar(1) + +# No implicit conversion between bool and int +print(bool_val == int_val) # False + +# Check if casting is possible +can_cast = enigma.can_cast(enigma.float64, enigma.int64) +print(can_cast) # False (can't safely cast float to int) +``` + +### Comparisons + +```python +# Value comparisons +a = enigma.Scalar(42) +b = enigma.Scalar(42.0) +c = enigma.Scalar(43) + +print(a == b) # True (same value, different types) +print(a != c) # True +``` + +## Advanced Features + +### Epsilon Comparisons for Floating Point + +```python +x = enigma.Scalar(0.1 + 0.2) +y = enigma.Scalar(0.3) + +# Automatically handles floating point precision +print(x == y) # True +``` diff --git a/examples/scalar_examples.py b/examples/scalar_examples.py new file mode 100644 index 0000000..5729611 --- /dev/null +++ b/examples/scalar_examples.py @@ -0,0 +1,164 @@ +import enigma +from typing import Any + + +def print_section(title: str) -> None: + """Helper to print formatted section titles.""" + print(f"\n{'='*50}") + print(f" {title}") + print(f"{'='*50}\n") + + +def demonstrate_basic_creation(): + """Demonstrate basic scalar creation and type inference.""" + print_section("Basic Scalar Creation") + + # Different ways to create scalars + examples = [ + (42, "Integer"), + (3.14, "Float"), + (True, "Boolean"), + (1 + 2j, "Complex"), + ] + + for value, name in examples: + scalar = enigma.Scalar(value) + print(f"{name:8} | Value: {scalar} | Type: {scalar.dtype}") + + # Show default construction + default_scalar = enigma.Scalar() + print(f"\nDefault | Value: {default_scalar} | Type: { + default_scalar.dtype}") + + +def demonstrate_type_checking(): + """Demonstrate type checking methods.""" + print_section("Type Checking") + + scalars = { + "Integer": enigma.Scalar(42), + "Float": enigma.Scalar(3.14), + "Complex": enigma.Scalar(1 + 2j), + "Boolean": enigma.Scalar(True) + } + + for name, scalar in scalars.items(): + print(f"{name:8} is:") + print(f" Integral? {scalar.is_integral()}") + print(f" Floating? {scalar.is_floating_point()}") + print(f" Complex? {scalar.is_complex()}") + print(f" Boolean? {scalar.is_bool()}\n") + + +def demonstrate_arithmetic(): + """Demonstrate arithmetic operations.""" + print_section("Arithmetic Operations") + + # Basic arithmetic + a = enigma.Scalar(10) + b = enigma.Scalar(3) + + print("Integer operations:") + print(f"10 + 3 = {a + b}") + print(f"10 - 3 = {a - b}") + print(f"10 * 3 = {a * b}") + print(f"10 / 3 = {a / b}") # Note: Division promotes to float + + # Mixed-type arithmetic + c = enigma.Scalar(3.14) + print("\nMixed-type operations:") + print(f"10 + 3.14 = {a + c}") + print(f"10 * 3.14 = {a * c}") + + # Complex arithmetic + d = enigma.Scalar(1 + 1j) + print("\nComplex operations:") + print(f"(1 + 1j) * 3.14 = {d * c}") + + +def demonstrate_type_conversion(): + """Demonstrate type conversion capabilities.""" + print_section("Type Conversion") + + # Safe conversions + scalar = enigma.Scalar(42) + print(f"Original: {scalar} (type: {scalar.dtype})") + print(f"To float: {scalar.to_float()} (explicit conversion)") + print(f"To int : {scalar.to_int()}") + print(f"To bool : {scalar.to_bool()}") + + # Conversion errors + print("\nDemonstrating conversion errors:") + try: + enigma.Scalar(3.14).to_int() + except enigma.ScalarTypeError as e: + print(f"Expected error: {e}") + + +def demonstrate_type_promotion(): + """Demonstrate type promotion rules.""" + print_section("Type Promotion") + + cases = [ + (enigma.int64, enigma.float64), + (enigma.float32, enigma.float64), + (enigma.int32, enigma.complex64), + ] + + for type1, type2 in cases: + promoted = enigma.promote_types(type1, type2) + print(f"{type1} + {type2} → {promoted}") + + +def demonstrate_error_handling(): + """Demonstrate error handling.""" + print_section("Error Handling") + examples = [ + # Division by zero + lambda: enigma.Scalar(1) / enigma.Scalar(0), + # Invalid conversion + lambda: enigma.Scalar(3.14).to_int(), + # Complex to real conversion with imaginary part + lambda: enigma.Scalar(1 + 1j).to_float(), + ] + + for i, example in enumerate(examples, 1): + try: + example() + except enigma.ScalarTypeError as e: + print(f"Example {i}: {type(e).__name__}: {e}") + + +def demonstrate_comparisons(): + """Demonstrate comparison operations.""" + print_section("Comparisons") + + a = enigma.Scalar(42) + b = enigma.Scalar(42.0) + c = enigma.Scalar(43) + + print(f"42 == 42.0: {a == b}") + print(f"42 != 43 : {a != c}") + + # Show type-aware comparisons + print(f"\nType-aware comparisons:") + print(f"Scalar(1) == Scalar(True): { + enigma.Scalar(1) == enigma.Scalar(True)}") + + +def main(): + """Run all demonstrations.""" + print("\nEnigma Scalar Library Demonstration") + print("Version:", enigma.__version__, "\n") + + demonstrate_basic_creation() + demonstrate_type_checking() + demonstrate_arithmetic() + demonstrate_type_conversion() + demonstrate_type_promotion() + demonstrate_error_handling() + demonstrate_comparisons() + + +if __name__ == "__main__": + main() diff --git a/meson.build b/meson.build index cf1d6f5..48e0ad6 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,34 @@ enigma_lib = static_library('enigma', cpp_args: cpp_args ) +# Python extension module +py_mod = import('python') +py3 = py_mod.find_installation('python3', pure: false) # Set pure to false +py3_dep = py3.dependency() + +pybind11_dep = dependency('pybind11', required: true) + +py3.extension_module('_enigma', + 'python/src/bindings.cpp', + include_directories: inc_dir, + link_with: enigma_lib, + dependencies: [pybind11_dep, py3_dep], + cpp_args: cpp_args, + install: true, + install_dir: py3.get_install_dir() / 'enigma' # Specify install directory +) + +# Install Python package files +python_files = [ + 'python/enigma/__init__.py', +] + +py3.install_sources( + python_files, + pure: false, # Set pure to false + subdir: 'enigma' +) + # Test Dependencies gtest_proj = subproject('gtest') gtest_dep = gtest_proj.get_variable('gtest_dep') diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..09fe0a0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +requires = [ + "meson>=1.3.2", + "meson-python", + "setuptools>=42", + "wheel", + "pybind11>=2.6.0", +] +build-backend = "mesonpy" + +[tool.pytest.ini_options] +testpaths = ["python/tests"] +python_files = ["test_*.py"] +addopts = "-v --tb=short" \ No newline at end of file diff --git a/python/enigma/__init__.py b/python/enigma/__init__.py new file mode 100644 index 0000000..4e1beec --- /dev/null +++ b/python/enigma/__init__.py @@ -0,0 +1,27 @@ +from ._enigma import ( + Scalar, + ScalarType, + ScalarError, + ScalarTypeError, + get_dtype, + promote_types, + can_cast, +) + + +int8 = ScalarType.int8 +int16 = ScalarType.int16 +int32 = ScalarType.int32 +int64 = ScalarType.int64 +uint8 = ScalarType.uint8 +uint16 = ScalarType.uint16 +uint32 = ScalarType.uint32 +uint64 = ScalarType.uint64 +float32 = ScalarType.float32 +float64 = ScalarType.float64 +complex64 = ScalarType.complex64 +complex128 = ScalarType.complex128 +bool_ = ScalarType.bool + +# Version info +__version__ = "0.0.1" diff --git a/python/src/bindings.cpp b/python/src/bindings.cpp new file mode 100644 index 0000000..f57fe15 --- /dev/null +++ b/python/src/bindings.cpp @@ -0,0 +1,188 @@ +// python/src/bindings.cpp +#include +#include +#include +#include "Scalar.h" +#include "DEBUG.h" + +namespace py = pybind11; +using namespace enigma; + +// Helper function to convert Python numeric types to Scalar +Scalar py_to_scalar(const py::object &obj) +{ + if (py::isinstance(obj)) + { + return Scalar(obj.cast()); + } + if (py::isinstance(obj)) + { + return Scalar(obj.cast()); + } + if (py::isinstance(obj)) + { + return Scalar(obj.cast()); + } + if (PyComplex_Check(obj.ptr())) + { + std::complex c( + PyComplex_RealAsDouble(obj.ptr()), + PyComplex_ImagAsDouble(obj.ptr())); + return Scalar(c); + } + + // Try to extract Scalar from Python object + try + { + return obj.cast(); + } + catch (const py::cast_error &) + { + throw py::type_error("Cannot convert Python object to Scalar"); + } +} + +// Convert Scalar to appropriate Python type +py::object scalar_to_py(const Scalar &scalar) +{ + if (scalar.isBoolean()) + { + return py::bool_(scalar.to()); + } + if (scalar.isIntegral()) + { + return py::int_(scalar.to()); + } + if (scalar.isFloatingPoint()) + { + return py::float_(scalar.to()); + } + if (scalar.isComplex()) + { + auto c = scalar.to>(); + return py::reinterpret_steal( + PyComplex_FromDoubles(c.real(), c.imag())); + } + throw py::type_error("Unknown Scalar type"); +} + +PYBIND11_MODULE(_enigma, m) +{ + // Create the module + m.doc() = "Python bindings for the Enigma tensor framework"; + + // Register exception translations + py::register_exception(m, "ScalarError"); + py::register_exception(m, "ScalarTypeError"); + + // Register ScalarType enum + py::enum_(m, "ScalarType") + .value("int8", ScalarType::Int8) + .value("int16", ScalarType::Int16) + .value("int32", ScalarType::Int32) + .value("int64", ScalarType::Int64) + .value("uint8", ScalarType::UInt8) + .value("uint16", ScalarType::UInt16) + .value("uint32", ScalarType::UInt32) + .value("uint64", ScalarType::UInt64) + .value("float32", ScalarType::Float32) + .value("float64", ScalarType::Float64) + .value("complex64", ScalarType::Complex64) + .value("complex128", ScalarType::Complex128) + .value("bool", ScalarType::Bool) + .export_values(); + + // Register Scalar class + py::class_(m, "Scalar") + // Constructors + .def(py::init<>()) + .def(py::init([](const py::object &value, py::object dtype) + { + if (!dtype.is_none()) { + auto scalar_type = dtype.cast(); + // TODO: Implement explicit dtype conversion + throw py::type_error("Explicit dtype not yet implemented"); + } + return py_to_scalar(value); }), + py::arg("value"), py::arg("dtype") = py::none()) + + // Type checking methods + .def_property_readonly("dtype", &Scalar::type) + .def("is_floating_point", &Scalar::isFloatingPoint) + .def("is_integral", &Scalar::isIntegral) + .def("is_complex", &Scalar::isComplex) + .def("is_bool", &Scalar::isBoolean) + + // Conversion methods + .def("to_float", [](const Scalar &self) + { return self.to(); }) + .def("to_int", [](const Scalar &self) + { return self.to(); }) + .def("to_bool", [](const Scalar &self) + { return self.to(); }) + .def("to_complex", [](const Scalar &self) + { return self.to>(); }) + + // String representation + .def("__str__", &Scalar::toString) + .def("__repr__", [](const Scalar &self) + { return "enigma.Scalar(" + self.toString() + ")"; }) + + // Arithmetic operators + .def("__add__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self + other.cast(); + } + return self + py_to_scalar(other); }) + .def("__sub__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self - other.cast(); + } + return self - py_to_scalar(other); }) + .def("__mul__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self * other.cast(); + } + return self * py_to_scalar(other); }) + .def("__truediv__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self / other.cast(); + } + return self / py_to_scalar(other); }) + .def("__neg__", [](const Scalar &self) + { return -self; }) + + // Reverse operators + .def("__radd__", [](const Scalar &self, const py::object &other) + { return py_to_scalar(other) + self; }) + .def("__rsub__", [](const Scalar &self, const py::object &other) + { return py_to_scalar(other) - self; }) + .def("__rmul__", [](const Scalar &self, const py::object &other) + { return py_to_scalar(other) * self; }) + .def("__rtruediv__", [](const Scalar &self, const py::object &other) + { return py_to_scalar(other) / self; }) + + // Comparison operators + .def("__eq__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self == other.cast(); + } + return self == py_to_scalar(other); }) + .def("__ne__", [](const Scalar &self, const py::object &other) + { + if (py::isinstance(other)) { + return self != other.cast(); + } + return self != py_to_scalar(other); }); + + // Module-level functions + m.def("get_dtype", [](const Scalar &scalar) + { return scalar.type(); }); + m.def("promote_types", &Scalar::promoteTypes); + m.def("can_cast", &Scalar::canCast); +} \ No newline at end of file diff --git a/python/tests/test_scalar.py b/python/tests/test_scalar.py new file mode 100644 index 0000000..7d0f010 --- /dev/null +++ b/python/tests/test_scalar.py @@ -0,0 +1,209 @@ +# python/tests/test_scalar.py +import pytest +import enigma +import math +import time +from typing import Callable + + +class TestScalar: + # Helper constant + EPSILON = 1e-7 + + def approx_equal(self, a: float, b: float) -> bool: + return abs(a - b) < self.EPSILON + + def test_default_construction(self): + """Test default construction of Scalar""" + s = enigma.Scalar() + assert s.dtype == enigma.float64 + assert self.approx_equal(s.to_float(), 0.0) + + def test_type_construction(self): + """Test construction with different types""" + # Integer construction + s1 = enigma.Scalar(42) + assert s1.dtype == enigma.int64 + assert s1.to_int() == 42 + + # Float construction + s2 = enigma.Scalar(3.14) + assert s2.dtype == enigma.float64 + assert self.approx_equal(s2.to_float(), 3.14) + + # Boolean construction + s3 = enigma.Scalar(True) + assert s3.dtype == enigma.bool_ + assert s3.to_bool() + + # Complex construction + s4 = enigma.Scalar(1.0 + 2.0j) + assert s4.dtype == enigma.complex128 + + def test_type_checking(self): + """Test type checking methods""" + i = enigma.Scalar(42) + assert i.is_integral() + assert not i.is_floating_point() + assert not i.is_complex() + assert not i.is_bool() + + f = enigma.Scalar(3.14) + assert not f.is_integral() + assert f.is_floating_point() + assert not f.is_complex() + assert not f.is_bool() + + c = enigma.Scalar(1.0 + 2.0j) + assert not c.is_integral() + assert not c.is_floating_point() + assert c.is_complex() + assert not c.is_bool() + + b = enigma.Scalar(True) + assert not b.is_integral() + assert not b.is_floating_point() + assert not b.is_complex() + assert b.is_bool() + + def test_numeric_conversions(self): + """Test numeric type conversions""" + # Int to Float + i = enigma.Scalar(42) + assert self.approx_equal(i.to_float(), 42.0) + + # Float to Int (when possible) + f = enigma.Scalar(42.0) + assert f.to_int() == 42 + + # Bool to numeric + b = enigma.Scalar(True) + assert b.to_int() == 1 + assert self.approx_equal(b.to_float(), 1.0) + + # Complex to real (when possible) + c = enigma.Scalar(1.0 + 0.0j) + assert self.approx_equal(c.to_float(), 1.0) + + def test_conversion_errors(self): + """Test conversion error cases""" + # Float with fractional part to int + f = enigma.Scalar(3.14) + with pytest.raises(enigma.ScalarTypeError): + f.to_int() + + # Complex with imaginary part to real + c = enigma.Scalar(1.0 + 2.0j) + with pytest.raises(enigma.ScalarTypeError): + c.to_float() + + def test_basic_arithmetic(self): + """Test basic arithmetic operations""" + # Integer arithmetic + i1, i2 = enigma.Scalar(42), enigma.Scalar(8) + assert (i1 + i2).to_int() == 50 + assert (i1 - i2).to_int() == 34 + assert (i1 * i2).to_int() == 336 + assert self.approx_equal((i1 / i2).to_float(), 5.25) + + # Floating point arithmetic + f1, f2 = enigma.Scalar(3.14), enigma.Scalar(2.0) + assert self.approx_equal((f1 + f2).to_float(), 5.14) + assert self.approx_equal((f1 - f2).to_float(), 1.14) + assert self.approx_equal((f1 * f2).to_float(), 6.28) + assert self.approx_equal((f1 / f2).to_float(), 1.57) + + # Complex arithmetic + c1 = enigma.Scalar(1.0 + 2.0j) + c2 = enigma.Scalar(2.0 - 1.0j) + result = (c1 + c2).to_complex() + assert self.approx_equal(result.real, 3.0) + assert self.approx_equal(result.imag, 1.0) + + def test_mixed_type_operations(self): + """Test operations between different types""" + i = enigma.Scalar(42) + f = enigma.Scalar(3.14) + c = enigma.Scalar(1.0 + 2.0j) + + # Int + Float + result = (i + f).to_float() + assert self.approx_equal(result, 45.14) + + # Float + Complex + result = (f + c).to_complex() + assert self.approx_equal(result.real, 4.14) + assert self.approx_equal(result.imag, 2.0) + + # Int * Float + result = (i * f).to_float() + assert self.approx_equal(result, 131.88) + + def test_division_operations(self): + """Test division operations""" + # Basic division + assert self.approx_equal( + (enigma.Scalar(6) / enigma.Scalar(2)).to_float(), 3.0) + assert self.approx_equal( + (enigma.Scalar(3.14) / enigma.Scalar(2.0)).to_float(), 1.57) + + # Division by zero + with pytest.raises(enigma.ScalarTypeError): + enigma.Scalar(1) / enigma.Scalar(0) + with pytest.raises(enigma.ScalarTypeError): + enigma.Scalar(1.0) / enigma.Scalar(0.0) + + def test_comparison_operations(self): + """Test comparison operations""" + # Same type comparisons + assert enigma.Scalar(42) == enigma.Scalar(42) + assert enigma.Scalar(3.14159) == enigma.Scalar(3.14159) + assert enigma.Scalar(True) == enigma.Scalar(True) + + # Mixed type comparisons + assert enigma.Scalar(42) == enigma.Scalar(42.0) + + # Boolean comparisons (strict) + assert not (enigma.Scalar(1) == enigma.Scalar(True)) + assert not (enigma.Scalar(0) == enigma.Scalar(False)) + + def test_type_promotion(self): + """Test type promotion rules""" + # Same type promotion + assert enigma.promote_types(enigma.int64, enigma.int64) == enigma.int64 + + # Complex promotion + assert enigma.promote_types( + enigma.float64, enigma.complex64) == enigma.complex128 + assert enigma.promote_types( + enigma.int64, enigma.complex64) == enigma.complex128 + + # Float promotion + assert enigma.promote_types( + enigma.int64, enigma.float64) == enigma.float64 + + def test_type_casting(self): + """Test type casting capabilities""" + # Same type casting + assert enigma.can_cast(enigma.int64, enigma.int64) + + # Complex casting + assert not enigma.can_cast(enigma.complex64, enigma.float64) + assert enigma.can_cast(enigma.float64, enigma.complex64) + + # Float casting + assert not enigma.can_cast(enigma.float64, enigma.int64) + assert enigma.can_cast(enigma.int64, enigma.float64) + + def test_string_representation(self): + """Test string conversion""" + assert str(enigma.Scalar(42)) == "42" + assert str(enigma.Scalar(True)) == "true" + + # Floating point precision + f = enigma.Scalar(3.14159265359) + assert "3.14159" in str(f) + + +if __name__ == "__main__": + pytest.main([__file__]) diff --git a/reinstall.sh b/reinstall.sh new file mode 100755 index 0000000..b502ee5 --- /dev/null +++ b/reinstall.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -xeuo pipefail +IFS=$'\n\t' + +if [ -d "build/" ] +then + rm -r build +fi + +#meson setup build -Db_sanitize=address,undefined +python -m pip uninstall -y enigma +# python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" +python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..44737cf --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup, find_packages + +setup( + name="enigma", + version="0.0.1", + packages=find_packages(), + install_requires=[], + author="Swayam Singh", + author_email="singhswayam008@gmail.com", + description="Python bindings for the Enigma tensor framework", + long_description=open("README.md").read(), + long_description_content_type="text/markdown", + url="https://github.com/swayaminsync/enigma", + classifiers=[ + "Programming Language :: Python :: 3", + "Programming Language :: C++", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + ], + python_requires=">=3.8", +) diff --git a/test.sh b/test.sh deleted file mode 100644 index b66233b..0000000 --- a/test.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -meson compile -C build -meson test -C build -v - -: ' -gdb build/storage_cow_test -(gdb) run -(gdb) bt -' \ No newline at end of file