code: def _set_variables(self, cacheable):
          
          # pull the parameters and inputs out of the symbolic pdf
          def extract_vars(expr):
              return [e for e in expr.atoms() if e.is_Symbol and e not in vars]
          self.cacheable = cacheable
          self.variables = {}
          vars = []
          for expression in self.expressions.values():
              vars += extract_vars(expression['function'])
          # inputs are assumed to be those things that are
          # cacheable. I.e. those things that aren't stored within the
          # object except as cached. For covariance functions this is X
          # and Z, for likelihoods F and for mapping functions X.
          self.cacheable_vars = [] # list of everything that's cacheable
          for var in cacheable:            
              self.variables[var] = [e for e in vars if e.name.split('_')[0]==var.lower()]
              self.cacheable_vars += self.variables[var]
          for var in cacheable:
              if not self.variables[var]:
                  raise ValueError('Variable ' + var + ' was specified as cacheable but is not in expression. Expected to find symbols of the form ' + var.lower() + '_0 to represent ' + var)
  
          # things that aren't cacheable are assumed to be parameters.
          self.variables['theta'] = sorted([e for e in vars if not e in self.cacheable_vars],key=lambda e:e.name)
docs: Pull the variable names out of the provided expressions and separate into cacheable expressions and normal parameters. Those that are only stored in the cache, the parameters are stored in this object.