Skip to content

Commit c51bcdc

Browse files
committed
write readme
1 parent 9c274e4 commit c51bcdc

File tree

1 file changed

+131
-35
lines changed

1 file changed

+131
-35
lines changed

readme.md

Lines changed: 131 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,151 @@
1-
# Funkypy
1+
# LuxTools
2+
> A collection of tools and utilities that I often use in my work.
23
3-
A collection of functional programming utilities for Python.
4+
## Features
45

6+
- **[Scientific](#scientific)**
7+
- [Error propagation](#error-propagation) for numerical calculations
8+
- [Pretty printing](#numeric-result-formatting) of numerical results with uncertainties
9+
- **[Functional](#functional)**
10+
- [Function composition](#function-composition) utilities
11+
- [Partial function](#partial-application) application
12+
- [Overload function](#overload-function-definitions) definitions
513

14+
## Installation
15+
16+
```bash
17+
pip install luxtools
618
```
7-
partial
8-
```
919

10-
Okay so i think I understand the problem of multiple partial:
11-
- @partial return a function, and it's that function we edit to apply it.
12-
- It should return the function we get to return the function that can the be partialled every time we try again.
13-
- maybe upon successful call
14-
- or maybe should give up and use functools.partial.
20+
## Usage
21+
22+
### Scientific
1523

24+
#### Error Propagation
1625

26+
```python
27+
import torch
28+
from luxtools import get_error
1729

18-
# Significant Figures
30+
# Create tensors with uncertainties
31+
x = torch.tensor([1.0, 3.0], dtype=torch.float32, requires_grad=True)
32+
x.sigma = torch.tensor([0.1, 0.2], dtype=torch.float32)
1933

20-
## Installation
21-
Navigate to the root directory of the repository and run the following command:
34+
y = torch.tensor([2.0, 4.0], dtype=torch.float32, requires_grad=True)
35+
y = Variable(y, torch.tensor([0.2, 0.3]))
2236

23-
```bash
24-
pip install -e .
37+
# Perform calculations
38+
f = x * y
39+
40+
# Get propagated error
41+
error = get_error(f)
42+
# tensor([0.2828, 1.2042])
2543
```
2644

27-
## Usage
45+
#### Numeric Result Formatting
46+
2847
```python
29-
from significant_figures import NumericResult
48+
from luxtools import NumericResult
3049

31-
result = NumericResult(value=1.2345, uncertainty=0.01, unit='m')
50+
# Create a measurement with uncertainty
51+
result = NumericResult(1.234, 0.193)
52+
print(result)
53+
# (1.2 ± 0.2)
3254

55+
# Scientific notation
56+
result = NumericResult(234.23424, 10)
3357
print(result)
34-
# Output: (1.23 ± 0.01) m
58+
# (2.3 ± 0.1)*10^(2)
59+
60+
# LaTeX output
61+
print(result.latex())
62+
# (2.3 \pm 0.1)\cdot 10^{2}
63+
```
64+
65+
### Functional
66+
67+
#### Function Composition
68+
69+
```python
70+
from luxtools import chain
71+
72+
# Compose functions
73+
f = lambda x: x + 1
74+
g = lambda x: x * 2
75+
h = lambda x: x ** 2
76+
77+
# Create a new function that applies f, then g, then h
78+
composed = chain(f, g, h)
3579

36-
print(result.latex(delimiter=""))
37-
# Output: (1.23 \\pm 0.01) m
80+
result = composed(3) # ((3 + 1) * 2) ** 2 = 64
3881
```
3982

83+
#### Partial Application
84+
Allows you to partially apply arguments to a function, creating a new function with fewer arguments. See [article](https://lunalux.io/functional-programming-in-python/better-currying-in-python/) for discussion.
85+
86+
```python
87+
from luxtools import partial
88+
89+
@partial
90+
def greet(greeting, name):
91+
return f"{greeting}, {name}!"
92+
93+
# Create a new function with 'Hello' fixed as the greeting
94+
say_hello = greet("Hello")
95+
96+
result = say_hello("World") # "Hello, World!"
97+
```
98+
99+
#### Overload function definitions
100+
101+
Allows you to have multiple function definitions for the same function name.
102+
It uses typehints to determine which function to call. See [article](https://lunalux.io/functional-programming-in-python/overloading-functions-in-python/) for discussion.
103+
104+
```python
105+
from luxtools import overload
106+
107+
class Email:
108+
def __init__(self, email: str):
109+
self.email = email
110+
111+
def __str__(self) -> str:
112+
return self.email
113+
114+
115+
class PhoneNumber:
116+
def __init__(self, phone_number: str):
117+
self.phone_number = phone_number
118+
119+
def __str__(self) -> str:
120+
return self.phone_number
121+
122+
123+
@overload
124+
def get_user(email: Email):
125+
print("Email:", email)
126+
return email
127+
128+
129+
@overload
130+
def get_user(phone_number: PhoneNumber):
131+
print("Phone:", phone_number)
132+
return phone_number
133+
134+
get_user(Email("test@example.com")) # prints: Email: test@example.com
135+
get_user(PhoneNumber("123-456-789")) # prints: Phone: 123-456-789
136+
```
137+
138+
Caveat, if the function is defined in a non-global scope such as a class, or inside a function, then you need to pass the local scope to the decorator.
139+
140+
```python
141+
def local_scope():
142+
@overload(scope=locals())
143+
def get_user(email: Email):
144+
print("Email:", email)
145+
146+
@overload(scope=locals())
147+
def get_user(phone_number: PhoneNumber):
148+
print("Phone:", phone_number)
149+
```
40150

41-
## Examples
42-
```Python
43-
Value, Uncertainty, expected result.
44-
(1e-4, 1e-5, "(1.0 +/- 0.1)*10^(-4)"),
45-
(1, 0.1, "(1.0 +/- 0.1)"),
46-
(1.234, 0.193, "(1.2 +/- 0.2)"),
47-
(0.1, 1, "(0 +/- 1)"),
48-
(234.23424,10, "(2.3 +/- 0.1)*10^(2)"),
49-
(0, 0, "(0 +/- 0)"),
50-
(10, 0, "(1.0 +/- 0.0)*10^(1)"),
51-
(0, 10, "(0 +/- 1)*10^(1)"),
52-
(0.123456789, 0.987654321, "(1 +/- 10)*10^(-1)"),
53-
(123456789, 987654321, "(1 +/- 10)*10^(8)"),
54-
(1.23456789e-9, 9.87654321e-11, "(1.23 +/- 0.10)*10^(-9)"),
55-
```
151+
This is necessary because the parent stack frame doesn't exist inside the `overload` function, so it has to be passed explicitly. See [tests](test/functional/test_overload.py).

0 commit comments

Comments
 (0)