Errors with Stan and external C++ functionality

Hello all,

I am having issues with using external C++ code in conjunction with Cmdstan. I am following the documentation listed here: 22 Using external C++ code. I am following these instrunctions with the end goal of accomplishing what Ben Goodrich described in his answer for this Stack Overflow question: Calling Stan routines from a C++ program.

I have installed Cmdstand on my Ubuntu 20.04 amd64 machine using the latest release here: Cmdstan v2.29.2 (25 March 2022)

The installation process I followed: I downloaded the installation labeled as cmdstan-2.29.2.tar.gz . Once the tar.gz was downloaded and unzipped into my home directory, I opened a terminal window and cd into ~/cmdstan-2.29.2/. From that directory, I called ā€˜make clean-allā€™ and then ā€˜make buildā€™ and the installation processes was completed without errors.

To test that the installation was successful, I ran the bernoulli example using these steps: 1 CmdStan Installation | CmdStan Userā€™s Guide. This test was completed without errors.

This is where things fell apart. From here, I went ahead and tried the external c++ code functionality. Based on this documentation, I had to edit the bernoulli.stan file, add a make_odds.hpp file, and call the make command with the two flags. I will attach the bernoulli.stan and make_odds.hpp files that I used. This was the make call that I used:

make STANCFLAGS=--allow_undefined USER_HEADER=examples/bernoulli/make_odds.hpp examples/bernoulli/bernoulli

I did this and I got back errors. Iā€™ve tried this on a fresh install of Cmdstan and end up with the same errors.

There is a deprecated notice for the --allow_undefined flag listed in the documentation, but using the non-deprecated flag results in the same errors. I will attach the .hpp file that is the result of the screenshot above.

Here is the g++ and make versions that I used.

I have cross-referenced the code that I am using with the code listed for cmdstanpy here: cmdstanpy/docstr/examples.

  • Operating System: Ubuntu 20.04 amd64
  • CmdStan Version: v2.29.2
  • Compiler/Toolkit: g++ 9.4.0 and make 4.2.1

Thank you for any help you can offer.

-Matt

bernoulli.hpp (14.5 KB)
make_odds.hpp (319 Bytes)
bernoulli.stan (309 Bytes)

1 Like

@andrjohns

It seems the doc is out of date since some recent template changes. I was able to get the model to compile using 2.29.2 with the following make_odds file

#include <stan/model/model_header.hpp>
#include <ostream>

namespace bernoulli_model_namespace {

  template <typename T0__, stan::require_stan_scalar_t<T0__>* = nullptr>
  stan::promote_args_t<T0__>
  make_odds(const T0__& theta, std::ostream* pstream__) {
    return theta / (1 - theta);
  }
}

Thank for the replies, @maxbiostat and @WardBrian.

@WardBrian I gave your make_odds.hpp a go. I am no longer seeing the overloading error nor the notes that would follow, but I am still seeing the redefinition error. This is on the same machine, version of cmdstand, g++, and make as before.

Prior to attempting to make, I had deleted all of the instances of cmdstan from my machine with the exception of the tar.gz I downloaded from the releases page on github. I followed the same procedure as before: unzip, make clean-all, make build, add a ā€˜make_odds.hppā€™ (with your edits), edit ā€˜bernoulli.stanā€™, and ran the following make command from cml:

make STANCFLAGS=--allow-undefined USER_HEADER=examples/bernoulli/make_odds.hpp examples/bernoulli/bernoulli

Following this attempt, I tried using the make/local file following the same procedure as above. The end result is the same.

To give a full walk through of this process, I recorded my screen as I attempted several different approaches. I apologize for the length of the recording; a majority of it is taken up from building.

As before, I have attached all the two .hpp files, ā€˜bernoulli.stanā€™, and ā€˜localā€™ from the make directory (saved as a .txt file else I canā€™t upload it).

@WardBrian Could it be the tar.gz that I am using? amd64 wasnā€™t listed among the others so I used what appeared to be the catch-all version.

Also, I hadnā€™t noted this prior: When g++ is attempting to compile, it includes two -include examples/bernoulli/make_odds.hpp -include examples/bernoulli/make_odds.hpp calls. Is this expected behavior?

Please let me know if thereā€™s anything I am missing or anything else I can try. Iā€™m stumped.

-Matt

bernoulli.hpp (14.5 KB)
bernoulli.stan (309 Bytes)
make_odds.hpp (296 Bytes)
local.txt (1.1 KB)

I am using Ubuntu 20.04, Make 4.3, Gcc 10.3.0

This behavior can be very compiler-dependent. What happens if you remove the * = nullptr from the make_odds file I posted earlier? Does the original error return?

Removing the * = nullptr from the most recent make_odds file does not give the original error overloading error/notes. However, the redefinition error and note continue to persist.

What do you think is going on?

Ah, I just noticed you have USER_HEADER in your make/local and on the command line - you want only one of those two

I had thought that was the issue as well, since I had used make/local as a different way of making. Perhaps make/local was persisting when I didnā€™t notice.

Thereā€™s currently not a make/local file. The only file in there is local.example but everything is still commented out. I had also tried calling without including the USER_HEADER=examples/bernoulli/make_odds.hpp flag, but g++ didnā€™t know how to look for the header file. So the flag is not being used elsewhere.

I wanted to try having a make/local with the required flags as well as call make + flags from the CML to see what would happen.

Technically, that is the expected the behavior. But it shouldnā€™t be occurring when I use only make/local or only the CML right?

Is there another place/way that USER_HEADER=examples/bernoulli/make_odds.hpp would be passed in but Iā€™m not noticing it?

I am not sure where it would be coming from, but the duplicated -includein the output of the make command is (I believe) the culprit of this. I would try debugging by getting as close as you can to a default cmdstan (e.g. no make/local, etc).

I believe you can do make print-USER_HEADER to see if it is being picked up from somewhere else. By default this should just be ā€˜user-header.hppā€™

@WardBrian I tried the make print-USER_HEADER command. Iā€™m including a screenshot of the output below.

Screenshot from 2022-06-14 17-05-29

For the first call/output: I am calling make print-USER_HEADER when make/local does not exist. I receive back the expected behavior.

For the second call/output: I am calling make print-USER_HEADER when make/local does exist. Within make/local, I include the line USER_HEADER=examples/bernoulli/make_odds.hpp and STANCFLAGS+= --allow-undefined. I receive back the expected behavior.

For each call, I also called make examples/bernoulli/bernoulli to see the results. Iā€™m including 2 screenshots of the output below.

This screenshot corresponds with the first call/output in the first screenshot. I receive back the expected behavior.

This screenshot corresponds with the second call/output in the first screenshot. I continue to see the same issue.

Below is the process I have gone through to ensure that my tests are as close to default cmdstan as possible:

  1. Download the tar.gz
  2. Unzip the file into my home directory
  3. Open a terminal window within the newly created ~/cmdstan-2.29.2 directory
  4. Call make clean-all
  5. Call make build
  6. Edit the bernoulli.stan file in examples/bernoulli/bernoulli to match the code laid out in the Stan external C++ documentation
  7. Add a make_odds.hpp to examples/bernoulli and paste in the code you had posted initially
  8. Run the following command make STANCFLAGS=--allow_undefined USER_HEADER=examples/bernoulli/make_odds.hpp examples/bernoulli/bernoulli from ~/cmdstan-2.29.2
  9. Run into the error

Iā€™ve gone through this same process with make/local. Most of my tests follow this same process except for step 8 where I try a different method of compilation, but end up with the same error.

What are your thoughts? Iā€™m human and I still forget semi-colons when Iā€™m writing C/C++ code. But I donā€™t think this is an issue on my part. I have boiled down the process above to the most basic parts and I am still seeing the same error.

Okay @matthew_ay - I just tried your exact steps starting from the source download and I got the same error.

I looked around in the makefiles and found some odd string comparisons that seem to be specific to the bernoulli example. I renamed the file to bernoulli_makeodds.stan, edited make_odds.hpp to use the bernoulli_makeodds_model namespace, and then the second -include worked.

I think this worked for me before because I didnā€™t have the files placed in examples/bernoulli. I have no idea why this extra make logic is present. If youā€™re able to confirm that moving or renaming the files worked, we can investigate more (pinging @rok_cesnovar who knows more about the makefiles)

make STANCFLAGS=--allow_undefined USER_HEADER=examples/bernoulli/make_odds.hpp examples/bernoulli/bernoulli

As long as I used the code you had mentioned earlier (but changing the namespace to bernoulli_makeodds_model instead of bernoulli_model), along with the make call above, then renaming/editing the files worked!

I tried this using make/local instead of the CML and that worked as well. Iā€™ll attach the files I used for this final test.

That was a tricky bug. I shouldā€™ve tried using alternate file names/namespaces prior to posting but the thought didnā€™t occur to me. Should I make two Github issues pointing to this thread? One issue for the documentation and one for the renaming/editing. Or does this thread suffice as a complete bug report?

If itā€™s okay, I have two final questions.

How does this codeā€¦

#include <stan/model/model_header.hpp>
#include <ostream>

namespace bernoulli_model_namespace {

  template <typename T0__, stan::require_stan_scalar_t<T0__>* = nullptr>
  stan::promote_args_t<T0__>
  make_odds(const T0__& theta, std::ostream* pstream__) {
    return theta / (1 - theta);
  }
}

ā€¦work? And how does it differ in functionality from the code within the documentation?

#include <boost/math/tools/promotion.hpp>
#include <ostream>

namespace bernoulli_model_namespace {
          template <typename T0__>  inline  typename
          boost::math::tools::promote_args<T0__>::type  make_odds(const T0__&
theta, std::ostream* pstream__) {
       return theta / (1 - theta);  }
       }

Second question: Bernoulli was a proof of concept to my advisor and I that we can use cmdstan in conjunction with external C++ header files to produce a standalone executable. Since this worked out, we were thinking of proceeding onto a more difficult example.

Do you know if there exists documentation and/or a repo/codebase/directory that uses cmdstan + external C++ header files to create a more complicated model than the bernoulli model? Iā€™ve seen a couple threads with users that tried something more difficult but the code is often not there when they supply a github link.

Thank you for taking your time and helping me on this @WardBrian. I greatly appreciate it!
Matt

bernoulli.stan (309 Bytes)
make_odds.hpp (294 Bytes)
local.txt (1.1 KB)

I can file bug reports with pointers back to this thread. Thanks for reporting!

On your questions:

The file I posted is largely the same - to a human, the meanings should be basically equivalent - the biggest different is just that the newer one has a signature which is based on the current output of the Stan compiler. These changed in recent versions to allow function overloading of user-defined functions, and itā€™s important for the C++ compiler that they match precisely.

The way I recommend doing this is to write the model you want with the function stubs, and running the build with STANCFLAGS=--allow-undefined. Then, look at what the generated hpp file has and use those signatures exactly. Especially for things like matrices, the signatures wonā€™t always be what youā€™d ā€œnaturallyā€ write.

I donā€™t know of any larger examples myself, sorry!

1 Like