docs: def dr_wrt(self, wrt, profiler=None):
          '''
          Loop over free variables and delete cache for the whole tree after finished each one
          '''
          if wrt is self.x:
              jacs = []
              for fvi, freevar in enumerate(self.free_variables):
                  tm = timer()
                  if isinstance(freevar, ch.Select):
                      new_jac = self.obj.dr_wrt(freevar.a, profiler=profiler)
                      try:
                          new_jac = new_jac[:, freevar.idxs]
                      except:
                          # non-csc sparse matrices may not support column-wise indexing
                          new_jac = new_jac.tocsc()[:, freevar.idxs]
                  else:
                      new_jac = self.obj.dr_wrt(freevar, profiler=profiler)
  
                  pif('dx wrt {} in {}sec, sparse: {}'.format(freevar.short_name, tm(), sp.issparse(new_jac)))
  
                  if self._make_dense and sp.issparse(new_jac):
                      new_jac = new_jac.todense()
                  if self._make_sparse and not sp.issparse(new_jac):
                      new_jac = sp.csc_matrix(new_jac)
  
                  if new_jac is None:
                      raise Exception(
                          'Objective has no derivative wrt free variable {}. '
                          'You should likely remove it.'.format(fvi))
  
                  jacs.append(new_jac)
              tm = timer()
              utils.dfs_do_func_on_graph(self.obj, clear_cache_single)
              pif('dfs_do_func_on_graph in {}sec'.format(tm()))
              tm = timer()
              J = hstack(jacs)
              pif('hstack in {}sec'.format(tm()))
              return J