code: def compute_ei_per_s(self, comp, pend, cand, vals, durs):
      # First we make predictions for the durations as that
      # doesn't depend on pending experiments
  
      # Compute covariances
      comp_time_cov = self.cov(self.time_amp2, self.time_ls, comp)
      cand_time_cross = self.cov(
          self.time_amp2, self.time_ls, comp, cand
      )
  
      # Cholesky decompositions
      obsv_time_cov = comp_time_cov + self.time_noise * np.eye(
          comp.shape[0]
      )
      obsv_time_chol = spla.cholesky(obsv_time_cov, lower=True)
  
      # Linear systems
      t_alpha = spla.cho_solve(
          (obsv_time_chol, True), durs - self.time_mean
      )
      # t_beta   = spla.solve_triangular(obsv_time_chol, cand_time_cross, lower=True)
  
      # Predict marginal mean times and (possibly) variances
      func_time_m = np.dot(cand_time_cross.T, t_alpha) + self.time_mean
      # We don't really need the time variances now
      # func_time_v = self.time_amp2*(1+1e-6) - np.sum(t_beta**2, axis=0)
  
      # Bring time out of the log domain
      func_time_m = np.exp(func_time_m)
  
      if pend.shape[0] == 0:
          # If there are no pending, don't do anything fancy.
  
          # Current best.
          best = np.min(vals)
  
          # The primary covariances for prediction.
          comp_cov = self.cov(self.amp2, self.ls, comp)
          cand_cross = self.cov(self.amp2, self.ls, comp, cand)
  
          # Compute the required Cholesky.
          obsv_cov = comp_cov + self.noise * np.eye(comp.shape[0])
          obsv_chol = spla.cholesky(obsv_cov, lower=True)
  
          # Solve the linear systems.
          alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
          beta = spla.solve_triangular(
              obsv_chol, cand_cross, lower=True
          )
  
          # Predict the marginal means and variances at candidates.
          func_m = np.dot(cand_cross.T, alpha) + self.mean
          func_v = self.amp2 * (1 + 1e-6) - np.sum(beta**2, axis=0)
  
          # Expected improvement
          func_s = np.sqrt(func_v)
          u = (best - func_m) / func_s
          ncdf = sps.norm.cdf(u)
          npdf = sps.norm.pdf(u)
          ei = func_s * (u * ncdf + npdf)
  
          ei_per_s = ei / func_time_m
          return ei_per_s
      else:
          # If there are pending experiments, fantasize their outcomes.
  
          # Create a composite vector of complete and pending.
          comp_pend = np.concatenate((comp, pend))
  
          # Compute the covariance and Cholesky decomposition.
          comp_pend_cov = self.cov(
              self.amp2, self.ls, comp_pend
          ) + self.noise * np.eye(comp_pend.shape[0])
          comp_pend_chol = spla.cholesky(comp_pend_cov, lower=True)
  
          # Compute submatrices.
          pend_cross = self.cov(self.amp2, self.ls, comp, pend)
          pend_kappa = self.cov(self.amp2, self.ls, pend)
  
          # Use the sub-Cholesky.
          obsv_chol = comp_pend_chol[: comp.shape[0], : comp.shape[0]]
  
          # Solve the linear systems.
          alpha = spla.cho_solve((obsv_chol, True), vals - self.mean)
          beta = spla.cho_solve((obsv_chol, True), pend_cross)
  
          # Finding predictive means and variances.
          pend_m = np.dot(pend_cross.T, alpha) + self.mean
          pend_K = pend_kappa - np.dot(pend_cross.T, beta)
  
          # Take the Cholesky of the predictive covariance.
          pend_chol = spla.cholesky(pend_K, lower=True)
  
          # Make predictions.
          pend_fant = (
              np.dot(
                  pend_chol,
                  npr.randn(pend.shape[0], self.pending_samples),
              )
              + pend_m[:, None]
          )
  
          # Include the fantasies.
          fant_vals = np.concatenate(
              (
                  np.tile(
                      vals[:, np.newaxis], (1, self.pending_samples)
                  ),
                  pend_fant,
              )
          )
  
          # Compute bests over the fantasies.
          bests = np.min(fant_vals, axis=0)
  
          # Now generalize from these fantasies.
          cand_cross = self.cov(self.amp2, self.ls, comp_pend, cand)
  
          # Solve the linear systems.
          alpha = spla.cho_solve(
              (comp_pend_chol, True), fant_vals - self.mean
          )
          beta = spla.solve_triangular(
              comp_pend_chol, cand_cross, lower=True
          )
  
          # Predict the marginal means and variances at candidates.
          func_m = np.dot(cand_cross.T, alpha) + self.mean
          func_v = self.amp2 * (1 + 1e-6) - np.sum(beta**2, axis=0)
  
          # Expected improvement
          func_s = np.sqrt(func_v[:, np.newaxis])
          u = (bests[np.newaxis, :] - func_m) / func_s
          ncdf = sps.norm.cdf(u)
          npdf = sps.norm.pdf(u)
          ei = func_s * (u * ncdf + npdf)
  
          return np.divide(np.mean(ei, axis=1), func_time_m)
  
imports: import os
  from ..None import gp
  import sys
  from ..None import util
  import tempfile
  import numpy
  import numpy.random
  import scipy.linalg
  import scipy.stats
  import scipy.optimize
  import pickle
  from ..helpers import *
  from ..Locker import *
  import logging
label_desc: Use image node_id 10 for calling the GPEIperSecChooser obj's underlying member method code with example usage: obj.compute_ei_per_s(comp, pend, cand, vals, durs) and returns: ei_per_s, np