"""
# LittleLearn: Touch the Big World with Little Steps 🌱

LittleLearn is an original, experimental machine learning framework — inspired by
the simplicity of Keras and the flexibility of PyTorch — but designed from scratch
with its own architecture, philosophy, and backend engine.

## 🧠 What Makes LittleLearn Different?

- It is **not a wrapper** or extension of existing libraries. All modules, layers, and
  training engines are **fully original**, including the custom autodiff engine: `GradientReflector`.
- Built with **raw control in mind**: define layers directly, customize everything, and build
  neural networks without being forced into predefined structures.
- Includes both **high-level APIs** (`Sequential`, `Model`) for convenience and **low-level access**
  for full experimental control.
- Features unique innovations like **node-level gradient clipping**, **inline graph execution tracing**,
  and fully customizable attention mechanisms (e.g., MHA from scratch).

## ⚙️ Core Philosophy:
    Touch Big World By Little Step \n 
    bring whatever your build model ways with ecosystem, are you a type want make model by one instruction ? 
    AutoBuildModel and AutoTransformers(coming soon) can answer you want. are want play with Sequential more give control with layers warapper ? 
    Sequential and ModelbyNode(coming soon) answers your call. and more ekstrem with raw layers witout layers wrapper ? you do it just call 
    layers,loss,optimizers but you have to know how to get gradient and weight of model. or more ekstrem you want interaction with 
    engine grad that is Gradient Reflector ? yes its can use by anything you need, all back end layers its availavle in Gradient Reflector, 


## 📚 Included Ecosystem:

- Deep learning (Dense, attention, LSTM, etc.)
- Auto tools like `AutoBuildModel` and `AutoTransformers` for one-liner construction.
- All powered by `Gradient Reflector` — the gradient engine with unique features like per-node clipping.

## 🔖 Note:

Although inspired by Keras and PyTorch, **LittleLearn is an original framework** built from the ground up.
It aims to provide a new perspective on model building — flexible, introspective, and fully customizable.

Author: 
----------
Candra Alpin Gunawan 
"""
__name__ = "littlelearn"
__author__ = "Candra Alpin Gunawan"
__version__ = "1.0.0"
__license__ = "Apache 2.0"
__realese__ = "29-December-2025"
__email__ = "hinamatsuriairin@gmail.com"
__repo__ = "https://github.com/Airinchan818/LittleLearn"
__youtube__ = "https://youtube.com/@hinamatsuriairin4596?si=KrBtOhXoVYnbBlpY"

from . import preprocessing
from .tensor import *
from .GradientReflector import GradientReflector
from .GradientReflector import Node
from . import DeepLearning


def to_tensor (x,dtype = float32,device="cpu",requires_grad=True) :
    if isinstance (x,Tensor) :
        print("warning data has being Tensor, is just rebuild Tensor in the same Tensor object")
        x = x.tensor
    return Tensor(x,dtype=dtype,device=device,requires_grad=requires_grad)

def matmul (a,b,transpose_a=False,transpose_b=False) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    if not isinstance(b,Tensor) :
        b = Tensor(b)
    return a.matmul(b,tranpose_a=transpose_a,transpose_b=transpose_b)

def sum(a,axis=None,keepdims=False) :
    if not isinstance(a,Tensor) : 
        a = Tensor(a)
    
    return a.sum(axis=axis,keepdims=keepdims)

def mean(a,axis=None,keepdims=False) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.mean(axis=axis,keepdims=keepdims)

def log (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.log()

def exp(a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.exp()

def flatten (a) :
  if not isinstance(a,Tensor) :
      a = Tensor(a)
  
  return a.flatten()

def expand_dims (a,axis=0) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.expand_dims(axis=axis)

def squezze (a,axis=None) :
  if not isinstance(a,Tensor) :
      a = Tensor(a)
  
  return a.squezze(axis=axis)

def unsquezze (a,axis=None) :
    if not isinstance(a,Tensor) :
        a= Tensor(a)
    
    return a.unsquezze(axis=axis)

def sumprod (a,b) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    if not isinstance(b,Tensor) :
        b = Tensor(b)
    
    return a.sumprod(a,b)

def var(a,axis=None,keepdims=False) :
    if not isinstance(a,Tensor)  :
        a = Tensor(a)
    
    return a.var(axis=axis,keepdims=keepdims)

def std(a,axis=None,keepdims=False,epsilon=1e-5) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.std(axis=axis,keepdims=keepdims,epsilon=1e-5)

def log2 (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.log2()

def log10(a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.log10()

def log1p(a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.log1p()

def reshape(a,new_shape: tuple) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    return a.reshape(newshape=new_shape)

def transpose(a,new_shape:tuple) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.reshape(newshape=new_shape)

def sqrt (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.sqrt()

def clip(a,min_vals :float,max_vals:float) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.clip(min_vals,max_vals)

def max(a,axis=None,keepdims=False) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.max(axis=axis,keepdims=keepdims)

def min (a,axis=None,keepdims=False) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.min(axis=axis,keepdims=keepdims)

def argmax(a,axis=None) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.argmax(axis=axis)

def abs (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.abs()

def pad(a,pad_with,mode='constant',constant_value=0) :
    if not isinstance(a,Tensor) :
        a =  Tensor(a)
    
    return a.pad(pad_with=pad_with,mode=mode,constant_value=constant_value)

def broadcast_to (a,shape :tuple) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.broadcast_to(shape)

def erf (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.erf()

def sinh (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.sinh()

def consh (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.cosh()

def tan (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.tan()

def floor (a) : 
    if not isinstance(a,Tensor) : 
        a = Tensor(a)
    
    return a.floor()

def ceil (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.ceil()

def rint (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.rint()

def reciprocal (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.reciprocal()

def sign (a) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.sign()

def identity (a) :
    if not isinstance(a,Tensor) : 
        a = Tensor(a)
    
    return a.identity()

def concat (tensor_list : list,axis=0) :
    if not isinstance(tensor_list[0],Tensor) :
        for i in range(len(tensor_list)) :
            tensor_list[i] = Tensor(tensor_list[i])
    
    return GradientReflector.concat(tensor_list,axis=axis)

def logsumexp (a,axis = None,keepdims=False) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.logsumexp(axis=axis,keepdims=keepdims)

def logaddexp (a,b) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    
    return a.logaddexp(a,b)

def argmin (a,axis=None) :
    if not isinstance(a,Tensor) :
        a = Tensor(a)
    return a.argmin(axis=axis)

def where (condition,a,b) :
   return GradientReflector.where(condition=condition,a=a,b=b)