-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathspaces.py
122 lines (97 loc) · 3.75 KB
/
spaces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from abc import ABC, abstractmethod, abstractproperty
import torch
from scipy import stats
import numpy as np
from scipy.stats import truncnorm
import sys
class Space(ABC):
@abstractmethod
def uniform(self, size, device):
pass
@abstractmethod
def normal(self, mean, std, size, device):
pass
@abstractmethod
def laplace(self, mean, std, size, device):
pass
@abstractmethod
def generalized_normal(self, mean, lbd, p, size, device):
pass
@property
@abstractmethod
def dim(self):
pass
class NRealSpace(Space):
def __init__(self, n):
self.n = n
@property
def dim(self):
return self.n
def uniform(self, size, device="cpu"):
raise NotImplementedError("Not defined on R^n")
def normal(self, mean, std, size, device="cpu", change_prob=1., Sigma=None):
"""Sample from a Normal distribution in R^N.
Args:
mean: Value(s) to sample around.
std: Concentration parameter of the distribution (=standard deviation).
size: Number of samples to draw.
device: torch device identifier
"""
if mean is None:
mean = torch.zeros(self.n)
if len(mean.shape) == 1 and mean.shape[0] == self.n:
mean = mean.unsqueeze(0)
if not torch.is_tensor(std):
std = torch.ones(self.n) * std
if len(std.shape) == 1 and std.shape[0] == self.n:
std = std.unsqueeze(0)
assert len(mean.shape) == 2
assert len(std.shape) == 2
if torch.is_tensor(mean):
mean = mean.to(device)
if torch.is_tensor(std):
std = std.to(device)
change_indices = torch.distributions.binomial.Binomial(probs=change_prob).sample((size, self.n)).to(device)
if Sigma is not None:
changes = np.random.multivariate_normal(np.zeros(self.n), Sigma, size)
changes = torch.FloatTensor(changes).to(device)
else:
changes = torch.randn((size, self.n), device=device) * std
return mean + change_indices * changes
def laplace(self, mean, lbd, size, device="cpu"):
raise NotImplementedError("Not used")
def generalized_normal(self, mean, lbd, p, size, device=None):
raise NotImplementedError("Not used")
class NBoxSpace(Space):
def __init__(self, n, min_=-1, max_=1):
self.n = n
self.min_ = min_
self.max_ = max_
@property
def dim(self):
return self.n
def uniform(self, size, device="cpu"):
raise NotImplementedError("Not used")
def normal(self, mean, std, size, device="cpu", change_prob=1., statistical_dependence=False):
"""Sample from a Normal distribution in R^N and then restrict the samples to a box.
Args:
mean: Value(s) to sample around.
std: Concentration parameter of the distribution (=standard deviation).
size: Number of samples to draw.
device: torch device identifier
"""
assert len(mean.shape) == 1 or (len(mean.shape) == 2 and len(mean) == size)
assert mean.shape[-1] == self.n
if len(mean.shape) == 1:
mean = mean.unsqueeze(0)
mean = mean.to(device)
mean_np = mean.detach().cpu().numpy()
a = (self.min_ - mean_np) / std
b = (self.max_ - mean_np) / std
unnormalised_samples = truncnorm.rvs(a, b, size=(size, self.n))
samples = torch.FloatTensor(std * unnormalised_samples + mean_np, device=device)
return samples
def laplace(self, mean, lbd, size, device="cpu"):
raise NotImplementedError("Not used")
def generalized_normal(self, mean, lbd, p, size, device=None):
raise NotImplementedError("Not used")