roystgnr / MetaPhysicL

Metaprogramming and operator-overloaded classes for numerical simulations
Other
22 stars 12 forks source link

CompareTypes depending on reverseorder #23

Open lindsayad opened 6 years ago

lindsayad commented 6 years ago

@roystgnr we have this in compare_types:

// We can define CompareTypes template specializations with user types
// asymmetrically, to assist in disambiguation of templated functions
// or classes.  But sometimes we just need the supertype:

// FIXME: this won't work yet for cases where CompareTypes depends on
// reverseorder

I am curious what that comment really means? I am interested in going backwards and removing some of the decltypes in the return types of my NDDualNumber operations because they can result in value/derivative members with const and & specifiers which can mess-up down-stream type deductions. (A work-around is to apply std::remove_const and std::remove_reference, but I have started worrying about disambiguation of wrapper types as you discuss in libMesh/libmesh#1818)

If I go back to using return types based on CompareTypes, I run into the following problem when multiplying a NotADuckDualNumber tensor times a NotADuckDualNumber vector: the compiler considers this operator overload:

  template <typename T, typename T2, typename D>                                                   
  inline typename functorname##Type<NDDualNumber<T2, D>, T, true>::supertype operator opname(      
      const T & a, const NDDualNumber<T2, D> & b)                                                  
  {                                                                                                
    auto value = a opname b.value();                                                               
    auto derivatives = dn_second_calc;                                                             
    return {value, derivatives};                                                                   
  }                                                                                                

and it instantiates this MultipliesType with T2 corresponding to the vector and T corresponding to the NDDualNumber tensor. Because of the switch in order, this goes down the line all the way to attempting to instantiate a MultipliesType<VectorValue, TensorValue> which I haven't defined because it doesn't make mathematical sense (unless we transposed the vector). Thoughts on how we can avoid this compilation error? Hopefully my explanation made sense.

roystgnr commented 6 years ago

I am curious what that comment really means?

Me too!

I wrote it in 2013, and digging through the svn logs (none of this was in git back then) isn't completely enlightening.

My first guess: this whole system is intended to support both compile-time sparsity and non-symmetric operations, and in those cases (e.g. the product of two sparse matrices) the type of the result can depend on the order of the arguments.

However, IIRC literally none of the current data types do that with operator overloading; there's DotType and OuterProductType for contraction and tensor product operations.

(A work-around is to apply std::remove_const and std::remove_reference, but I have started worrying about disambiguation of wrapper types as you discuss in libMesh/libmesh#1818)

You might be able to get the best of both worlds by using type metaprogramming only as an enable argument? Like, you use the existence of CompareType<Foo,Bar> to see whether Foo wraps Bar or vice-versa by default, but then in whichever function gets enabled you use decltype to determine the actual return type details?

Thoughts on how we can avoid this compilation error?

Not as many as I should have. This is the exact sort of situation I had in mind, but now I don't recall the exact solution I had in mind. I believe the idea was to do two separate partial template specializations of CompareTypes, one for the reverseorder=false case and the other for the reverseorder=true case? And in this particular case only one order might be enabled by the Enable argument, because "tensor times vector" has an obvious interpretation whereas "vector times tensor" is more ambiguous?