54
54
55
55
import abc
56
56
import enum
57
+ import gin
57
58
import math
58
59
59
60
import numpy as np
64
65
65
66
from compiler_opt .es import gradient_ascent_optimization_algorithms
66
67
67
- SequenceOfFloats = Union [Sequence [float ], npt .NDArray [np .float32 ]]
68
+ FloatArray = npt .NDArray [np .float32 ]
69
+
70
+ # should specifically be a 2d numpy array of floats
71
+ # but numpy.typing does not allow for that indication
72
+ FloatArray2D = Sequence [FloatArray ]
73
+
74
+ SequenceOfFloats = Union [Sequence [float ], FloatArray ]
68
75
69
76
LinearModel = Union [linear_model .Ridge , linear_model .Lasso ,
70
77
linear_model .LinearRegression ]
@@ -75,22 +82,33 @@ class CurrentPointEstimate(enum.Enum):
75
82
AVERAGE = 2
76
83
77
84
85
+ @gin .constants_from_enum (module = 'blackbox_optimizers' )
86
+ class Algorithm (enum .Enum ):
87
+ MONTE_CARLO = 1
88
+ TRUST_REGION = 2
89
+ SKLEARN_REGRESSION = 3
90
+
91
+
92
+ @gin .constants_from_enum (module = 'blackbox_optimizers' )
78
93
class EstimatorType (enum .Enum ):
79
94
FORWARD_FD = 1
80
95
ANTITHETIC = 2
81
96
82
97
98
+ @gin .constants_from_enum (module = 'blackbox_optimizers' )
83
99
class GradientType (enum .Enum ):
84
100
MC = 1
85
101
REGRESSION = 2
86
102
87
103
104
+ @gin .constants_from_enum (module = 'blackbox_optimizers' )
88
105
class RegressionType (enum .Enum ):
89
106
LASSO = 1
90
107
RIDGE = 2
91
108
LINEAR = 3
92
109
93
110
111
+ @gin .constants_from_enum (module = 'blackbox_optimizers' )
94
112
class UpdateMethod (enum .Enum ):
95
113
STATE_NORMALIZATION = 1
96
114
NO_METHOD = 2
@@ -100,10 +118,9 @@ class UpdateMethod(enum.Enum):
100
118
101
119
102
120
def filter_top_directions (
103
- perturbations : npt .NDArray [np .float32 ],
104
- function_values : npt .NDArray [np .float32 ], est_type : EstimatorType ,
105
- num_top_directions : int
106
- ) -> Tuple [npt .NDArray [np .float32 ], npt .NDArray [np .float32 ]]:
121
+ perturbations : FloatArray2D , function_values : FloatArray ,
122
+ est_type : EstimatorType ,
123
+ num_top_directions : int ) -> Tuple [FloatArray , FloatArray ]:
107
124
"""Select the subset of top-performing perturbations.
108
125
109
126
Args:
@@ -151,10 +168,8 @@ class BlackboxOptimizer(metaclass=abc.ABCMeta):
151
168
"""
152
169
153
170
@abc .abstractmethod
154
- def run_step (self , perturbations : npt .NDArray [np .float32 ],
155
- function_values : npt .NDArray [np .float32 ],
156
- current_input : npt .NDArray [np .float32 ],
157
- current_value : float ) -> npt .NDArray [np .float32 ]:
171
+ def run_step (self , perturbations : FloatArray2D , function_values : FloatArray ,
172
+ current_input : FloatArray , current_value : float ) -> FloatArray :
158
173
"""Conducts a single step of blackbox optimization procedure.
159
174
160
175
Conducts a single step of blackbox optimization procedure, given values of
@@ -332,10 +347,9 @@ def __init__(self,
332
347
super ().__init__ (est_type , normalize_fvalues , hyperparameters_update_method ,
333
348
extra_params )
334
349
335
- def run_step (self , perturbations : npt .NDArray [np .float32 ],
336
- function_values : npt .NDArray [np .float32 ],
337
- current_input : npt .NDArray [np .float32 ],
338
- current_value : float ) -> npt .NDArray [np .float32 ]:
350
+ # TODO: Issue #285
351
+ def run_step (self , perturbations : FloatArray2D , function_values : FloatArray ,
352
+ current_input : FloatArray , current_value : float ) -> FloatArray :
339
353
dim = len (current_input )
340
354
if self .normalize_fvalues :
341
355
values = function_values .tolist ()
@@ -413,10 +427,8 @@ def __init__(self,
413
427
super ().__init__ (est_type , normalize_fvalues , hyperparameters_update_method ,
414
428
extra_params )
415
429
416
- def run_step (self , perturbations : npt .NDArray [np .float32 ],
417
- function_values : npt .NDArray [np .float32 ],
418
- current_input : npt .NDArray [np .float32 ],
419
- current_value : float ) -> npt .NDArray [np .float32 ]:
430
+ def run_step (self , perturbations : FloatArray2D , function_values : FloatArray ,
431
+ current_input : FloatArray , current_value : float ) -> FloatArray :
420
432
dim = len (current_input )
421
433
if self .normalize_fvalues :
422
434
values = function_values .tolist ()
@@ -474,8 +486,8 @@ def set_state(self, state: SequenceOfFloats) -> None:
474
486
475
487
476
488
def normalize_function_values (
477
- function_values : npt . NDArray [ np . float32 ] ,
478
- current_value : float ) -> Tuple [npt . NDArray [ np . float32 ] , List [float ]]:
489
+ function_values : FloatArray ,
490
+ current_value : float ) -> Tuple [FloatArray , List [float ]]:
479
491
values = function_values .tolist ()
480
492
values .append (current_value )
481
493
mean = sum (values ) / float (len (values ))
@@ -484,13 +496,12 @@ def normalize_function_values(
484
496
return (np .array (normalized_values [:- 1 ]), normalized_values [- 1 ])
485
497
486
498
487
- def monte_carlo_gradient (
488
- precision_parameter : float ,
489
- est_type : EstimatorType ,
490
- perturbations : npt .NDArray [np .float32 ],
491
- function_values : npt .NDArray [np .float32 ],
492
- current_value : float ,
493
- energy : Optional [float ] = 0 ) -> npt .NDArray [np .float32 ]:
499
+ def monte_carlo_gradient (precision_parameter : float ,
500
+ est_type : EstimatorType ,
501
+ perturbations : FloatArray2D ,
502
+ function_values : FloatArray ,
503
+ current_value : float ,
504
+ energy : Optional [float ] = 0 ) -> FloatArray :
494
505
"""Calculates Monte Carlo gradient.
495
506
496
507
There are several ways of estimating the gradient. This is specified by the
@@ -530,11 +541,10 @@ def monte_carlo_gradient(
530
541
return gradient
531
542
532
543
533
- def sklearn_regression_gradient (
534
- clf : LinearModel , est_type : EstimatorType ,
535
- perturbations : npt .NDArray [np .float32 ],
536
- function_values : npt .NDArray [np .float32 ],
537
- current_value : float ) -> npt .NDArray [np .float32 ]:
544
+ def sklearn_regression_gradient (clf : LinearModel , est_type : EstimatorType ,
545
+ perturbations : FloatArray2D ,
546
+ function_values : FloatArray ,
547
+ current_value : float ) -> FloatArray :
538
548
"""Calculates gradient by function difference regression.
539
549
540
550
Args:
@@ -603,8 +613,8 @@ class QuadraticModel(object):
603
613
# pylint: disable=invalid-name
604
614
# argument Av should be capitalized as such for mathematical convention
605
615
def __init__ (self ,
606
- Av : Callable [[npt . NDArray [ np . float32 ]], npt . NDArray [ np . float32 ] ],
607
- b : npt . NDArray [ np . float32 ] ,
616
+ Av : Callable [[FloatArray ], FloatArray ],
617
+ b : FloatArray ,
608
618
c : Optional [float ] = 0 ):
609
619
"""Initialize quadratic function.
610
620
@@ -619,7 +629,7 @@ def __init__(self,
619
629
self .c = c
620
630
621
631
# pylint: enable=invalid-name
622
- def f (self , x : npt . NDArray [ np . float32 ] ) -> float :
632
+ def f (self , x : FloatArray ) -> float :
623
633
"""Evaluate the quadratic function.
624
634
625
635
Args:
@@ -629,7 +639,7 @@ def f(self, x: npt.NDArray[np.float32]) -> float:
629
639
"""
630
640
return 0.5 * np .dot (x , self .quad_v (x )) + np .dot (x , self .b ) + self .c
631
641
632
- def grad (self , x : npt . NDArray [ np . float32 ] ) -> npt . NDArray [ np . float32 ] :
642
+ def grad (self , x : FloatArray ) -> FloatArray :
633
643
"""Evaluate the gradient of the quadratic, Ax + b.
634
644
635
645
Args:
@@ -653,9 +663,8 @@ class ProjectedGradientOptimizer(object):
653
663
"""
654
664
655
665
def __init__ (self , function_object : QuadraticModel ,
656
- projection_operator : Callable [[npt .NDArray [np .float32 ]],
657
- npt .NDArray [np .float32 ]],
658
- pgd_params : Mapping [str , Any ], x_init : npt .NDArray [np .float32 ]):
666
+ projection_operator : Callable [[FloatArray ], FloatArray ],
667
+ pgd_params : Mapping [str , Any ], x_init : FloatArray ):
659
668
self .f = function_object
660
669
self .proj = projection_operator
661
670
if pgd_params is not None :
@@ -698,7 +707,7 @@ def run_step(self) -> None:
698
707
self .x = x_next
699
708
self .k += 1
700
709
701
- def get_solution (self ) -> npt . NDArray [ np . float32 ] :
710
+ def get_solution (self ) -> FloatArray :
702
711
return self .x
703
712
704
713
def get_x_diff_norm (self ) -> float :
@@ -708,9 +717,7 @@ def get_iterations(self) -> int:
708
717
return self .k
709
718
710
719
711
- def make_projector (
712
- radius : float
713
- ) -> Callable [[npt .NDArray [np .float32 ]], npt .NDArray [np .float32 ]]:
720
+ def make_projector (radius : float ) -> Callable [[FloatArray ], FloatArray ]:
714
721
"""Makes an L2 projector function centered at origin.
715
722
716
723
Args:
@@ -719,7 +726,7 @@ def make_projector(
719
726
A function of one argument that projects onto L2 ball.
720
727
"""
721
728
722
- def projector (w : npt . NDArray [ np . float32 ] ) -> npt . NDArray [ np . float32 ] :
729
+ def projector (w : FloatArray ) -> FloatArray :
723
730
w_norm = np .linalg .norm (w , 2 )
724
731
if w_norm > radius :
725
732
return radius / w_norm * w
@@ -748,7 +755,7 @@ class TrustRegionSubproblemOptimizer(object):
748
755
def __init__ (self ,
749
756
model_function : QuadraticModel ,
750
757
trust_region_params : Dict [str , Any ],
751
- x_init : Optional [npt . NDArray [ np . float32 ] ] = None ):
758
+ x_init : Optional [FloatArray ] = None ):
752
759
self .mf = model_function
753
760
self .params = trust_region_params
754
761
self .center = x_init
@@ -783,7 +790,7 @@ def solve_trust_region_subproblem(self) -> None:
783
790
784
791
self .x = pgd_solver .get_solution ()
785
792
786
- def get_solution (self ) -> npt . NDArray [ np . float32 ] :
793
+ def get_solution (self ) -> FloatArray :
787
794
return self .x
788
795
789
796
@@ -913,7 +920,7 @@ def __init__(self, precision_parameter: float, est_type: EstimatorType,
913
920
self .clf = linear_model .Lasso (alpha = self .params ['grad_reg_alpha' ])
914
921
self .is_returned_step = False
915
922
916
- def trust_region_test (self , current_input : npt . NDArray [ np . float32 ] ,
923
+ def trust_region_test (self , current_input : FloatArray ,
917
924
current_value : float ) -> bool :
918
925
"""Test the next step to determine how to update the trust region.
919
926
@@ -981,9 +988,9 @@ def trust_region_test(self, current_input: npt.NDArray[np.float32],
981
988
print ('Unchanged: ' + str (self .radius ) + log_message )
982
989
return True
983
990
984
- def update_hessian_part (self , perturbations : npt . NDArray [ np . float32 ] ,
985
- function_values : npt . NDArray [ np . float32 ] ,
986
- current_value : float , is_update : bool ) -> None :
991
+ def update_hessian_part (self , perturbations : FloatArray2D ,
992
+ function_values : FloatArray , current_value : float ,
993
+ is_update : bool ) -> None :
987
994
"""Updates the internal state which stores Hessian information.
988
995
989
996
Recall that the Hessian is given by
@@ -1046,13 +1053,12 @@ def update_hessian_part(self, perturbations: npt.NDArray[np.float32],
1046
1053
self .saved_function_values = np .append (self .saved_function_values ,
1047
1054
function_values )
1048
1055
1049
- def create_hessv_function (
1050
- self ) -> Callable [[npt .NDArray [np .float32 ]], npt .NDArray [np .float32 ]]:
1056
+ def create_hessv_function (self ) -> Callable [[FloatArray ], FloatArray ]:
1051
1057
"""Returns a function of one argument that evaluates Hessian-vector product.
1052
1058
"""
1053
1059
if self .params ['dense_hessian' ]:
1054
1060
1055
- def hessv_func (x : npt . NDArray [ np . float32 ] ) -> npt . NDArray [ np . float32 ] :
1061
+ def hessv_func (x : FloatArray ) -> FloatArray :
1056
1062
"""Calculates Hessian-vector product from dense Hessian.
1057
1063
1058
1064
Args:
@@ -1068,7 +1074,7 @@ def hessv_func(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
1068
1074
return hessv
1069
1075
else :
1070
1076
1071
- def hessv_func (x : npt . NDArray [ np . float32 ] ) -> npt . NDArray [ np . float32 ] :
1077
+ def hessv_func (x : FloatArray ) -> FloatArray :
1072
1078
"""Calculates Hessian-vector product from perturbation/value pairs.
1073
1079
1074
1080
Args:
@@ -1095,9 +1101,8 @@ def hessv_func(x: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
1095
1101
1096
1102
return hessv_func
1097
1103
1098
- def update_quadratic_model (self , perturbations : npt .NDArray [np .float32 ],
1099
- function_values : npt .NDArray [np .float32 ],
1100
- current_value : float ,
1104
+ def update_quadratic_model (self , perturbations : FloatArray2D ,
1105
+ function_values : FloatArray , current_value : float ,
1101
1106
is_update : bool ) -> QuadraticModel :
1102
1107
"""Updates the internal state of the optimizer with new perturbations.
1103
1108
@@ -1145,10 +1150,8 @@ def update_quadratic_model(self, perturbations: npt.NDArray[np.float32],
1145
1150
is_update )
1146
1151
return QuadraticModel (self .create_hessv_function (), self .saved_gradient )
1147
1152
1148
- def run_step (self , perturbations : npt .NDArray [np .float32 ],
1149
- function_values : npt .NDArray [np .float32 ],
1150
- current_input : npt .NDArray [np .float32 ],
1151
- current_value : float ) -> npt .NDArray [np .float32 ]:
1153
+ def run_step (self , perturbations : FloatArray2D , function_values : FloatArray ,
1154
+ current_input : FloatArray , current_value : float ) -> FloatArray :
1152
1155
"""Run a single step of trust region optimizer.
1153
1156
1154
1157
Args:
0 commit comments