@@ -32,6 +32,16 @@ var tpuFamilyDefaults = map[string]int{
3232
3333var tpuRegex = regexp .MustCompile (`^v[4-6][ep]?(-\d+)?$` )
3434
35+ type tpu3DConstraints struct {
36+ maxCubes int
37+ }
38+
39+ var tpu3DConstraintsMap = map [string ]tpu3DConstraints {
40+ "ct4" : {maxCubes : 64 },
41+ "ct5p" : {maxCubes : 140 },
42+ "tpu7x" : {maxCubes : 144 },
43+ }
44+
3545var valid2DTPUFamilies = []string {"ct6e" , "ct5lp" , "v5litepod" , "v5-lite" , "v6e" }
3646var valid3DTPUFamilies = []string {"v4" , "v5p" , "ct4p" , "ct5p" , "tpu7" }
3747
@@ -92,8 +102,8 @@ var ValidGPUAccelerators = map[string]bool{
92102 "nvidia-h100-mega-80gb" : true ,
93103}
94104
95- // 3D topologies for v4, v5p, tpu7x
96- var allowed3DTopologies = map [int ]string {
105+ // 3D topologies for v4, v5p
106+ var common3DTopologies = map [int ]string {
97107 4 : "2x2x1" ,
98108 8 : "2x2x2" ,
99109 16 : "2x2x4" ,
@@ -118,13 +128,9 @@ var allowed2DTopologies = map[int]string{
118128 256 : "16x16" ,
119129}
120130
121- var valid3DShapes = make (map [string ]bool )
122131var valid2DShapes = make (map [string ]bool )
123132
124133func init () {
125- for _ , v := range allowed3DTopologies {
126- valid3DShapes [v ] = true
127- }
128134 for _ , v := range allowed2DTopologies {
129135 valid2DShapes [v ] = true
130136 }
@@ -237,9 +243,6 @@ func CalculateAcceleratorNodes(machineType, topology string, acceleratorsPerVM i
237243
238244 // 2. Identify Accelerators per VM if not provided
239245 if acceleratorsPerVM <= 0 {
240- // Resolve shorthand to full machine type if necessary
241- machineType = ResolveMachineType (machineType )
242-
243246 acceleratorsPerVM = func () int {
244247 // Check for explicit accelerators count in machine_type (e.g., "-4t")
245248 if idx := strings .LastIndex (machineType , "-" ); idx != - 1 && strings .HasSuffix (machineType , "t" ) {
@@ -278,11 +281,9 @@ func ResolveMachineType(acceleratorType string) string {
278281}
279282
280283// matchesTPUFamily returns true if the accelerator type matches any of the given families.
281- func matchesTPUFamily (acceleratorType string , families []string ) bool {
282- resolved := ResolveMachineType (acceleratorType )
283- resolvedLower := strings .ToLower (resolved )
284+ func matchesTPUFamily (machineType string , families []string ) bool {
284285 for _ , f := range families {
285- if strings .Contains (resolvedLower , f ) {
286+ if strings .Contains (machineType , f ) {
286287 return true
287288 }
288289 }
@@ -321,65 +322,81 @@ func GetCandidatesForShorthand(shorthand string) []string {
321322 return candidates
322323}
323324
324- // ResolveTopologyForChips returns the default topology for a given accelerator and total chips.
325- func ResolveTopologyForChips (accelaratorType string ) (string , error ) {
326- idx := strings .LastIndex (accelaratorType , "-" )
327- if idx == - 1 {
328- return "" , fmt .Errorf ("invalid accelerator type format for topology resolution: %s (expected prefix-suffix)" , accelaratorType )
329- }
330- accelType := accelaratorType [:idx ]
331- suffix := accelaratorType [idx + 1 :]
332- totalChips , err := strconv .Atoi (suffix )
325+ func parseShorthand (accelaratorType string ) (size int , err error ) {
326+ if strings .Count (accelaratorType , "-" ) != 1 {
327+ return 0 , fmt .Errorf ("invalid accelerator type format for topology resolution: %s (expected prefix-suffix)" , accelaratorType )
328+ }
329+ if strings .HasPrefix (accelaratorType , "tpu7x" ) {
330+ return 0 , fmt .Errorf ("for TPU7x, topology should be passed explicitly via --topology flag. It cannot be passed as chip count via compute-type as a suffix (%q). Please use --topology=AxBxC" , accelaratorType )
331+ }
332+
333+ parts := strings .Split (accelaratorType , "-" )
334+ suffix := parts [1 ]
335+ size , err = strconv .Atoi (suffix )
333336 if err != nil {
334- return "" , fmt .Errorf ("invalid chips value for accelerator type %s: %w" , accelaratorType , err )
337+ if strings .Contains (suffix , "x" ) {
338+ return 0 , fmt .Errorf ("only total chip count can be passed via compute-type as a suffix (%q). Use --topology flag with full topology string instead" , accelaratorType )
339+ }
340+ return 0 , fmt .Errorf ("invalid chips value for accelerator type %s: %w" , accelaratorType , err )
335341 }
336- resolved := ResolveMachineType (accelType )
337- resolvedLower := strings .ToLower (resolved )
338342
339343 // For TPU v4 (v4-8), the shorthand suffix represents cores. We need to divide by 2 to get chips.
340344 if accelaratorType == "v4-8" {
341- totalChips = totalChips / 2
345+ size = size / 2
342346 }
343347
344- // 3D topologies for v4, v5p, tpu7x
345- if strings .HasPrefix (resolvedLower , "v4" ) || strings .HasPrefix (resolvedLower , "v5p" ) || strings .HasPrefix (resolvedLower , "ct4" ) || strings .HasPrefix (resolvedLower , "ct5p" ) || strings .HasPrefix (resolvedLower , "tpu7" ) {
346- if shape , exists := allowed3DTopologies [totalChips ]; exists {
347- return shape , nil
348+ return size , nil
349+ }
350+
351+ // ResolveTopologyForChips returns the default topology for a given accelerator and total chips.
352+ func ResolveTopologyForChips (computeType string , machineType string ) (string , error ) {
353+ totalChips , err := parseShorthand (computeType )
354+ if err != nil {
355+ return "" , err
356+ }
357+
358+ // Validate size based on family
359+ isPowerOf2 := totalChips >= 1 && (totalChips & (totalChips - 1 )) == 0 && totalChips != 2
360+ if matchesTPUFamily (machineType , valid2DTPUFamilies ) {
361+ if ! (isPowerOf2 ) {
362+ return "" , fmt .Errorf ("invalid size %d in compute-type %q. Valid sizes are powers of 2, except 2" , totalChips , computeType )
348363 }
349- } else {
350- // 2D topologies for v5litepod and v6e (default)
351364 if shape , exists := allowed2DTopologies [totalChips ]; exists {
352365 return shape , nil
353366 }
367+ } else if matchesTPUFamily (machineType , valid3DTPUFamilies ) {
368+ isMultipleOf64 := totalChips >= 64 && totalChips % 64 == 0
369+ if ! (isPowerOf2 || isMultipleOf64 ) {
370+ return "" , fmt .Errorf ("invalid size %d in compute-type %q. Valid sizes are powers of 2, or multiples of 64" , totalChips , computeType )
371+ }
372+ if shape , exists := common3DTopologies [totalChips ]; exists {
373+ return shape , nil
374+ }
354375 }
355376
356- return "" , fmt .Errorf ("could not find a valid topology shape for %d chips with accelerator %s" , totalChips , accelType )
377+ return "" , fmt .Errorf ("could not find a valid topology shape for %d chips with accelerator %s" , totalChips , computeType )
357378}
358379
359380// ValidateHardwareRequest validates hardware requests for TPUs.
360- func ValidateHardwareRequest (machineType , topology , placementPolicy string ) error {
381+ func ValidateHardwareRequest (machineType , topology string ) error {
361382 if ! IsTPU (machineType ) {
362383 return nil // Skip for non-TPUs
363384 }
364385
365- if topology != "" {
366- if err := validateTopologyMeshFormat (topology , machineType ); err != nil {
367- return err
368- }
386+ if err := validateTopologyMeshFormat (topology , machineType ); err != nil {
387+ return err
388+ }
369389
370- if matchesTPUFamily (machineType , valid2DTPUFamilies ) {
371- if ! valid2DShapes [topology ] {
372- return fmt .Errorf ("requested carve footprint layout %s is not an authorized topology subset layout for %s" , topology , machineType )
373- }
374- } else if matchesTPUFamily (machineType , valid3DTPUFamilies ) {
375- if ! valid3DShapes [topology ] {
376- return fmt .Errorf ("requested carve footprint layout %s is not an authorized topology subset layout for %s" , topology , machineType )
377- }
378- } else {
379- return fmt .Errorf ("TPU type %q is recognized but its topology family is unknown; please report a bug to the toolkit maintainers." , machineType )
390+ if matchesTPUFamily (machineType , valid2DTPUFamilies ) {
391+ if ! valid2DShapes [topology ] {
392+ return fmt .Errorf ("requested carve footprint layout %s is not an authorized topology subset layout for %s" , topology , machineType )
380393 }
394+ return nil
395+ } else if matchesTPUFamily (machineType , valid3DTPUFamilies ) {
396+ return validate3DTopology (topology , machineType )
397+ } else {
398+ return fmt .Errorf ("TPU type %q is recognized but its topology family is unknown; please report a bug to the toolkit maintainers." , machineType )
381399 }
382- return nil
383400}
384401
385402func validateTopologyMeshFormat (requested string , accelType string ) error {
@@ -401,6 +418,53 @@ func validateTopologyMeshFormat(requested string, accelType string) error {
401418 return nil
402419}
403420
421+ func isValid3DDimension (x int ) bool {
422+ return x >= 4 && x % 4 == 0
423+ }
424+
425+ func validate3DTopology (topology string , machineType string ) error {
426+ for _ , v := range common3DTopologies {
427+ if topology == v {
428+ return nil
429+ }
430+ }
431+
432+ dims := strings .Split (topology , "x" )
433+ a , _ := strconv .Atoi (dims [0 ])
434+ b , _ := strconv .Atoi (dims [1 ])
435+ c , _ := strconv .Atoi (dims [2 ])
436+
437+ if ! isValid3DDimension (a ) || ! isValid3DDimension (b ) || ! isValid3DDimension (c ) {
438+ return fmt .Errorf ("topology %s dimensions must be >= 4 and multiples of 4 (unless it is a common base topology)" , topology )
439+ }
440+
441+ maxCubes := 0
442+ found := false
443+
444+ for prefix , constraints := range tpu3DConstraintsMap {
445+ if strings .HasPrefix (machineType , prefix ) {
446+ maxCubes = constraints .maxCubes
447+ found = true
448+ break
449+ }
450+ }
451+
452+ if ! found {
453+ return fmt .Errorf ("unknown 3D TPU family for machine type %s" , machineType )
454+ }
455+
456+ cubes := (a / 4 ) * (b / 4 ) * (c / 4 )
457+ if cubes > maxCubes {
458+ return fmt .Errorf ("topology %s exceeds max cubes limit of %d (current: %d)" , topology , maxCubes , cubes )
459+ }
460+
461+ if ! (a <= b && b <= c ) {
462+ return fmt .Errorf ("topology %s dimensions must be in non-decreasing order (A <= B <= C)" , topology )
463+ }
464+
465+ return nil
466+ }
467+
404468// CheckTopologyContainment returns true if the requested topology fits within the container topology.
405469func CheckTopologyContainment (requested , container string , accelType string ) (bool , error ) {
406470 reqDims := strings .Split (requested , "x" )
@@ -422,16 +486,5 @@ func CheckTopologyContainment(requested, container string, accelType string) (bo
422486 }
423487 }
424488
425- if requested != container {
426- if matchesTPUFamily (accelType , valid2DTPUFamilies ) {
427- if ! valid2DShapes [requested ] {
428- return false , fmt .Errorf ("requested carve footprint layout %s is not an authorized topology subset layout" , requested )
429- }
430- } else if matchesTPUFamily (accelType , valid3DTPUFamilies ) {
431- if ! valid3DShapes [requested ] {
432- return false , fmt .Errorf ("requested carve footprint layout %s is not an authorized topology subset layout" , requested )
433- }
434- }
435- }
436489 return true , nil
437490}
0 commit comments