-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSetsFromFunctions.py
More file actions
89 lines (76 loc) · 4.52 KB
/
SetsFromFunctions.py
File metadata and controls
89 lines (76 loc) · 4.52 KB
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
import numpy as np
from DifferentiableFunction import IDifferentiableFunction, DifferentiableFunction
from Set import MultidimensionalInterval
from multimethod import multimethod
from BFGS import BFGS
class BoundedSet(MultidimensionalInterval):
"""This class models bounded set in an affine space. The bounded set is given as intersection of a MultidimensionalInterval and an inequality constraint.
An inequality constrint is given by a differentiable functions f where x satisfies the constraint if all components of f(x) are non-positive."""
def __init__(self, lower_bounds: np.ndarray, upper_bounds: np.ndarray, InequalityConstraints: IDifferentiableFunction):
super().__init__(lower_bounds=lower_bounds, upper_bounds=upper_bounds)
self._InequalityConstraints = InequalityConstraints
def contains(self, point: np.ndarray) -> bool:
return super().contains(point) and self.InequalityConstraints.evaluate(point).max() <= 0
def point(self) -> np.ndarray:
starting_point = np.array([np.random.uniform(
self.lower_bounds[i], self.upper_bounds[i]) for i in range(self._ambient_dimension)])
bfgs = BFGS()
penalty_function = DifferentiableFunction.FromComposition(
DifferentiableFunction.ReLU(dimension=self._ambient_dimension), self.InequalityConstraints)
penalty_function = DifferentiableFunction.FromComposition(
DifferentiableFunction.TwoNormSquared(dimension=self.InequalityConstraints.evaluate(starting_point).shape[0]), penalty_function)
# intersect penalty function with a smaller domain by adding a suitable zero function with constraints
penalty_function = penalty_function + DifferentiableFunction(
name="0", domain=MultidimensionalInterval(lower_bounds=self.lower_bounds, upper_bounds=self.upper_bounds), evaluate=lambda x: np.array([0]), jacobian=lambda x: (0*x).reshape(1, -1))
x = bfgs.Minimize(penalty_function,
startingpoint=starting_point, tol_x=1e-9, tol_y=1e-9, )
if penalty_function.evaluate(x) <= 0:
return x
else:
return None
def project(self, point: np.array) -> np.array:
"""
Projects a point onto the bounded set
:param point: Point to project
:return: Projected point
"""
projected_point = np.minimum(np.maximum(point, self.lower_bounds), self.upper_bounds)
# If there are inequality constraints, project onto them as well
if self._InequalityConstraints is not None:
while np.any(self._InequalityConstraints.evaluate(projected_point) > 0):
gradient = self._InequalityConstraints.jacobian(projected_point)
projected_point -= gradient * self._InequalityConstraints.evaluate(projected_point)
projected_point = np.minimum(np.maximum(projected_point, self.lower_bounds), self.upper_bounds)
return projected_point
@ property
def InequalityConstraints(self) -> IDifferentiableFunction:
"""Returns the function describing the inequality constraints of this set"""
return self._InequalityConstraints
@ property
def upper_bounds(self) -> np.ndarray:
"""Returns the functions that sets the inequality constraints"""
return self._upper_bounds
@ multimethod
def intersect(self: 'BoundedSet', other: 'BoundedSet') -> 'BoundedSet':
"""Intersects two BoundedSets"""
return BoundedSet(
lower_bounds=np.maximum(self.lower_bounds, other.lower_bounds),
upper_bounds=np.minimum(self.upper_bounds, other.upper_bounds),
InequalityConstraints=self.InequalityConstraints.Pairing(
other.InequalityConstraints)
)
@ multimethod
def intersect(self: 'BoundedSet', other: 'MultidimensionalInterval') -> 'BoundedSet':
"""Intersects a BoundedSet with a MultidimensionalInterval"""
lowerBounds = self.lower_bounds
if isinstance(other, MultidimensionalInterval):
lowerBounds = np.maximum(lowerBounds, other.lower_bounds)
upperBounds = self.upper_bounds
if isinstance(other, MultidimensionalInterval):
upperBounds = np.minimum(upperBounds, other.upper_bounds)
return BoundedSet(
lower_bounds=lowerBounds,
upper_bounds=upperBounds,
InequalityConstraints=self.InequalityConstraints
)
# ToDo: CartesianProducts