diff --git a/suggestion/grid/main.go b/suggestion/grid/main.go index 5317a1d3164..990a4159631 100644 --- a/suggestion/grid/main.go +++ b/suggestion/grid/main.go @@ -1,192 +1,24 @@ package main import ( - "context" - "fmt" - "github.com/kubeflow/hp-tuning/api" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" "log" "net" - "strconv" -) - -const ( - port = "0.0.0.0:6789" -) - -type GridSuggestParameters struct { - defaultGridNum int - gridConfig map[string]int - MaxParallel int -} - -type GridSuggestService struct { - parameters map[string]*GridSuggestParameters - grids map[string][][]*api.Parameter - gridPointer map[string]int -} - -func NewGridSuggestService() *GridSuggestService { - return &GridSuggestService{parameters: make(map[string]*GridSuggestParameters), grids: make(map[string][][]*api.Parameter), gridPointer: make(map[string]int)} -} - -func (s *GridSuggestService) SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) { - p := &GridSuggestParameters{gridConfig: make(map[string]int)} - for _, sp := range in.SuggestionParameters { - switch sp.Name { - case "DefaultGrid": - p.defaultGridNum, _ = strconv.Atoi(sp.Value) - case "MaxParrallel": - p.MaxParallel, _ = strconv.Atoi(sp.Value) - default: - p.gridConfig[sp.Name], _ = strconv.Atoi(sp.Value) - } - } - s.parameters[in.StudyId] = p - return &api.SetSuggestionParametersReply{}, nil -} - -func (s *GridSuggestService) allocInt(min int, max int, reqnum int) []string { - ret := make([]string, reqnum) - if reqnum == 1 { - ret[0] = strconv.Itoa(min) - } else { - for i := 0; i < reqnum; i++ { - ret[i] = strconv.Itoa(min + ((max - min) * i / (reqnum - 1))) - } - } - return ret -} - -func (s *GridSuggestService) allocFloat(min float64, max float64, reqnum int) []string { - ret := make([]string, reqnum) - if reqnum == 1 { - ret[0] = strconv.FormatFloat(min, 'f', 4, 64) - } else { - for i := 0; i < reqnum; i++ { - ret[i] = strconv.FormatFloat(min+(((max-min)/float64(reqnum-1))*float64(i)), 'f', 4, 64) - } - } - return ret -} -func (s *GridSuggestService) allocCat(list []string, reqnum int) []string { - ret := make([]string, reqnum) - if reqnum == 1 { - ret[0] = list[0] - } else { - for i := 0; i < reqnum; i++ { - ret[i] = list[(((len(list) - 1) * i) / (reqnum - 1))] - fmt.Printf("ret %v %v\n", i, ret[i]) - } - } - return ret -} - -func (s *GridSuggestService) setP(gci int, p [][]*api.Parameter, pg [][]string, pcs []*api.ParameterConfig) { - if gci == len(pg)-1 { - for i := range pg[gci] { - p[i] = append(p[i], &api.Parameter{ - Name: pcs[gci].Name, - ParameterType: pcs[gci].ParameterType, - Value: pg[gci][i], - }) - - } - return - } else { - d := len(p) / len(pg[gci]) - for i := range pg[gci] { - for j := d * i; j < d*(i+1); j++ { - p[j] = append(p[j], &api.Parameter{ - Name: pcs[gci].Name, - ParameterType: pcs[gci].ParameterType, - Value: pg[gci][i], - }) - } - s.setP(gci+1, p[d*i:d*(i+1)], pg, pcs) - } - } -} - -func (s *GridSuggestService) genGrids(studyId string, pcs []*api.ParameterConfig) [][]*api.Parameter { - var pg [][]string - var holenum int = 1 - gcl := make([]int, len(pcs)) - for i, pc := range pcs { - gc, ok := s.parameters[studyId].gridConfig[pc.Name] - if !ok { - gc = s.parameters[studyId].defaultGridNum - } - holenum *= gc - gcl[i] = gc - switch pc.ParameterType { - case api.ParameterType_INT: - imin, _ := strconv.Atoi(pc.Feasible.Min) - imax, _ := strconv.Atoi(pc.Feasible.Max) - pg = append(pg, s.allocInt(imin, imax, gc)) - case api.ParameterType_DOUBLE: - dmin, _ := strconv.ParseFloat(pc.Feasible.Min, 64) - dmax, _ := strconv.ParseFloat(pc.Feasible.Max, 64) - pg = append(pg, s.allocFloat(dmin, dmax, gc)) - case api.ParameterType_CATEGORICAL: - pg = append(pg, s.allocCat(pc.Feasible.List, gc)) - } - } - ret := make([][]*api.Parameter, holenum) - s.setP(0, ret, pg, pcs) - log.Printf("Study %v : %v parameters generated", studyId, holenum) - return ret -} - -func (s *GridSuggestService) GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) { - if _, ok := s.grids[in.StudyId]; !ok { - s.grids[in.StudyId] = s.genGrids(in.StudyId, in.Configs.ParameterConfigs.Configs) - s.gridPointer[in.StudyId] = 0 - } - if s.gridPointer[in.StudyId] >= len(s.grids[in.StudyId]) { - if len(in.RunningTrials) == 0 { - s.StopSuggestion(ctx, &api.StopSuggestionRequest{StudyId: in.StudyId}) - return &api.GenerateTrialsReply{Completed: true}, nil - } else { - return &api.GenerateTrialsReply{Completed: false}, nil - } - } - var reqnum int = 0 - if s.parameters[in.StudyId].MaxParallel <= 0 { - reqnum = len(s.grids[in.StudyId]) - } else if len(s.grids[in.StudyId])-s.gridPointer[in.StudyId] < s.parameters[in.StudyId].MaxParallel-len(in.RunningTrials) { - reqnum = len(s.grids[in.StudyId]) - s.gridPointer[in.StudyId] - } else if len(in.RunningTrials) < s.parameters[in.StudyId].MaxParallel { - reqnum = s.parameters[in.StudyId].MaxParallel - len(in.RunningTrials) - } - s_t := make([]*api.Trial, reqnum) - for i := 0; i < int(reqnum); i++ { - s_t[i] = &api.Trial{} - s_t[i].Status = api.TrialState_PENDING - s_t[i].EvalLogs = make([]*api.EvaluationLog, 0) - s_t[i].ParameterSet = s.grids[in.StudyId][s.gridPointer[in.StudyId]+i] - } - s.gridPointer[in.StudyId] += reqnum - return &api.GenerateTrialsReply{Trials: s_t, Completed: false}, nil -} + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" -func (s *GridSuggestService) StopSuggestion(ctx context.Context, in *api.StopSuggestionRequest) (*api.StopSuggestionReply, error) { - delete(s.gridPointer, in.StudyId) - delete(s.grids, in.StudyId) - delete(s.parameters, in.StudyId) - return &api.StopSuggestionReply{}, nil -} + pb "github.com/kubeflow/hp-tuning/api" + "github.com/kubeflow/hp-tuning/suggestion" +) func main() { - listener, err := net.Listen("tcp", port) + listener, err := net.Listen("tcp", suggestion.DefaultPort) if err != nil { log.Fatalf("Failed to listen: %v", err) } size := 1<<31 - 1 s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size)) - api.RegisterSuggestionServer(s, NewGridSuggestService()) + pb.RegisterSuggestionServer(s, suggestion.NewGridSuggestService()) reflection.Register(s) log.Printf("Grid Search Suggestion Service\n") if err = s.Serve(listener); err != nil { diff --git a/suggestion/grid_service.go b/suggestion/grid_service.go new file mode 100644 index 00000000000..204afbb52f1 --- /dev/null +++ b/suggestion/grid_service.go @@ -0,0 +1,174 @@ +package suggestion + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/kubeflow/hp-tuning/api" +) + +type GridSuggestParameters struct { + defaultGridNum int + gridConfig map[string]int + MaxParallel int +} + +type GridSuggestService struct { + parameters map[string]*GridSuggestParameters + grids map[string][][]*api.Parameter + gridPointer map[string]int +} + +func NewGridSuggestService() *GridSuggestService { + return &GridSuggestService{parameters: make(map[string]*GridSuggestParameters), grids: make(map[string][][]*api.Parameter), gridPointer: make(map[string]int)} +} + +func (s *GridSuggestService) SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) { + p := &GridSuggestParameters{gridConfig: make(map[string]int)} + for _, sp := range in.SuggestionParameters { + switch sp.Name { + case "DefaultGrid": + p.defaultGridNum, _ = strconv.Atoi(sp.Value) + case "MaxParallel": + p.MaxParallel, _ = strconv.Atoi(sp.Value) + default: + p.gridConfig[sp.Name], _ = strconv.Atoi(sp.Value) + } + } + s.parameters[in.StudyId] = p + return &api.SetSuggestionParametersReply{}, nil +} + +func (s *GridSuggestService) allocInt(min int, max int, reqnum int) []string { + ret := make([]string, reqnum) + if reqnum == 1 { + ret[0] = strconv.Itoa(min) + } else { + for i := 0; i < reqnum; i++ { + ret[i] = strconv.Itoa(min + ((max - min) * i / (reqnum - 1))) + } + } + return ret +} + +func (s *GridSuggestService) allocFloat(min float64, max float64, reqnum int) []string { + ret := make([]string, reqnum) + if reqnum == 1 { + ret[0] = strconv.FormatFloat(min, 'f', 4, 64) + } else { + for i := 0; i < reqnum; i++ { + ret[i] = strconv.FormatFloat(min+(((max-min)/float64(reqnum-1))*float64(i)), 'f', 4, 64) + } + } + return ret +} + +func (s *GridSuggestService) allocCat(list []string, reqnum int) []string { + ret := make([]string, reqnum) + if reqnum == 1 { + ret[0] = list[0] + } else { + for i := 0; i < reqnum; i++ { + ret[i] = list[(((len(list) - 1) * i) / (reqnum - 1))] + fmt.Printf("ret %v %v\n", i, ret[i]) + } + } + return ret +} + +func (s *GridSuggestService) setP(gci int, p [][]*api.Parameter, pg [][]string, pcs []*api.ParameterConfig) { + if gci == len(pg)-1 { + for i := range pg[gci] { + p[i] = append(p[i], &api.Parameter{ + Name: pcs[gci].Name, + ParameterType: pcs[gci].ParameterType, + Value: pg[gci][i], + }) + + } + return + } else { + d := len(p) / len(pg[gci]) + for i := range pg[gci] { + for j := d * i; j < d*(i+1); j++ { + p[j] = append(p[j], &api.Parameter{ + Name: pcs[gci].Name, + ParameterType: pcs[gci].ParameterType, + Value: pg[gci][i], + }) + } + s.setP(gci+1, p[d*i:d*(i+1)], pg, pcs) + } + } +} + +func (s *GridSuggestService) genGrids(studyId string, pcs []*api.ParameterConfig) [][]*api.Parameter { + var pg [][]string + var holenum = 1 + gcl := make([]int, len(pcs)) + for i, pc := range pcs { + gc, ok := s.parameters[studyId].gridConfig[pc.Name] + if !ok { + gc = s.parameters[studyId].defaultGridNum + } + holenum *= gc + gcl[i] = gc + switch pc.ParameterType { + case api.ParameterType_INT: + imin, _ := strconv.Atoi(pc.Feasible.Min) + imax, _ := strconv.Atoi(pc.Feasible.Max) + pg = append(pg, s.allocInt(imin, imax, gc)) + case api.ParameterType_DOUBLE: + dmin, _ := strconv.ParseFloat(pc.Feasible.Min, 64) + dmax, _ := strconv.ParseFloat(pc.Feasible.Max, 64) + pg = append(pg, s.allocFloat(dmin, dmax, gc)) + case api.ParameterType_CATEGORICAL: + pg = append(pg, s.allocCat(pc.Feasible.List, gc)) + } + } + ret := make([][]*api.Parameter, holenum) + s.setP(0, ret, pg, pcs) + log.Printf("Study %v : %v parameters generated", studyId, holenum) + return ret +} + +func (s *GridSuggestService) GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) { + if _, ok := s.grids[in.StudyId]; !ok { + s.grids[in.StudyId] = s.genGrids(in.StudyId, in.Configs.ParameterConfigs.Configs) + s.gridPointer[in.StudyId] = 0 + } + if s.gridPointer[in.StudyId] >= len(s.grids[in.StudyId]) { + if len(in.RunningTrials) == 0 { + s.StopSuggestion(ctx, &api.StopSuggestionRequest{StudyId: in.StudyId}) + return &api.GenerateTrialsReply{Completed: true}, nil + } else { + return &api.GenerateTrialsReply{Completed: false}, nil + } + } + var reqnum = 0 + if s.parameters[in.StudyId].MaxParallel <= 0 { + reqnum = len(s.grids[in.StudyId]) + } else if len(s.grids[in.StudyId])-s.gridPointer[in.StudyId] < s.parameters[in.StudyId].MaxParallel-len(in.RunningTrials) { + reqnum = len(s.grids[in.StudyId]) - s.gridPointer[in.StudyId] + } else if len(in.RunningTrials) < s.parameters[in.StudyId].MaxParallel { + reqnum = s.parameters[in.StudyId].MaxParallel - len(in.RunningTrials) + } + trials := make([]*api.Trial, reqnum) + for i := 0; i < int(reqnum); i++ { + trials[i] = &api.Trial{} + trials[i].Status = api.TrialState_PENDING + trials[i].EvalLogs = make([]*api.EvaluationLog, 0) + trials[i].ParameterSet = s.grids[in.StudyId][s.gridPointer[in.StudyId]+i] + } + s.gridPointer[in.StudyId] += reqnum + return &api.GenerateTrialsReply{Trials: trials, Completed: false}, nil +} + +func (s *GridSuggestService) StopSuggestion(ctx context.Context, in *api.StopSuggestionRequest) (*api.StopSuggestionReply, error) { + delete(s.gridPointer, in.StudyId) + delete(s.grids, in.StudyId) + delete(s.parameters, in.StudyId) + return &api.StopSuggestionReply{}, nil +} diff --git a/suggestion/hyperband/main.go b/suggestion/hyperband/main.go index 7f31017c5f6..4463c5dbfe9 100644 --- a/suggestion/hyperband/main.go +++ b/suggestion/hyperband/main.go @@ -1,27 +1,26 @@ package main import ( - pb "github.com/kubeflow/hp-tuning/api" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" "log" "net" -) -const ( - port = "0.0.0.0:6789" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + + pb "github.com/kubeflow/hp-tuning/api" + "github.com/kubeflow/hp-tuning/suggestion" ) func main() { - listener, err := net.Listen("tcp", port) + listener, err := net.Listen("tcp", suggestion.DefaultPort) if err != nil { log.Fatalf("Failed to listen: %v", err) } size := 1<<31 - 1 s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size)) - pb.RegisterSuggestionServer(s, NewHyperBandSuggestService()) + pb.RegisterSuggestionServer(s, suggestion.NewHyperBandSuggestService()) reflection.Register(s) - log.Printf("HyperBand Suggestion Service\n") + log.Println("HyperBand Suggestion Service") if err = s.Serve(listener); err != nil { log.Fatalf("Failed to serve: %v", err) } diff --git a/suggestion/hyperband/hyperband.go b/suggestion/hyperband_service.go similarity index 98% rename from suggestion/hyperband/hyperband.go rename to suggestion/hyperband_service.go index 7fe1e47ad8f..3d712c91c6e 100644 --- a/suggestion/hyperband/hyperband.go +++ b/suggestion/hyperband_service.go @@ -1,15 +1,15 @@ -package main +package suggestion import ( "context" "crypto/rand" "fmt" - "github.com/kubeflow/hp-tuning/api" - "github.com/kubeflow/hp-tuning/suggestion" "log" "math" "sort" "strconv" + + "github.com/kubeflow/hp-tuning/api" ) type Bracket []*api.Trial @@ -41,7 +41,7 @@ type HyperBandParameters struct { ResourceName string } type HyperBandSuggestService struct { - suggestion.RandomSuggestService + RandomSuggestService parameters map[string]*HyperBandParameters } diff --git a/suggestion/random/main.go b/suggestion/random/main.go index e074cb1eb24..bddb2e7a4ad 100644 --- a/suggestion/random/main.go +++ b/suggestion/random/main.go @@ -1,20 +1,18 @@ package main import ( - pb "github.com/kubeflow/hp-tuning/api" - "github.com/kubeflow/hp-tuning/suggestion" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" "log" "net" -) -const ( - port = "0.0.0.0:6789" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" + + pb "github.com/kubeflow/hp-tuning/api" + "github.com/kubeflow/hp-tuning/suggestion" ) func main() { - listener, err := net.Listen("tcp", port) + listener, err := net.Listen("tcp", suggestion.DefaultPort) if err != nil { log.Fatalf("Failed to listen: %v", err) } @@ -22,7 +20,7 @@ func main() { s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size)) pb.RegisterSuggestionServer(s, suggestion.NewRandomSuggestService()) reflection.Register(s) - log.Printf("Random Suggestion Service\n") + log.Println("Random Suggestion Service") if err = s.Serve(listener); err != nil { log.Fatalf("Failed to serve: %v", err) } diff --git a/suggestion/suggestService.go b/suggestion/random_service.go similarity index 93% rename from suggestion/suggestService.go rename to suggestion/random_service.go index 3653725001f..90554f0930f 100644 --- a/suggestion/suggestService.go +++ b/suggestion/random_service.go @@ -2,22 +2,19 @@ package suggestion import ( "context" - "github.com/kubeflow/hp-tuning/api" "log" "math/rand" "strconv" "time" -) -type SuggestService interface { - SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) - GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) -} + "github.com/kubeflow/hp-tuning/api" +) type RandomSuggestParameters struct { SuggestionNum int MaxParallel int } + type RandomSuggestService struct { parameters map[string]*RandomSuggestParameters } diff --git a/suggestion/types.go b/suggestion/types.go new file mode 100644 index 00000000000..47fc608ee78 --- /dev/null +++ b/suggestion/types.go @@ -0,0 +1,18 @@ +package suggestion + +import ( + "context" + + "github.com/kubeflow/hp-tuning/api" +) + +const ( + // DefaultPort is the port to serve the suggestion service. + DefaultPort = "0.0.0.0:6789" +) + +// SuggestService is the interface for suggestion service. +type SuggestService interface { + SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) + GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) +}