docs: def is_valid_program(self,p):
          """checks whether program p makes a syntactically valid tree.
  
          checks that the accumulated program length is always greater than the
          accumulated arities, indicating that the appropriate number of arguments is
          alway present for functions. It then checks that the sum of arties +1
          exactly equals the length of the stack, indicating that there are no
          missing arguments.
          """
          # print("p:",p)
          arities = list(a.arity[a.in_type] for a in p)
          accu_arities = list(accumulate(arities))
          accu_len = list(np.arange(len(p))+1)
          check = list(a < b for a,b in zip(accu_arities,accu_len))
          # print("accu_arities:",accu_arities)
          # print("accu_len:",accu_len)
          # print("accu_arities < accu_len:",accu_arities<accu_len)
          return all(check) and sum(a.arity[a.in_type] for a in p) +1 == len(p) and len(p)>0