MP_BINARY_OP_EQUAL in .binary_op with user type

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
v923z
Posts: 168
Joined: Mon Dec 28, 2015 6:19 pm

MP_BINARY_OP_EQUAL in .binary_op with user type

Post by v923z » Sat Apr 04, 2020 8:32 pm

Hi all,

Before raising an issue on github, I wanted to ask, whether I try to do something note quite legal. I would like to clean up the implementation of the ==, and != binary operators in ulab, and I don't get what I expect. Namely, the following code does what it is supposed to

Code: Select all

import ulab

a = ulab.array([1, 2, 3], dtype=ulab.uint8)
b = ulab.array([2, 2, 2])
print(b)
print(a == b)
print(a < 2)
i.e., returns the results of the element-wise comparisons (internally, the scalar 2 is converted into an array, and then the broadcasting rules make sure that a < 2 is a meaningful statement), but this

Code: Select all

import ulab

a = ulab.array([1, 2, 3], dtype=ulab.uint8)
print(a == 2)
doesn't even call the .binary_op method of the ndarray object, but returns a False instead. Here is the relevant part of my code: https://github.com/v923z/micropython-ul ... ray.c#L736 (On github, I removed a printf statement at the very beginning of the function. Originally it looked like this

Code: Select all

mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lhs, mp_obj_t rhs) {
	printf("in binary_op");
	// if the ndarray stands on the right hand side of the expression, simply swap the operands
	ndarray_obj_t *ol, *or;
	mp_binary_op_t op = _op;
and this is why I know that the function is never called, if one of the operands is a scalar.)

I presume, this has to do with how binary operators are resolved in runtime.c. Is this reasoning correct, or there is something I could do without having to tamper with the micropython core?

Thanks,

Zoltán

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: MP_BINARY_OP_EQUAL in .binary_op with user type

Post by jimmo » Sun Apr 05, 2020 3:12 am

Hi,

Have you read the implementation of mp_binary_op in runtime.c -- in this case the relevant detail is that it calls mp_obj_equal_not_equal.

It goes through a bunch of the steps, to finally call the type's binary_op.

Importantly though, the behavior is controlled by the flags set on the type. This behavior was overhauled quite recently. I think probably https://github.com/micropython/micropython/pull/5533 is the best place to start to see some of the discussion around this.

Anyway, I haven't tested this myself but seeing as you didn't explicitly mention that you're setting any of the MP_TYPE_FLAG_EQ_ flags, then I suspect the comments in obj.c (above mp_obj_equal_not_equal) and obj.j (above the flags) will be interesting to you.

Code: Select all

// Flags for type behaviour (mp_obj_type_t.flags)
// If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True).
// If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an
// instance of any different class that also clears this flag.  If this flag is set
// then the type may check for equality against a different type.
// If MP_TYPE_FLAG_EQ_HAS_NEQ_TEST is clear then the type only implements the __eq__
// operator and not the __ne__ operator.  If it's set then __ne__ may be implemented.
#define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001)
#define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002)
#define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0040)
#define MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE (0x0080)
#define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010)

v923z
Posts: 168
Joined: Mon Dec 28, 2015 6:19 pm

Re: MP_BINARY_OP_EQUAL in .binary_op with user type

Post by v923z » Mon Apr 06, 2020 6:30 am

jimmo wrote:
Sun Apr 05, 2020 3:12 am
Hi,

Have you read the implementation of mp_binary_op in runtime.c -- in this case the relevant detail is that it calls mp_obj_equal_not_equal.
Not recently, so if it has changed recently, then I am not up to date.
jimmo wrote:
Sun Apr 05, 2020 3:12 am
It goes through a bunch of the steps, to finally call the type's binary_op.

Importantly though, the behavior is controlled by the flags set on the type. This behavior was overhauled quite recently. I think probably https://github.com/micropython/micropython/pull/5533 is the best place to start to see some of the discussion around this.

Anyway, I haven't tested this myself but seeing as you didn't explicitly mention that you're setting any of the MP_TYPE_FLAG_EQ_ flags, then I suspect the comments in obj.c (above mp_obj_equal_not_equal) and obj.j (above the flags) will be interesting to you.
Thanks for the pointers! Adding the flags MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, and MP_TYPE_FLAG_EQ_HAS_NEQ_TEST solved the problem.

Zoltán

Post Reply