docstr: When doing parallel training inside parallel trials, Ray requires to provide placement group for resource scheduling
  We wrap a group where the resource requirement is 0 because the trial only spread the task and doesn't require too much resources.
docstr_tok: ["When","doing","parallel","training","inside","parallel","trials",",","Ray","requires","to","provide","placement","group","for","resource","scheduling","We","wrap","a","group","where","the","resource","requirement","is","0","because","the","trial","only","spread","the","task","and","does","n't","require","too","much","resources","."]
code: def wrap_resources_per_job_into_placement_group(
      self, resources_per_job
  ):
      """
      When doing parallel training inside parallel trials, Ray requires to provide placement group for resource scheduling
      We wrap a group where the resource requirement is 0 because the trial only spread the task and doesn't require too much resources.
      """
      from ray import tune
  
      num_cpus = resources_per_job.get("cpu", 0)
      num_gpus = resources_per_job.get("gpu", 0)
      # The head bundle doesn't actually require resources.
      # This memory constraint is a hack (1024 is a random number, unit is bytes) to trick ray to schedule bundles on the same node
      # Currently we force the trial and its underlying folds being trained on the same node to avoid synchronization
      # TODO: consider separate the sub-bundle of folding so they can be scheduled on separate nodes
      return tune.PlacementGroupFactory(
          [{"memory": 1024}, {"CPU": num_cpus, "GPU": num_gpus}],
          strategy="STRICT_PACK",
      )
  
imports: import logging
  import math
  from abc import ABC, abstractmethod
  from autogluon.common.utils.resource_utils import ResourceManager
label_desc: Use image node_id 3 for calling the ResourceCalculator obj's underlying member method code with example usage: obj.wrap_resources_per_job_into_placement_group(resources_per_job) and returns: tune