Metadata-Version: 2.4
Name: pynet-ai
Version: 2.0.0
Summary: A Python neural network library for simple networks and educational use.
Author-email: Andru Cupala <andrucupala@gmail.com>
License-Expression: LicenseRef-PolyForm-Noncommercial-1.0.0
Keywords: neural-network,machine-learning,deep-learning,education,python
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# PyNet 2.0.0

A simple educational neural network library built from scratch in Python.

PyNet focuses on understanding how neural networks work internally, including layers, activations, loss functions, backpropagation, optimizers, weight initialization, and training.

## Installation

```bash
pip install pynet-ai
```

## Example Program

Run with Python 3.12

```python
import random
import pygame
from pynet import Network, Dense, LeakyReLU, Tanh, MeanSquaredErrorLoss, Adam, Xavier

WIDTH, HEIGHT = (480, 360)
FPS = 60
EPOCHS = 10000
DELAY = 1000
LEARNING_RATE = 0.0001
ACCURACY_TRIES = 50
SPEED = 100
RADIUS = 10


def normalize(x, y):
    length = (x * x + y * y) ** 0.5
    if length < 0.0001:
        return (0.0, 0.0)
    return (x / length, y / length)


def random_direction():
    return normalize(random.uniform(-1, 1), random.uniform(-1, 1))


def direction_to(x, y, target_x, target_y):
    return normalize(target_x - x, target_y - y)


def calculate_accuracy(network):
    total = 0.0
    for _ in range(ACCURACY_TRIES):
        x, y = random_direction()
        predicted_x, predicted_y = network.predict([x, y])
        predicted_x, predicted_y = normalize(predicted_x, predicted_y)
        total += (predicted_x * x + predicted_y * y + 1) / 2
    return total / ACCURACY_TRIES * 100


def train_network(network):
    loss_function = MeanSquaredErrorLoss()
    optimizer = Adam(learning_rate=LEARNING_RATE)
    for epoch in range(EPOCHS):
        x, y = random_direction()
        prediction, values = network.forward([x, y], save_inputs=True)
        loss = loss_function.forward(prediction, [x, y])
        gradient = loss_function.backward(prediction, [x, y])
        network.backward(values, gradient)
        optimizer.step(network.modules)
        if (epoch + 1) % DELAY == 0:
            print(f"Epoch {epoch + 1}/{EPOCHS} Loss: {loss:.5f} Accuracy: {calculate_accuracy(network):.2f}%")


def main():
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
    pygame.display.set_caption("PyNet Follow Mouse")
    font = pygame.font.SysFont(None, 28)
    small_font = pygame.font.SysFont(None, 20)
    network = Network(Dense(2, 8, initializer=Xavier()), LeakyReLU(), Dense(8, 8, initializer=Xavier()), LeakyReLU(), Dense(8, 2, initializer=Xavier()), Tanh())
    screen.fill((20, 20, 20))
    text = font.render("Training network...", True, "white")
    screen.blit(text, text.get_rect(center=(WIDTH // 2, HEIGHT // 2)))
    pygame.display.flip()
    train_network(network)
    print(network.summary())
    accuracy = calculate_accuracy(network)
    clock = pygame.time.Clock()
    player_position = [WIDTH / 2, HEIGHT / 2]
    windowed_size = (WIDTH, HEIGHT)
    fullscreen = False
    paused = False
    running = True
    while running:
        delta_time = clock.tick(FPS) / 1000.0
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    paused = not paused
                elif event.key == pygame.K_F11:
                    fullscreen = not fullscreen
                    if fullscreen:
                        screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
                    else:
                        screen = pygame.display.set_mode(windowed_size, pygame.RESIZABLE)
            elif event.type == pygame.VIDEORESIZE and (not fullscreen):
                windowed_size = (event.w, event.h)
                screen = pygame.display.set_mode(windowed_size, pygame.RESIZABLE)
        mouse_position = pygame.mouse.get_pos()
        screen_width, screen_height = screen.get_size()
        if not paused:
            target_direction = direction_to(player_position[0], player_position[1], mouse_position[0], mouse_position[1])
            output_x, output_y = network.predict(list(target_direction))
            move_x, move_y = normalize(output_x, output_y)
            player_position[0] += move_x * SPEED * delta_time
            player_position[1] += move_y * SPEED * delta_time
            player_position[0] = max(RADIUS, min(screen_width - RADIUS, player_position[0]))
            player_position[1] = max(RADIUS, min(screen_height - RADIUS, player_position[1]))
        screen.fill((20, 20, 20))
        pygame.draw.line(screen, (80, 80, 80), (int(player_position[0]), int(player_position[1])), mouse_position, 1)
        pygame.draw.circle(screen, (255, 70, 70), mouse_position, RADIUS)
        pygame.draw.circle(screen, (70, 230, 120), (int(player_position[0]), int(player_position[1])), RADIUS)
        screen.blit(font.render(f"Accuracy: {accuracy:.1f}%", True, "white"), (10, 10))
        screen.blit(small_font.render("Space: Pause", True, (180, 180, 180)), (10, 42))
        status = "PAUSED" if paused else "RUNNING"
        screen.blit(font.render(status, True, "white"), (screen_width - 120, 10))
        pygame.display.flip()
    pygame.quit()


if __name__ == "__main__":
    main()

```
