Skip to content

Commit 356833a

Browse files
committed
Limited Nelder Mead with sigmoid
1 parent f0c753b commit 356833a

File tree

7 files changed

+81
-41
lines changed

7 files changed

+81
-41
lines changed

pyduino/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
load_dotenv()
66
load_dotenv(dotenv_path=".env.local")
77

8+
log_file_path = os.environ.get('PYDUINO_SLAVE_LOG','/var/log/pyduino/slave.log')
9+
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
10+
811
logging.basicConfig(
912
level=logging.DEBUG,
1013
format='%(asctime)s %(levelname)s %(message)s',
1114
handlers=[
12-
logging.FileHandler(os.environ.get('PYDUINO_SLAVE_LOG','/var/log/pyduino/slave.log')),
15+
logging.FileHandler(log_file_path),
1316
logging.StreamHandler()
1417
]
1518
)

pyduino/optimization/nelder_mead.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
from typing import Callable, List
33
from . import Optimizer, linmap
44

5+
def sigmoid(x):
6+
return 1 / (1 + np.exp(-x))
7+
def logit(x, inf=100):
8+
return np.where(x==0, -inf, np.where(x==1, inf, np.log(x) - np.log(1-x)))
9+
510
class NelderMead(Optimizer):
6-
def __init__(self, population_size: int, ranges: List[float], rng_seed: int = 0):
11+
def __init__(self, population_size: int, ranges: List[float], rng_seed: int = 0, hypercube_radius = 100):
712
"""
813
This Nelder-Mead algorithm assumes that the optimization function 'f' is to be minimized and time independent.
914
@@ -20,8 +25,9 @@ def __init__(self, population_size: int, ranges: List[float], rng_seed: int = 0)
2025
self.rng_seed = np.random.default_rng(rng_seed)
2126

2227
# Derived attributes
23-
self.invlinmap = linmap(self.ranges, np.array([[0, 1]] * len(self.ranges)))
24-
self.linmap = linmap(np.array([[0, 1]] * len(self.ranges)), self.ranges)
28+
self.a = 1/hypercube_radius
29+
self.backward = lambda x: logit(linmap(self.ranges, np.array([[0, 1]] * len(self.ranges)))(x))/self.a
30+
self.forward = lambda x: linmap(np.array([[0, 1]] * len(self.ranges)), self.ranges)(sigmoid(self.a * x))
2531

2632
# Initialize the population (random position and initial momentum)
2733
self.population = self.rng_seed.random((self.population_size, len(self.ranges)))
@@ -33,19 +39,19 @@ def view(self, x):
3339
"""
3440
Maps the input from the domain to the codomain.
3541
"""
36-
return self.linmap(x)
42+
return self.forward(x)
3743

3844
def view_g(self):
3945
"""
4046
Maps the input from the domain to the codomain.
4147
"""
42-
return self.linmap(self.population)
48+
return self.forward(self.population)
4349

4450
def inverse_view(self, x):
4551
"""
4652
Maps the input from the codomain to the domain.
4753
"""
48-
return self.invlinmap(x)
54+
return self.backward(x)
4955

5056
def ask_oracle(self, X: np.ndarray) -> np.ndarray:
5157
return super().ask_oracle()

tests/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import dotenv
2+
dotenv.load_dotenv()
3+
dotenv.load_dotenv(dotenv_path=".env.local")

tests/test_linmap.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import sys
33
import os
44
import numpy as np
5+
import dotenv
6+
dotenv.load_dotenv()
7+
dotenv.load_dotenv(dotenv_path=".env.local")
58

69
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
710
from pyduino.optimization import linmap

tests/test_optim.py

+53-34
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,61 @@
11
import sys
22
import os
33
import numpy as np
4+
import dotenv
5+
import dotenv
6+
import pytest
7+
dotenv.load_dotenv()
8+
dotenv.load_dotenv(dotenv_path=".env.local")
49

510
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
611
from pyduino.optimization.nelder_mead import NelderMead
712

8-
def test_nelder_mead_parabola():
13+
class TestOptim:
914
optimizer = NelderMead(population_size=10, ranges=[(-10, 10)] * 4)
10-
class Oracle:
11-
def ask(self, X):
12-
return (X**2).sum(axis=1)
13-
oracle = Oracle()
14-
optimizer.ask_oracle = oracle.ask
15-
16-
# Set initial population to oracle
17-
optimizer.init_oracle()
18-
19-
for i in range(100):
20-
optimizer.step()
21-
assert optimizer.view(optimizer.population).shape == (10, 4)
22-
assert optimizer.view_g().shape == (10, 4)
23-
assert optimizer.inverse_view(optimizer.view(optimizer.population)).shape == (10, 4)
24-
assert np.isclose(optimizer.y.min(), 0, atol=1e-4), f"Oracle: {optimizer.y.min()}"
25-
26-
def test_nelder_mead_rosenbrock():
27-
optimizer = NelderMead(population_size=10, ranges=[(-10, 10)] * 4)
28-
class Oracle:
29-
def ask(self, X):
30-
return ((1 - X[:, :-1])**2).sum(axis=1) + 100 * ((X[:, 1:] - X[:, :-1]**2)**2).sum(axis=1)
31-
oracle = Oracle()
32-
optimizer.ask_oracle = oracle.ask
33-
34-
# Set initial population to oracle
35-
optimizer.init_oracle()
36-
37-
for i in range(1000):
38-
optimizer.step()
39-
assert optimizer.view(optimizer.population).shape == (10, 4)
40-
assert optimizer.view_g().shape == (10, 4)
41-
assert optimizer.inverse_view(optimizer.view(optimizer.population)).shape == (10, 4)
42-
assert np.isclose(optimizer.y.min(), 0, atol=1e-4), f"Oracle: {optimizer.y.min()}"
15+
16+
def test_logic(self):
17+
x = (2*np.random.random((10, 4))-1)*100
18+
19+
print("X")
20+
print(x)
21+
print("Forward -> Backward")
22+
print(self.optimizer.backward(self.optimizer.forward(x)))
23+
24+
assert np.allclose(x, self.optimizer.backward(self.optimizer.forward(x)))
25+
assert np.allclose(x, self.optimizer.inverse_view(self.optimizer.view(x)))
26+
27+
assert np.all(self.optimizer.backward(np.zeros((10,4))) < 1)
28+
29+
def test_nelder_mead_parabola(self):
30+
class Oracle:
31+
def ask(self, X):
32+
return (X**2).sum(axis=1)
33+
oracle = Oracle()
34+
self.optimizer.ask_oracle = oracle.ask
35+
36+
# Set initial population to oracle
37+
self.optimizer.init_oracle()
38+
39+
for i in range(100):
40+
self.optimizer.step()
41+
assert self.optimizer.view(self.optimizer.population).shape == (10, 4)
42+
assert self.optimizer.view_g().shape == (10, 4)
43+
assert self.optimizer.inverse_view(self.optimizer.view(self.optimizer.population)).shape == (10, 4)
44+
assert np.isclose(self.optimizer.y.min(), 0, atol=1e-4), f"Oracle: {self.optimizer.y.min()}"
45+
46+
def test_nelder_mead_rosenbrock(self):
47+
class Oracle:
48+
def ask(self, X):
49+
return ((1 - X[:, :-1])**2).sum(axis=1) + 100 * ((X[:, 1:] - X[:, :-1]**2)**2).sum(axis=1)
50+
oracle = Oracle()
51+
self.optimizer.ask_oracle = oracle.ask
52+
53+
# Set initial population to oracle
54+
self.optimizer.init_oracle()
55+
56+
for i in range(1000):
57+
self.optimizer.step()
58+
assert self.optimizer.view(self.optimizer.population).shape == (10, 4)
59+
assert self.optimizer.view_g().shape == (10, 4)
60+
assert self.optimizer.inverse_view(self.optimizer.view(self.optimizer.population)).shape == (10, 4)
61+
assert np.isclose(self.optimizer.y.min(), 0, atol=1e-4), f"Oracle: {self.optimizer.y.min()}"

tests/test_spectra.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import os
33
import numpy as np
44
import pandas as pd
5+
import dotenv
6+
dotenv.load_dotenv()
7+
dotenv.load_dotenv(dotenv_path=".env.local")
58

69
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
710
from pyduino.spectra import Spectra, PATHS

tests/test_utils.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import os
33
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
44
from pyduino.utils import get_param
5+
import dotenv
6+
dotenv.load_dotenv()
7+
dotenv.load_dotenv(dotenv_path=".env.local")
58

69
def test_get_param():
710
# Test case 1: No IDs provided

0 commit comments

Comments
 (0)