Universal, static, logger-style output

Yes it has:

  1. Can’t do exact re-starts with text output without making output files huge (that’s why we haven’t done it).
  2. The entire .csv-reading discussion, most recent part here: Alternative .csv reader, this really limits the utility of generated quantities

On the library side we have a lot of table-writing calls defined by the model file, which makes them shared across all algorithms! That code is written once and mostly shared because it’s all tables of numbers. Then we have a few calls per algorithm that write algorithm-specific parameters with more complex types. That’s not a “huge pain” to extend.

On the interface side it’s not completely clear yet because I need to write what sort of constructor arguments would be used to parameterize the relay. I can see that this is not a straightforward problem.

You never articulated how it’s going to be difficult to manage, that’s the issue we need to address. So I’ll do some of that:

One issue that has come up repeatedly throughout the discussion about routing the messages is that algorithm-specific output is more heterogeneous than the model-file-defined output. Algorithm-specific output sometimes also need to be generated much deeper in the algorithm making it less convenient to pass down writers. A few examples from HMC: mass matrix, momenta, and Martin’s example of trajectories.

For that output the benefits of a static logger do become apparent because we would no longer have to pass the output relay object to some arbitrary depth in the stan-dev/stan code. I don’t think it’s a necessary pattern but I can see how it reduces the maintenance burden.

I still don’t see the point of stringifying everything so I suggest a signature like:

logger.debug(...);
logger.info(...);
logger.warn(...);
logger.error(...);
logger.fatal(...);

Where the ... are handled as parameter pack (it’s a standard simple use of templates so hopefully not a burden). The the default implementation (barring some more specific match that lets the interface handle the data in a binary format) can be:

template<T> logger::warn(T x) {
  ostream_ << "WARN: " << stan::stringify(x)
}

with stan-dev/stan defining how various standard types turn into strings

template<std::vector<double>> stan::stringify(std::vector<double>) {
  stringstream ss; 
  ss << x[0];
  for (int i = 1; i < x.size(); ++i) {
    ss <<  ", " << x[i];
  }
  return ss.str();
}

You get your default text stream and you don’t sabotage other methods of returning output. I didn’t get the text-tags in there but you could assume that the logger always gets a first argument that’s a std::string that can be pulled out as a tag.

For the model-file-defined output (see the beginning of “signatures a relay needs to handle”), we 1) generate it at a limited set of points in the services code; 2) sometimes process it using the model (e.g.-parameter transformations); and 3) have different preferences from the different interfaces for how to output it that are all compatible with some use of std::vector<double> or std::vector<int>

For this model-defined output a static logger doesn’t address the main issues (the coupling with the model, the volume of output that makes stringifying everything awkward).