code: def runSearch(
      length,
      computeScript,
      gamma,
      hyperparameters,
      lossFunc=None,
      initializationRounds=None,
      nEICandidates=None,
      priorWeight=None,
      secondaryCutoff=None,
      independentModellingRate=None,
  ):
      testGlobals = {}
      exec(computeScript, testGlobals)
      computeLoss = testGlobals["computeLoss"]
  
      losses = []
  
      best = None
      bestLoss = None
  
      if initializationRounds is None:
          initializationRounds = 20
  
      if nEICandidates is None:
          nEICandidates = 24
  
      if priorWeight is None:
          priorWeight = 1.0
  
      if secondaryCutoff is None:
          secondaryCutoff = 0.0
  
      currentResults = []
  
      def computePrimarySecondary():
          if len(currentResults) < initializationRounds:
              return (
                  list(sorted(hyperparameters["properties"].keys())),
                  [],
              )  # Put all parameters as primary
  
          if len(set(result["loss"] for result in currentResults)) < 5:
              return (
                  list(sorted(hyperparameters["properties"].keys())),
                  [],
              )  # Put all parameters as primary
  
          cutoffForTrial = secondaryCutoff
          if secondaryCutoff == "auto":
              cutoffForTrial = adaptiveSecondaryCutoff(
                  currentResults, hyperparameters
              )
  
          totalWeight = 0
          correlations = {}
          for parameter in hyperparameters["properties"]:
              if (
                  len(
                      set(
                          result[parameter] for result in currentResults
                      )
                  )
                  < 2
              ):
                  correlations[parameter] = 0
              else:
                  correlation = abs(
                      scipy.stats.spearmanr(
                          numpy.array(
                              [
                                  result[parameter]
                                  for result in currentResults
                              ]
                          ),
                          numpy.array(
                              [
                                  result["loss"]
                                  for result in currentResults
                              ]
                          ),
                      )[0]
                  )
                  correlations[parameter] = correlation
                  totalWeight += correlation
  
          threshold = totalWeight * cutoffForTrial
  
          primaryParameters = []
          secondaryParameters = []
          cumulative = totalWeight
          # Sort properties by their weight
          sortedParameters = sorted(
              hyperparameters["properties"].keys(),
              key=lambda parameter: -correlations[parameter],
          )
          for parameter in sortedParameters:
              if cumulative < threshold:
                  secondaryParameters.append(parameter)
              else:
                  primaryParameters.append(parameter)
  
              cumulative -= correlations[parameter]
          return primaryParameters, secondaryParameters
  
      while len(currentResults) < length:
          next = {}
  
          def sample(parameters):
              nonlocal next
              next = parameters
              return {"loss": 0.5, "status": "ok"}
  
          space = {
              paramName: hyperopt.hp.uniform(paramName, 0, 1)
              for paramName in hyperparameters["properties"]
          }
  
          (
              primaryParameters,
              secondaryParameters,
          ) = computePrimarySecondary()
  
          if best is not None:
              # Any secondary parameters have a 50-50 chance of being locked to their current best value.
              for secondary in secondaryParameters:
                  if random.uniform(0, 1) < 0.5:
                      space[secondary] = best[secondary]
  
          willModelIndependently = False
          if independentModellingRate is not None:
              willModelIndependently = bool(
                  random.uniform(0, 1.0) <= independentModellingRate
              )
  
          gammaForTrial = gamma
          if isinstance(gamma, str):
              n_startup_trials = int(gamma[gamma.find("_") + 1 :])
              gammaForTrial = adaptiveGamma(
                  currentResults,
                  hyperparameters,
                  n_startup_trials=n_startup_trials,
              )
  
          nEICandidatesForTrial = nEICandidates
          if isinstance(nEICandidates, str):
              nEICandidatesForTrial = adaptiveNCandidates(
                  hyperparameters
              )
  
          if not willModelIndependently:
              hyperopt.fmin(
                  fn=sample,
                  space=space,
                  algo=functools.partial(
                      hyperopt.tpe.suggest,
                      n_startup_jobs=initializationRounds,
                      gamma=gammaForTrial,
                      n_EI_candidates=nEICandidatesForTrial,
                      prior_weight=priorWeight,
                  ),
                  max_evals=1,
                  trials=convertResultsToTrials(
                      hyperparameters["properties"].keys(),
                      currentResults,
                      lossFunc=lossFunc,
                  ),
                  rstate=numpy.random.RandomState(
                      seed=int(random.randint(1, 2**32 - 1))
                  ),
              )
          else:
              # We make two completely separate distributions. One for the first half of parameters, one for the second half.
              nameSortedParameters = sorted(
                  hyperparameters["properties"].keys()
              )
              cutoff = int(len(nameSortedParameters) / 2)
              group1ParametersNames = nameSortedParameters[:cutoff]
              group2ParametersNames = nameSortedParameters[cutoff:]
  
              group1HyperoptSpace = {
                  paramName: hyperoptConfig
                  for paramName, hyperoptConfig in space.items()
                  if paramName in group1ParametersNames
              }
              group2HyperoptSpace = {
                  paramName: hyperoptConfig
                  for paramName, hyperoptConfig in space.items()
                  if paramName in group2ParametersNames
              }
  
              group1Hyperparameters = {
                  "type": "object",
                  "properties": {
                      prop: config
                      for prop, config in hyperparameters[
                          "properties"
                      ].items()
                      if prop in group1ParametersNames
                  },
              }
  
              group2Hyperparameters = {
                  "type": "object",
                  "properties": {
                      prop: config
                      for prop, config in hyperparameters[
                          "properties"
                      ].items()
                      if prop in group2ParametersNames
                  },
              }
  
              group1ResultsHistory = [
                  {
                      prop: value
                      for prop, value in result.items()
                      if (
                          prop in group1ParametersNames
                          or prop == "loss"
                          or prop == "status"
                      )
                  }
                  for result in currentResults
              ]
  
              group2ResultsHistory = [
                  {
                      prop: value
                      for prop, value in result.items()
                      if (
                          prop in group2ParametersNames
                          or prop == "loss"
                          or prop == "status"
                      )
                  }
                  for result in currentResults
              ]
  
              # Obtain the recommended hyper parameters for group 1
              hyperopt.fmin(
                  fn=sample,
                  space=group1HyperoptSpace,
                  algo=functools.partial(
                      hyperopt.tpe.suggest,
                      n_startup_jobs=initializationRounds,
                      gamma=gamma,
                      n_EI_candidates=nEICandidates,
                      prior_weight=priorWeight,
                  ),
                  max_evals=1,
                  trials=convertResultsToTrials(
                      group1Hyperparameters,
                      group1ResultsHistory,
                      lossFunc=lossFunc,
                  ),
                  rstate=numpy.random.RandomState(
                      seed=int(random.randint(1, 2**32 - 1))
                  ),
              )
  
              group1Next = dict(next)
  
              # Obtain the recommended hyper parameters for group 2
              hyperopt.fmin(
                  fn=sample,
                  space=group2HyperoptSpace,
                  algo=functools.partial(
                      hyperopt.tpe.suggest,
                      n_startup_jobs=initializationRounds,
                      gamma=gamma,
                      n_EI_candidates=nEICandidates,
                      prior_weight=priorWeight,
                  ),
                  max_evals=1,
                  trials=convertResultsToTrials(
                      group2Hyperparameters,
                      group2ResultsHistory,
                      lossFunc=lossFunc,
                  ),
                  rstate=numpy.random.RandomState(
                      seed=int(random.randint(1, 2**32 - 1))
                  ),
              )
  
              group2Next = dict(next)
  
              # Merge together the next recommended hyper-parameters for each group
              next = {}
              for key, value in group1Next.items():
                  next[key] = value
              for key, value in group2Next.items():
                  next[key] = value
  
          result = computeLoss(next)
          loss = result["loss"]
          losses.append(loss)
          data = dict(next)  # Clone the next dict
          data["loss"] = loss
          currentResults.append(data)
  
          if best is None or loss < bestLoss:
              best = data
              bestLoss = loss
  
      return {
          "loss": best["loss"],
          "skew": scipy.stats.skew(numpy.array(losses)),
          "std": scipy.std(losses),
          "kurtosis": scipy.stats.kurtosis(losses),
          "range": (numpy.min(losses), numpy.max(losses)),
          "min": numpy.min(losses),
          "median": numpy.median(losses),
          "max": numpy.max(losses),
          "best/median": (best["loss"] / numpy.median(losses)),
      }
  
imports: import hyperopt
  import math
  import json
  import random
  import numpy
  import functools
  import concurrent.futures
  import os
  import sys
  import time
  import datetime
  import subprocess
  import scipy.stats
  import csv
  from scipy.stats import norm
  import scipy.interpolate
  from hypermax.utils import roundPrecision
  from hypermax.hyperparameter import Hyperparameter
  from pprint import pprint
  import lightgbm
label_desc: Use image node_id 8 for calling a global function with example usage: runSearch(length, computeScript, gamma, hyperparameters, lossFunc, initializationRounds, nEICandidates, priorWeight, secondaryCutoff, independentModellingRate) and returns: dict, primaryParameters,  secondaryParameters, list,  list, list,  list, dict