Gecode / gecode

Generic Constraint Development Environment
https://www.gecode.org
Other
275 stars 76 forks source link

Rounding mode switching side-effects #155

Open fkonvick opened 2 years ago

fkonvick commented 2 years ago

When running a model involving FloatVars, the FPU rouding mode may get changed, which may cause issues with code running afterwards. This happens with Gecode 6.2.0 (I haven't checked whether this changed in 6.3 yet).

I'm not certain which platforms this applies to, but it certainly happened to me on Linux x64 and, in the past, on Windows (I wasn't able to reproduce this now).

I created the following simple "guard class" to work around this problem.

struct SaveRoundingState {
  Gecode::Float::Rounding::rounding_mode original_rt;
  SaveRoundingState() {
    Gecode::Float::Rounding::get_rounding_mode(original_rt);
  }
  ~SaveRoundingState() {
    Gecode::Float::Rounding::set_rounding_mode(original_rt);
  }
};

Using this as follows resolves the issue (including printouts of the rounding mode):

{
  Gecode::Float::Rounding::rounding_mode rm;
  Gecode::Float::Rounding::get_rounding_mode(rm);
  std::cout << rm << "\n";
  {
    SaveRoundingState save_state;
    // (run the solver)
  }
  Gecode::Float::Rounding::get_rounding_mode(rm);
  std::cout << rm << "\n";
}

In any case I think that Gecode should be cleaning up by itself.

zayenz commented 2 years ago

Interesting issue, thanks for bringing it up. There are no recent changes to how this code operates, so nothing new to be expected in 6.3.

I agree that we should probably be more careful with how we set and manage the rounding mode for Gecode. One issue is that there is no outermost entry-point into Gecode proper where an automatic method would naturally fit in. As an alternative, creating a helper-class to use outside as you show above is a simple solution, but it also requires knowledge by the user.

Let's think some more on this, to see if there is a good solution that makes it easier for users.

I know that there are additional problems with rounding modes, and that these can change outside of the programs control as it is essentially shared mutable state of the CPU. In discussions with others that do serious work with floating point computations, I've heard of systems going so far as to set the rounding mode immediately before each computation. I think this is probably overkill for Gecode, but I just thought I should mention it as an expanded viewpoint.

fkonvick commented 2 years ago

I agree on all points. I did not expect a quick solution for 6.3.0 as the issue has apparently existed for quite some time. I will try to create some minimal example code to allow testing. The solution should ideally allow Gecode to set up the rounding mode once (to prevent that each computation needs to set the rounding mode anew).

zayenz commented 2 years ago

I fully agree that it would be good to have a simple built-in way to set the rounding mode that can be recommended. If you can figure out a good way to package that, that would be a great addition.

For the record, only setting it before using any Gecode code might not be enough in all cases. Consider a user that writes a custom logic propagator that uses, for example, an in-house dynamic library for price calculations. That library might set the rounding mode and not re-set it afterwards. In other words, the same type of guard logic saving and restoring the rounding mode would be valuable to use around any external calls.

The above reasoning is why I've heard that some libraries are very cautious and spend a lot of time ensuring that the rounding mode is always set - as a library the user may have any kind of code running concurrently with the library code in the same process. For Gecode I think that that level of caution would be overkill, and that your proposed solution would be a good level of abstraction.