code: def multiplexer(f=None, nruns=1, nprocs=1, seeding=None, **args):
      
      if not callable(f):
          raise ValueError('multiplexer: function f missing, or not callable')
      if seeding is None:
          seeding = (nruns > 1)
      # extra arguments (meant to be arguments for f)
      fixedargs, listargs, dictargs = {}, {}, {}
      listargs['run'] = list(range(nruns))
      for k, v in args.items():
          if isinstance(v, list):
              listargs[k] = v
          elif isinstance(v, dict):
              dictargs[k] = v
          else:
              fixedargs[k] = v
      # cartesian product
      inputs, outputs = cartesian_args(fixedargs, listargs, dictargs)
      for ip in inputs:
          ip.pop('run')  # run is not an argument of f, just an id for output
      # distributing different seeds
      if seeding:
          seeds = distinct_seeds(len(inputs))
          for ip, op, s in zip(inputs, outputs, seeds):
              ip['seed'] = s
              op['seed'] = s
      # the actual work happens here
      return distribute_work(f, inputs, outputs, nprocs=nprocs)
docs: Evaluate a function for different parameters, optionally in parallel.
  
      Parameters
      ----------
      f: function 
          function f to evaluate, must take only kw arguments as inputs
      nruns: int 
          number of evaluations of f for each set of arguments
      nprocs: int
          + if <=0, set to actual number of physical processors plus nprocs
          (i.e. -1 => number of cpus on your machine minus one)
          Default is 1, which means no multiprocessing
      seeding: bool (default: True if nruns > 1, False otherwise)
          whether we need to provide different seeds for RNGS
      **args:
          keyword arguments for function f.
  
      Note
      ----
      see documentation of `utils`