Skip to content

Commit

Permalink
initial python interface added
Browse files Browse the repository at this point in the history
  • Loading branch information
SwayamInSync committed Oct 26, 2024
1 parent eca3eac commit 110c689
Show file tree
Hide file tree
Showing 10 changed files with 831 additions and 10 deletions.
168 changes: 167 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<p align="center">
<img src="assets/logo.png" alt="Description" width="500">
</p>

### 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
```

<details>
<summary>Project Roadmap(Click Me)</summary>
Expand Down Expand Up @@ -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.

---

</details>

## 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
```
164 changes: 164 additions & 0 deletions examples/scalar_examples.py
Original file line number Diff line number Diff line change
@@ -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()
28 changes: 28 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Loading

0 comments on commit 110c689

Please sign in to comment.