vectorphile 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vectorphile/__init__.py +154 -0
- vectorphile/amp/__init__.py +52 -0
- vectorphile/autograd/__init__.py +13 -0
- vectorphile/autograd/engine.py +208 -0
- vectorphile/core/__init__.py +21 -0
- vectorphile/core/tensor.py +696 -0
- vectorphile/data/__init__.py +23 -0
- vectorphile/data/frame.py +537 -0
- vectorphile/data/groupby.py +98 -0
- vectorphile/data/index.py +86 -0
- vectorphile/data/series.py +228 -0
- vectorphile/fft/__init__.py +34 -0
- vectorphile/linalg/__init__.py +63 -0
- vectorphile/ml/__init__.py +44 -0
- vectorphile/ml/boost/__init__.py +133 -0
- vectorphile/ml/cluster.py +218 -0
- vectorphile/ml/decomposition.py +161 -0
- vectorphile/ml/ensemble.py +209 -0
- vectorphile/ml/linear.py +250 -0
- vectorphile/ml/metrics.py +148 -0
- vectorphile/ml/model_selection.py +171 -0
- vectorphile/ml/naive_bayes.py +113 -0
- vectorphile/ml/neighbors.py +95 -0
- vectorphile/ml/preprocessing.py +202 -0
- vectorphile/ml/svm.py +165 -0
- vectorphile/ml/tree.py +155 -0
- vectorphile/nn/__init__.py +20 -0
- vectorphile/nn/activations.py +97 -0
- vectorphile/nn/layers.py +231 -0
- vectorphile/nn/losses.py +172 -0
- vectorphile/nn/module.py +97 -0
- vectorphile/nn/sequential.py +47 -0
- vectorphile/optim/__init__.py +12 -0
- vectorphile/optim/adagrad.py +31 -0
- vectorphile/optim/adam.py +71 -0
- vectorphile/optim/lr_scheduler.py +110 -0
- vectorphile/optim/optimizer.py +30 -0
- vectorphile/optim/rmsprop.py +34 -0
- vectorphile/optim/sgd.py +38 -0
- vectorphile/optimize/__init__.py +97 -0
- vectorphile/random/__init__.py +64 -0
- vectorphile/signal/__init__.py +67 -0
- vectorphile/sparse/__init__.py +129 -0
- vectorphile/stats/__init__.py +90 -0
- vectorphile/utils/__init__.py +3 -0
- vectorphile/utils/data/__init__.py +4 -0
- vectorphile/utils/data/dataloader.py +47 -0
- vectorphile/utils/data/dataset.py +29 -0
- vectorphile-0.1.0.dist-info/METADATA +208 -0
- vectorphile-0.1.0.dist-info/RECORD +53 -0
- vectorphile-0.1.0.dist-info/WHEEL +5 -0
- vectorphile-0.1.0.dist-info/licenses/LICENSE +201 -0
- vectorphile-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Adam and AdamW optimizers."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.optim.optimizer import Optimizer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Adam(Optimizer):
|
|
7
|
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.0, amsgrad=False):
|
|
8
|
+
defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay, amsgrad=amsgrad)
|
|
9
|
+
super().__init__(params, defaults)
|
|
10
|
+
self.state = {}
|
|
11
|
+
|
|
12
|
+
def step(self):
|
|
13
|
+
for group in self.param_groups:
|
|
14
|
+
lr = group['lr']
|
|
15
|
+
b1, b2 = group['betas']
|
|
16
|
+
eps = group['eps']
|
|
17
|
+
wd = group['weight_decay']
|
|
18
|
+
amsgrad = group['amsgrad']
|
|
19
|
+
for p in group['params']:
|
|
20
|
+
if p.grad is None:
|
|
21
|
+
continue
|
|
22
|
+
g = p.grad._data
|
|
23
|
+
pid = id(p)
|
|
24
|
+
if pid not in self.state:
|
|
25
|
+
self.state[pid] = {'step': 0, 'm': np.zeros_like(p._data), 'v': np.zeros_like(p._data)}
|
|
26
|
+
if amsgrad:
|
|
27
|
+
self.state[pid]['v_max'] = np.zeros_like(p._data)
|
|
28
|
+
s = self.state[pid]
|
|
29
|
+
s['step'] += 1
|
|
30
|
+
s['m'] = b1 * s['m'] + (1 - b1) * g
|
|
31
|
+
s['v'] = b2 * s['v'] + (1 - b2) * g**2
|
|
32
|
+
m_hat = s['m'] / (1 - b1**s['step'])
|
|
33
|
+
v_hat = s['v'] / (1 - b2**s['step'])
|
|
34
|
+
if amsgrad:
|
|
35
|
+
s['v_max'] = np.maximum(s['v_max'], v_hat)
|
|
36
|
+
denom = np.sqrt(s['v_max']) + eps
|
|
37
|
+
else:
|
|
38
|
+
denom = np.sqrt(v_hat) + eps
|
|
39
|
+
if wd != 0:
|
|
40
|
+
p._data -= lr * wd * p._data
|
|
41
|
+
p._data -= lr * m_hat / denom
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AdamW(Optimizer):
|
|
45
|
+
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01):
|
|
46
|
+
defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
|
|
47
|
+
super().__init__(params, defaults)
|
|
48
|
+
self.state = {}
|
|
49
|
+
|
|
50
|
+
def step(self):
|
|
51
|
+
for group in self.param_groups:
|
|
52
|
+
lr = group['lr']
|
|
53
|
+
b1, b2 = group['betas']
|
|
54
|
+
eps = group['eps']
|
|
55
|
+
wd = group['weight_decay']
|
|
56
|
+
for p in group['params']:
|
|
57
|
+
if p.grad is None:
|
|
58
|
+
continue
|
|
59
|
+
# Decoupled weight decay
|
|
60
|
+
p._data *= (1 - lr * wd)
|
|
61
|
+
g = p.grad._data
|
|
62
|
+
pid = id(p)
|
|
63
|
+
if pid not in self.state:
|
|
64
|
+
self.state[pid] = {'step': 0, 'm': np.zeros_like(p._data), 'v': np.zeros_like(p._data)}
|
|
65
|
+
s = self.state[pid]
|
|
66
|
+
s['step'] += 1
|
|
67
|
+
s['m'] = b1 * s['m'] + (1 - b1) * g
|
|
68
|
+
s['v'] = b2 * s['v'] + (1 - b2) * g**2
|
|
69
|
+
m_hat = s['m'] / (1 - b1**s['step'])
|
|
70
|
+
v_hat = s['v'] / (1 - b2**s['step'])
|
|
71
|
+
p._data -= lr * m_hat / (np.sqrt(v_hat) + eps)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Learning rate schedulers."""
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class _LRScheduler:
|
|
6
|
+
def __init__(self, optimizer, last_epoch=-1):
|
|
7
|
+
self.optimizer = optimizer
|
|
8
|
+
self.last_epoch = last_epoch
|
|
9
|
+
self.base_lrs = [g['lr'] for g in optimizer.param_groups]
|
|
10
|
+
self.step()
|
|
11
|
+
|
|
12
|
+
def get_lr(self):
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
def step(self, epoch=None):
|
|
16
|
+
if epoch is None:
|
|
17
|
+
self.last_epoch += 1
|
|
18
|
+
else:
|
|
19
|
+
self.last_epoch = epoch
|
|
20
|
+
lrs = self.get_lr()
|
|
21
|
+
for g, lr in zip(self.optimizer.param_groups, lrs):
|
|
22
|
+
g['lr'] = lr
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StepLR(_LRScheduler):
|
|
26
|
+
def __init__(self, optimizer, step_size, gamma=0.1, last_epoch=-1):
|
|
27
|
+
self.step_size = step_size
|
|
28
|
+
self.gamma = gamma
|
|
29
|
+
super().__init__(optimizer, last_epoch)
|
|
30
|
+
|
|
31
|
+
def get_lr(self):
|
|
32
|
+
return [base * self.gamma ** (self.last_epoch // self.step_size) for base in self.base_lrs]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ExponentialLR(_LRScheduler):
|
|
36
|
+
def __init__(self, optimizer, gamma, last_epoch=-1):
|
|
37
|
+
self.gamma = gamma
|
|
38
|
+
super().__init__(optimizer, last_epoch)
|
|
39
|
+
|
|
40
|
+
def get_lr(self):
|
|
41
|
+
return [base * self.gamma ** self.last_epoch for base in self.base_lrs]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CosineAnnealingLR(_LRScheduler):
|
|
45
|
+
def __init__(self, optimizer, T_max, eta_min=0.0, last_epoch=-1):
|
|
46
|
+
self.T_max = T_max
|
|
47
|
+
self.eta_min = eta_min
|
|
48
|
+
super().__init__(optimizer, last_epoch)
|
|
49
|
+
|
|
50
|
+
def get_lr(self):
|
|
51
|
+
t = self.last_epoch
|
|
52
|
+
return [
|
|
53
|
+
self.eta_min + (base - self.eta_min) * (1 + math.cos(math.pi * t / self.T_max)) / 2
|
|
54
|
+
for base in self.base_lrs
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LinearLR(_LRScheduler):
|
|
59
|
+
def __init__(self, optimizer, start_factor=1.0/3, end_factor=1.0, total_iters=5, last_epoch=-1):
|
|
60
|
+
self.start_factor = start_factor
|
|
61
|
+
self.end_factor = end_factor
|
|
62
|
+
self.total_iters = total_iters
|
|
63
|
+
super().__init__(optimizer, last_epoch)
|
|
64
|
+
|
|
65
|
+
def get_lr(self):
|
|
66
|
+
t = min(self.last_epoch, self.total_iters)
|
|
67
|
+
alpha = t / self.total_iters
|
|
68
|
+
factor = self.start_factor + alpha * (self.end_factor - self.start_factor)
|
|
69
|
+
return [base * factor for base in self.base_lrs]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ReduceLROnPlateau:
|
|
73
|
+
def __init__(self, optimizer, mode='min', factor=0.1, patience=10, threshold=1e-4,
|
|
74
|
+
threshold_mode='rel', cooldown=0, min_lr=0.0, eps=1e-8):
|
|
75
|
+
self.optimizer = optimizer
|
|
76
|
+
self.mode = mode
|
|
77
|
+
self.factor = factor
|
|
78
|
+
self.patience = patience
|
|
79
|
+
self.threshold = threshold
|
|
80
|
+
self.threshold_mode = threshold_mode
|
|
81
|
+
self.cooldown = cooldown
|
|
82
|
+
self.min_lr = min_lr
|
|
83
|
+
self.eps = eps
|
|
84
|
+
self.best = None
|
|
85
|
+
self.num_bad_epochs = 0
|
|
86
|
+
self.cooldown_counter = 0
|
|
87
|
+
|
|
88
|
+
def step(self, metrics):
|
|
89
|
+
current = float(metrics)
|
|
90
|
+
if self.best is None:
|
|
91
|
+
self.best = current
|
|
92
|
+
return
|
|
93
|
+
if self.mode == 'min':
|
|
94
|
+
improved = current < self.best - self.threshold
|
|
95
|
+
else:
|
|
96
|
+
improved = current > self.best + self.threshold
|
|
97
|
+
if improved:
|
|
98
|
+
self.best = current
|
|
99
|
+
self.num_bad_epochs = 0
|
|
100
|
+
else:
|
|
101
|
+
self.num_bad_epochs += 1
|
|
102
|
+
if self.cooldown_counter > 0:
|
|
103
|
+
self.cooldown_counter -= 1
|
|
104
|
+
return
|
|
105
|
+
if self.num_bad_epochs >= self.patience:
|
|
106
|
+
for g in self.optimizer.param_groups:
|
|
107
|
+
new_lr = max(g['lr'] * self.factor, self.min_lr)
|
|
108
|
+
g['lr'] = new_lr
|
|
109
|
+
self.cooldown_counter = self.cooldown
|
|
110
|
+
self.num_bad_epochs = 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Base Optimizer class."""
|
|
2
|
+
from typing import List, Dict, Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Optimizer:
|
|
6
|
+
def __init__(self, params, defaults: Dict[str, Any]):
|
|
7
|
+
if isinstance(params, dict):
|
|
8
|
+
params = list(params.values())
|
|
9
|
+
self.param_groups: List[Dict] = []
|
|
10
|
+
self.defaults = defaults
|
|
11
|
+
self.add_param_group({'params': list(params)})
|
|
12
|
+
|
|
13
|
+
def add_param_group(self, param_group: Dict):
|
|
14
|
+
group = {**self.defaults, **param_group}
|
|
15
|
+
self.param_groups.append(group)
|
|
16
|
+
|
|
17
|
+
def zero_grad(self):
|
|
18
|
+
for group in self.param_groups:
|
|
19
|
+
for p in group['params']:
|
|
20
|
+
if p.grad is not None:
|
|
21
|
+
p.grad = None
|
|
22
|
+
|
|
23
|
+
def step(self):
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
def state_dict(self) -> Dict:
|
|
27
|
+
return {'state': self.state, 'param_groups': self.param_groups}
|
|
28
|
+
|
|
29
|
+
def load_state_dict(self, state_dict: Dict):
|
|
30
|
+
self.state = state_dict['state']
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""RMSprop and Adagrad optimizers."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.optim.optimizer import Optimizer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RMSprop(Optimizer):
|
|
7
|
+
def __init__(self, params, lr=0.01, alpha=0.99, eps=1e-8, weight_decay=0.0, momentum=0.0):
|
|
8
|
+
defaults = dict(lr=lr, alpha=alpha, eps=eps, weight_decay=weight_decay, momentum=momentum)
|
|
9
|
+
super().__init__(params, defaults)
|
|
10
|
+
self.state = {}
|
|
11
|
+
|
|
12
|
+
def step(self):
|
|
13
|
+
for group in self.param_groups:
|
|
14
|
+
lr = group['lr']
|
|
15
|
+
alpha = group['alpha']
|
|
16
|
+
eps = group['eps']
|
|
17
|
+
wd = group['weight_decay']
|
|
18
|
+
mu = group['momentum']
|
|
19
|
+
for p in group['params']:
|
|
20
|
+
if p.grad is None:
|
|
21
|
+
continue
|
|
22
|
+
g = p.grad._data.copy()
|
|
23
|
+
if wd != 0:
|
|
24
|
+
g += wd * p._data
|
|
25
|
+
pid = id(p)
|
|
26
|
+
if pid not in self.state:
|
|
27
|
+
self.state[pid] = {'sq': np.zeros_like(p._data), 'buf': np.zeros_like(p._data)}
|
|
28
|
+
s = self.state[pid]
|
|
29
|
+
s['sq'] = alpha * s['sq'] + (1 - alpha) * g**2
|
|
30
|
+
step = g / (np.sqrt(s['sq']) + eps)
|
|
31
|
+
if mu != 0:
|
|
32
|
+
s['buf'] = mu * s['buf'] + step
|
|
33
|
+
step = s['buf']
|
|
34
|
+
p._data -= lr * step
|
vectorphile/optim/sgd.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""SGD optimizer with momentum and weight decay."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.optim.optimizer import Optimizer
|
|
4
|
+
from vectorphile.core.tensor import Tensor
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SGD(Optimizer):
|
|
8
|
+
def __init__(self, params, lr=0.01, momentum=0.0, weight_decay=0.0, nesterov=False, dampening=0.0):
|
|
9
|
+
defaults = dict(lr=lr, momentum=momentum, weight_decay=weight_decay,
|
|
10
|
+
nesterov=nesterov, dampening=dampening)
|
|
11
|
+
super().__init__(params, defaults)
|
|
12
|
+
self.state = {}
|
|
13
|
+
|
|
14
|
+
def step(self):
|
|
15
|
+
for group in self.param_groups:
|
|
16
|
+
lr = group['lr']
|
|
17
|
+
momentum = group['momentum']
|
|
18
|
+
weight_decay = group['weight_decay']
|
|
19
|
+
dampening = group['dampening']
|
|
20
|
+
nesterov = group['nesterov']
|
|
21
|
+
for p in group['params']:
|
|
22
|
+
if p.grad is None:
|
|
23
|
+
continue
|
|
24
|
+
g = p.grad._data.copy()
|
|
25
|
+
if weight_decay != 0:
|
|
26
|
+
g += weight_decay * p._data
|
|
27
|
+
pid = id(p)
|
|
28
|
+
if momentum != 0:
|
|
29
|
+
if pid not in self.state:
|
|
30
|
+
self.state[pid] = {'momentum_buffer': np.zeros_like(p._data)}
|
|
31
|
+
buf = self.state[pid]['momentum_buffer']
|
|
32
|
+
buf *= momentum
|
|
33
|
+
buf += (1 - dampening) * g
|
|
34
|
+
if nesterov:
|
|
35
|
+
g = g + momentum * buf
|
|
36
|
+
else:
|
|
37
|
+
g = buf
|
|
38
|
+
p._data -= lr * g
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Numerical optimization routines."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Callable, Optional, Tuple
|
|
4
|
+
from vectorphile.core.tensor import Tensor
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def minimize(fun: Callable, x0, method='bfgs', jac=None, tol=1e-6, options=None):
|
|
8
|
+
"""Minimize a scalar function. Wraps scipy.optimize.minimize when available."""
|
|
9
|
+
try:
|
|
10
|
+
from scipy.optimize import minimize as sp_minimize
|
|
11
|
+
x0_arr = x0._data.flatten() if isinstance(x0, Tensor) else np.array(x0).flatten()
|
|
12
|
+
def fn(x):
|
|
13
|
+
t = Tensor(x.reshape(x0_arr.shape) if isinstance(x0, Tensor) else x, requires_grad=True)
|
|
14
|
+
result = fun(t)
|
|
15
|
+
return float(result._data) if isinstance(result, Tensor) else float(result)
|
|
16
|
+
result = sp_minimize(fn, x0_arr, method=method.upper(), jac=jac, tol=tol, options=options)
|
|
17
|
+
result.x = Tensor(result.x.reshape(x0._data.shape if isinstance(x0, Tensor) else x0.shape))
|
|
18
|
+
return result
|
|
19
|
+
except ImportError:
|
|
20
|
+
return _gradient_descent(fun, x0, tol=tol, options=options or {})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _gradient_descent(fun: Callable, x0, lr=0.01, tol=1e-6, max_iter=1000, options=None):
|
|
24
|
+
if options:
|
|
25
|
+
lr = options.get('lr', lr)
|
|
26
|
+
max_iter = options.get('maxiter', max_iter)
|
|
27
|
+
if isinstance(x0, Tensor):
|
|
28
|
+
x = Tensor(x0._data.copy(), requires_grad=True)
|
|
29
|
+
else:
|
|
30
|
+
x = Tensor(np.array(x0, dtype=float), requires_grad=True)
|
|
31
|
+
for i in range(max_iter):
|
|
32
|
+
if x.grad is not None:
|
|
33
|
+
x.grad = None
|
|
34
|
+
result = fun(x)
|
|
35
|
+
result.backward()
|
|
36
|
+
if x.grad is None:
|
|
37
|
+
break
|
|
38
|
+
grad_norm = float(np.linalg.norm(x.grad._data))
|
|
39
|
+
if grad_norm < tol:
|
|
40
|
+
break
|
|
41
|
+
x._data -= lr * x.grad._data
|
|
42
|
+
|
|
43
|
+
class Result:
|
|
44
|
+
pass
|
|
45
|
+
r = Result()
|
|
46
|
+
r.x = x
|
|
47
|
+
r.fun = float(fun(x)._data)
|
|
48
|
+
r.success = True
|
|
49
|
+
return r
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def root(fun: Callable, x0, method='hybr', tol=1e-6):
|
|
53
|
+
"""Find root of a function."""
|
|
54
|
+
try:
|
|
55
|
+
from scipy.optimize import root as sp_root
|
|
56
|
+
x0_arr = x0._data.flatten() if isinstance(x0, Tensor) else np.array(x0).flatten()
|
|
57
|
+
def fn(x):
|
|
58
|
+
t = Tensor(x)
|
|
59
|
+
result = fun(t)
|
|
60
|
+
return result._data.flatten() if isinstance(result, Tensor) else np.array(result).flatten()
|
|
61
|
+
return sp_root(fn, x0_arr, method=method, tol=tol)
|
|
62
|
+
except ImportError:
|
|
63
|
+
raise ImportError("root requires scipy: pip install scipy")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def linprog(c, A_ub=None, b_ub=None, A_eq=None, b_eq=None, bounds=None, method='highs'):
|
|
67
|
+
"""Linear programming."""
|
|
68
|
+
try:
|
|
69
|
+
from scipy.optimize import linprog as sp_linprog
|
|
70
|
+
c_arr = c._data if isinstance(c, Tensor) else np.array(c)
|
|
71
|
+
return sp_linprog(c_arr, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method=method)
|
|
72
|
+
except ImportError:
|
|
73
|
+
raise ImportError("linprog requires scipy: pip install scipy")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def curve_fit(f: Callable, xdata, ydata, p0=None):
|
|
77
|
+
"""Curve fitting."""
|
|
78
|
+
try:
|
|
79
|
+
from scipy.optimize import curve_fit as sp_curve_fit
|
|
80
|
+
xd = xdata._data if isinstance(xdata, Tensor) else np.array(xdata)
|
|
81
|
+
yd = ydata._data if isinstance(ydata, Tensor) else np.array(ydata)
|
|
82
|
+
popt, pcov = sp_curve_fit(f, xd, yd, p0=p0)
|
|
83
|
+
return Tensor(popt.astype(np.float32)), Tensor(pcov.astype(np.float32))
|
|
84
|
+
except ImportError:
|
|
85
|
+
raise ImportError("curve_fit requires scipy: pip install scipy")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def differential_evolution(fun: Callable, bounds, maxiter=1000, tol=0.01, seed=None):
|
|
89
|
+
"""Global optimization via differential evolution."""
|
|
90
|
+
try:
|
|
91
|
+
from scipy.optimize import differential_evolution as sp_de
|
|
92
|
+
def fn(x):
|
|
93
|
+
result = fun(Tensor(x))
|
|
94
|
+
return float(result._data) if isinstance(result, Tensor) else float(result)
|
|
95
|
+
return sp_de(fn, bounds, maxiter=maxiter, tol=tol, seed=seed)
|
|
96
|
+
except ImportError:
|
|
97
|
+
raise ImportError("differential_evolution requires scipy: pip install scipy")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Random number generation."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.core.tensor import Tensor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def seed(s: int):
|
|
7
|
+
np.random.seed(s)
|
|
8
|
+
|
|
9
|
+
def rand(*shape, device='cpu') -> Tensor:
|
|
10
|
+
if len(shape) == 1 and isinstance(shape[0], (tuple, list)):
|
|
11
|
+
shape = shape[0]
|
|
12
|
+
return Tensor(np.random.rand(*shape).astype(np.float32), device=device)
|
|
13
|
+
|
|
14
|
+
def randn(*shape, device='cpu') -> Tensor:
|
|
15
|
+
if len(shape) == 1 and isinstance(shape[0], (tuple, list)):
|
|
16
|
+
shape = shape[0]
|
|
17
|
+
return Tensor(np.random.randn(*shape).astype(np.float32), device=device)
|
|
18
|
+
|
|
19
|
+
def randint(low, high, shape, device='cpu') -> Tensor:
|
|
20
|
+
return Tensor(np.random.randint(low, high, shape).astype(np.int64), device=device)
|
|
21
|
+
|
|
22
|
+
def choice(a, size=None, replace=True, p=None) -> Tensor:
|
|
23
|
+
if isinstance(a, Tensor):
|
|
24
|
+
a = a._data
|
|
25
|
+
return Tensor(np.random.choice(a, size=size, replace=replace, p=p))
|
|
26
|
+
|
|
27
|
+
def shuffle(x: Tensor) -> Tensor:
|
|
28
|
+
data = x._data.copy()
|
|
29
|
+
np.random.shuffle(data)
|
|
30
|
+
return Tensor(data)
|
|
31
|
+
|
|
32
|
+
def normal(loc=0.0, scale=1.0, size=None, device='cpu') -> Tensor:
|
|
33
|
+
return Tensor(np.random.normal(loc, scale, size).astype(np.float32), device=device)
|
|
34
|
+
|
|
35
|
+
def uniform(low=0.0, high=1.0, size=None, device='cpu') -> Tensor:
|
|
36
|
+
return Tensor(np.random.uniform(low, high, size).astype(np.float32), device=device)
|
|
37
|
+
|
|
38
|
+
def binomial(n, p, size=None) -> Tensor:
|
|
39
|
+
return Tensor(np.random.binomial(n, p, size).astype(np.float32))
|
|
40
|
+
|
|
41
|
+
def poisson(lam=1.0, size=None) -> Tensor:
|
|
42
|
+
return Tensor(np.random.poisson(lam, size).astype(np.float32))
|
|
43
|
+
|
|
44
|
+
def exponential(scale=1.0, size=None) -> Tensor:
|
|
45
|
+
return Tensor(np.random.exponential(scale, size).astype(np.float32))
|
|
46
|
+
|
|
47
|
+
def multinomial(input: Tensor, num_samples: int, replacement=False) -> Tensor:
|
|
48
|
+
probs = input._data
|
|
49
|
+
probs = probs / probs.sum(axis=-1, keepdims=True)
|
|
50
|
+
if probs.ndim == 1:
|
|
51
|
+
return Tensor(np.random.choice(len(probs), size=num_samples, replace=replacement, p=probs).astype(np.int64))
|
|
52
|
+
result = np.stack([
|
|
53
|
+
np.random.choice(probs.shape[-1], size=num_samples, replace=replacement, p=row)
|
|
54
|
+
for row in probs.reshape(-1, probs.shape[-1])
|
|
55
|
+
])
|
|
56
|
+
return Tensor(result.reshape(*probs.shape[:-1], num_samples).astype(np.int64))
|
|
57
|
+
|
|
58
|
+
def permutation(x) -> Tensor:
|
|
59
|
+
if isinstance(x, int):
|
|
60
|
+
return Tensor(np.random.permutation(x).astype(np.int64))
|
|
61
|
+
return Tensor(np.random.permutation(x._data if isinstance(x, Tensor) else x))
|
|
62
|
+
|
|
63
|
+
def bernoulli(p: Tensor) -> Tensor:
|
|
64
|
+
return Tensor((np.random.rand(*p._data.shape) < p._data).astype(np.float32))
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Digital signal processing."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.core.tensor import Tensor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def fft(x: Tensor) -> Tensor:
|
|
7
|
+
return Tensor(np.fft.fft(x._data))
|
|
8
|
+
|
|
9
|
+
def ifft(x: Tensor) -> Tensor:
|
|
10
|
+
return Tensor(np.fft.ifft(x._data))
|
|
11
|
+
|
|
12
|
+
def fftfreq(n: int, d: float = 1.0) -> Tensor:
|
|
13
|
+
return Tensor(np.fft.fftfreq(n, d).astype(np.float32))
|
|
14
|
+
|
|
15
|
+
def convolve(x: Tensor, h: Tensor, mode='full') -> Tensor:
|
|
16
|
+
return Tensor(np.convolve(x._data.flatten(), h._data.flatten(), mode=mode).astype(np.float32))
|
|
17
|
+
|
|
18
|
+
def correlate(x: Tensor, y: Tensor, mode='full') -> Tensor:
|
|
19
|
+
return Tensor(np.correlate(x._data.flatten(), y._data.flatten(), mode=mode).astype(np.float32))
|
|
20
|
+
|
|
21
|
+
def stft(x: Tensor, n_fft=512, hop_length=None, win_length=None, window='hann') -> Tensor:
|
|
22
|
+
signal = x._data.flatten()
|
|
23
|
+
if hop_length is None:
|
|
24
|
+
hop_length = n_fft // 4
|
|
25
|
+
if win_length is None:
|
|
26
|
+
win_length = n_fft
|
|
27
|
+
if window == 'hann':
|
|
28
|
+
win = np.hanning(win_length)
|
|
29
|
+
else:
|
|
30
|
+
win = np.ones(win_length)
|
|
31
|
+
n = len(signal)
|
|
32
|
+
frames = []
|
|
33
|
+
for i in range(0, n - win_length + 1, hop_length):
|
|
34
|
+
frame = signal[i:i+win_length] * win
|
|
35
|
+
frames.append(np.fft.rfft(frame, n=n_fft))
|
|
36
|
+
return Tensor(np.array(frames))
|
|
37
|
+
|
|
38
|
+
def spectrogram(x: Tensor, n_fft=512, hop_length=None, win_length=None) -> Tensor:
|
|
39
|
+
s = stft(x, n_fft, hop_length, win_length)
|
|
40
|
+
return Tensor(np.abs(s._data)**2)
|
|
41
|
+
|
|
42
|
+
def butter(N: int, Wn, btype='low', output='ba'):
|
|
43
|
+
try:
|
|
44
|
+
from scipy.signal import butter as sp_butter
|
|
45
|
+
return sp_butter(N, Wn, btype=btype, output=output)
|
|
46
|
+
except ImportError:
|
|
47
|
+
raise ImportError("butter requires scipy: pip install scipy")
|
|
48
|
+
|
|
49
|
+
def filtfilt(b, a, x: Tensor) -> Tensor:
|
|
50
|
+
try:
|
|
51
|
+
from scipy.signal import filtfilt as sp_filtfilt
|
|
52
|
+
return Tensor(sp_filtfilt(b, a, x._data).astype(np.float32))
|
|
53
|
+
except ImportError:
|
|
54
|
+
raise ImportError("filtfilt requires scipy: pip install scipy")
|
|
55
|
+
|
|
56
|
+
def hilbert(x: Tensor) -> Tensor:
|
|
57
|
+
try:
|
|
58
|
+
from scipy.signal import hilbert as sp_hilbert
|
|
59
|
+
return Tensor(np.abs(sp_hilbert(x._data)).astype(np.float32))
|
|
60
|
+
except ImportError:
|
|
61
|
+
return x
|
|
62
|
+
|
|
63
|
+
def resample(x: Tensor, num: int) -> Tensor:
|
|
64
|
+
signal = x._data.flatten()
|
|
65
|
+
n = len(signal)
|
|
66
|
+
indices = np.linspace(0, n - 1, num)
|
|
67
|
+
return Tensor(np.interp(indices, np.arange(n), signal).astype(np.float32))
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Sparse matrix support."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from vectorphile.core.tensor import Tensor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class csr_matrix:
|
|
7
|
+
"""Compressed Sparse Row matrix."""
|
|
8
|
+
def __init__(self, data, shape=None):
|
|
9
|
+
if isinstance(data, np.ndarray):
|
|
10
|
+
self._from_dense(data)
|
|
11
|
+
elif isinstance(data, Tensor):
|
|
12
|
+
self._from_dense(data._data)
|
|
13
|
+
elif isinstance(data, tuple) and len(data) == 3:
|
|
14
|
+
(vals, (row, col)), shape = data, shape
|
|
15
|
+
self.data = np.array(vals)
|
|
16
|
+
self.indices = np.array(col, dtype=int)
|
|
17
|
+
self.indptr = np.zeros(shape[0] + 1, dtype=int)
|
|
18
|
+
for r in row:
|
|
19
|
+
self.indptr[r + 1:] += 1
|
|
20
|
+
self.shape = shape
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError("Unsupported input format")
|
|
23
|
+
|
|
24
|
+
def _from_dense(self, arr):
|
|
25
|
+
self.shape = arr.shape
|
|
26
|
+
rows, cols = np.nonzero(arr)
|
|
27
|
+
self.data = arr[rows, cols]
|
|
28
|
+
self.indices = cols
|
|
29
|
+
self.indptr = np.zeros(arr.shape[0] + 1, dtype=int)
|
|
30
|
+
for r in rows:
|
|
31
|
+
self.indptr[r + 1] += 1
|
|
32
|
+
self.indptr = np.cumsum(self.indptr)
|
|
33
|
+
|
|
34
|
+
def toarray(self) -> np.ndarray:
|
|
35
|
+
result = np.zeros(self.shape)
|
|
36
|
+
for i in range(self.shape[0]):
|
|
37
|
+
start, end = self.indptr[i], self.indptr[i+1]
|
|
38
|
+
result[i, self.indices[start:end]] = self.data[start:end]
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
def to_tensor(self) -> Tensor:
|
|
42
|
+
return Tensor(self.toarray().astype(np.float32))
|
|
43
|
+
|
|
44
|
+
def __matmul__(self, other):
|
|
45
|
+
a = self.toarray()
|
|
46
|
+
if isinstance(other, csr_matrix):
|
|
47
|
+
return csr_matrix(a @ other.toarray())
|
|
48
|
+
if isinstance(other, np.ndarray):
|
|
49
|
+
return a @ other
|
|
50
|
+
if isinstance(other, Tensor):
|
|
51
|
+
return Tensor((a @ other._data).astype(np.float32))
|
|
52
|
+
return NotImplemented
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def nnz(self) -> int:
|
|
56
|
+
return len(self.data)
|
|
57
|
+
|
|
58
|
+
def __repr__(self):
|
|
59
|
+
return f'csr_matrix(shape={self.shape}, nnz={self.nnz})'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class csc_matrix:
|
|
63
|
+
"""Compressed Sparse Column matrix."""
|
|
64
|
+
def __init__(self, data):
|
|
65
|
+
if isinstance(data, np.ndarray):
|
|
66
|
+
arr = data
|
|
67
|
+
elif isinstance(data, Tensor):
|
|
68
|
+
arr = data._data
|
|
69
|
+
else:
|
|
70
|
+
arr = np.array(data)
|
|
71
|
+
self.shape = arr.shape
|
|
72
|
+
rows, cols = np.nonzero(arr)
|
|
73
|
+
order = np.argsort(cols)
|
|
74
|
+
self.data = arr[rows[order], cols[order]]
|
|
75
|
+
self.indices = rows[order]
|
|
76
|
+
self.indptr = np.zeros(arr.shape[1] + 1, dtype=int)
|
|
77
|
+
for c in cols[order]:
|
|
78
|
+
self.indptr[c + 1] += 1
|
|
79
|
+
self.indptr = np.cumsum(self.indptr)
|
|
80
|
+
|
|
81
|
+
def toarray(self) -> np.ndarray:
|
|
82
|
+
result = np.zeros(self.shape)
|
|
83
|
+
for j in range(self.shape[1]):
|
|
84
|
+
start, end = self.indptr[j], self.indptr[j+1]
|
|
85
|
+
result[self.indices[start:end], j] = self.data[start:end]
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
def to_tensor(self) -> Tensor:
|
|
89
|
+
return Tensor(self.toarray().astype(np.float32))
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def nnz(self) -> int:
|
|
93
|
+
return len(self.data)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class coo_matrix:
|
|
97
|
+
"""Coordinate (COO) sparse matrix."""
|
|
98
|
+
def __init__(self, data):
|
|
99
|
+
if isinstance(data, tuple) and len(data) == 2:
|
|
100
|
+
(vals, (rows, cols)), shape = data
|
|
101
|
+
self.data = np.array(vals)
|
|
102
|
+
self.row = np.array(rows, dtype=int)
|
|
103
|
+
self.col = np.array(cols, dtype=int)
|
|
104
|
+
self.shape = shape
|
|
105
|
+
elif isinstance(data, np.ndarray):
|
|
106
|
+
arr = data
|
|
107
|
+
self.shape = arr.shape
|
|
108
|
+
self.row, self.col = np.nonzero(arr)
|
|
109
|
+
self.data = arr[self.row, self.col]
|
|
110
|
+
else:
|
|
111
|
+
arr = np.array(data)
|
|
112
|
+
self.shape = arr.shape
|
|
113
|
+
self.row, self.col = np.nonzero(arr)
|
|
114
|
+
self.data = arr[self.row, self.col]
|
|
115
|
+
|
|
116
|
+
def toarray(self) -> np.ndarray:
|
|
117
|
+
result = np.zeros(self.shape)
|
|
118
|
+
result[self.row, self.col] = self.data
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
def tocsr(self) -> csr_matrix:
|
|
122
|
+
return csr_matrix(self.toarray())
|
|
123
|
+
|
|
124
|
+
def to_tensor(self) -> Tensor:
|
|
125
|
+
return Tensor(self.toarray().astype(np.float32))
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def nnz(self) -> int:
|
|
129
|
+
return len(self.data)
|