mitsuba-renderer / enoki

Enoki: structured vectorization and differentiation on modern processor architectures

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Confusion regarding mask types

arpit15 opened this issue · comments

I wrote a simple test to try enoki. However, I am unable to perform simple comparison operations due to type differences. Documentation states that return type of operator< and neq is mask_t<Array>. However, types of result1 and result2 variable in the following code are different.

import enoki as ek

def myfunc(arr1, arr2):
  result1 = ek.dot(arr1, arr1) < 0
  result2 = ek.neq(arr2, 0)
  print(type(result1), type(result2))
  return result1, result2

def test_scalar():
  from enoki import scalar
  arr1 = scalar.Vector1f([1])
  arr2 = scalar.Vector1f([2])
  res = myfunc(arr1, arr2)


def test_cuda():
  from enoki import cuda
  arr1 = cuda.Vector1f([1])
  arr2 = cuda.Vector1f([2])
  res = myfunc(arr1, arr2)


if __name__ == '__main__':
  test_scalar()
  test_cuda()

Output in scalar mode gives below. According to my understanding this is because the output of dot operation is converted to py::float. Is there a way to perform comparison without explicitly casting to bool in this case?

<class 'bool'> <class 'enoki.scalar.Vector1m'>

Output in cuda mode gives below. The difference between these types is unclear to me. Can you kindly give more details?

class 'enoki.cuda.Mask'> <class 'enoki.cuda.Vector1m'>

Hi @arpit15 ,

Regarding the test_scalar, you are right, the dot function will return a float for which the comparison op will yield a bool value. On the other hand, Vector1m is equivalent to mask_t<Array<float, 1>> in C++. Arrays with 1 dimension are pretty much the same as scalar values like float. Therefore you should be able to seamlessly do arithmetic between bool, Mask, and Vector1m in python, so no need for explicit cast.

The same applies to the test_cuda. However this time, the dot function return a CUDAArray<float> value (that lives on the GPU), to the operator< returns a Mask and not a bool.

Please let me know if this answered you questions.

I asked the difference in types because I am unable to perform operations between these types. For ex: I tried res = result1 & result2 in test_scalar and test_gpu both give me errors.
Error in scalar
TypeError: unsupported operand type(s) for &: 'bool' and 'enoki.scalar.Vector1m'
Error in gpu
TypeError: unsupported operand type(s) for &: 'enoki.cuda.Mask' and 'enoki.cuda.Vector1m

Please let me know the correct way to perform element-wise operations in enoki, if this is not the correct way.

Currently enoki.cuda.Mask can be implicitly casted to enoki.cuda.Vector1m but not the other way around. Somehow Python also tries to cast from right to left with the & operator, which causes your issue. I will try to fix this in the future.
In the meantime, you should be able to do res = result2 & result1.

Also, could you tell me why using cuda.Vector1f and not cuda.Float32? This should solve you issue as well.

Thanks! This issue is solved by using res = result2 & result1.
I am trying to write custom area emitter in python. My idea was to create a Vector1m for both scalar and cuda, to write logic like here. I presumed that comparing Vector1f in both variants would give me Vector1m which is the type I am observing on the python side when using arguments of sample_direction function.
Is there a reason to not have implicit converters from scalar.Vector1m to bool?

Is there a reason to not have implicit converters from scalar.Vector1m to bool?

No specific reason, we might add it in the future.

In this code

dot(ds.d, ds.n) < 0.f && neq(ds.pdf, 0.f)

both comparisons should return a Mask. I don't see where the Vector1f comes in?

I don't really care about using Vector1f. I am just using it to create Vector1m. Using neq in python yields Vector1m for neq(ds.pdf, 0). Therefore using
dot(ds.d, ds.n) < 0.f & neq(ds.pdf, 0.f) doesn't work. However,
neq(ds.pdf, 0.f) & dot(ds.d, ds.n) < 0.f works fine, as you suggested.
Is there a way to create Mask type var from Vector1m type var without explicitly knowing, if the variant is scalar or cuda?
The main source of my problems is friction between Vector1m and Mask types.

I see problems due to no implicit conversion from Vector1m to Mask when calling m_radiance->eval in scalar variant as the function requires Mask aka bool type. However, processing using neq(ds.pdf, 0.f) & dot(ds.d, ds.n) < 0.f & active gives me Vector1m

I see your problem now. There was indeed some missing bindings for the scalar mode causing the following issue:

ek.neq(0.0, 0.0) # -> return Vector1m instead of bool

This should be fixed now (486ddb0)

In the other modes (dynamic, cuda), I would be surprise if you ever encounter a Vector1m. This would indicate that there is another bug. Let me know in that case.

Thanks for resolving this error. I tripped over another bug. I don't know if I should make another issue for this.
ek.select(True, 0.1, 0.2) returns enoki.scalar.Vector1m with value [1] which is wrong type and value.

Another one, thanks!

Just fixed in 1cc24d0