Code generation has nondeterministic behaviour
An addition of new types in UFL triggered different FFC regression test code output on different buildbot platforms for optimized quadrature code.
I've tracked the problem to the following parts:
# In optimizedquadraturetransformer.py: def _math_function(self, operands, format_function): #print("Calling _math_function() of optimisedquadraturetransformer.") # TODO: Are these safety checks needed? ffc_assert(len(operands) == 1 and () in operands and len(operands) == 1, \ "MathFunctions expect one operand of function type: " + repr(operands)) # Use format function on value of operand. operand = operands for key, val in operand.items(): #new_val = create_symbol(format_function(str(val)), val.t) #new_val.base_expr = val #new_val.base_op = 1 # Add one operation for the math function. new_val = create_symbol(format_function, val.t, val, 1) # <----- HERE operand[key] = new_val return operand
- The above create_symbol call triggers construction of a Symbol with a lambda function as the variable instead of a string.
- In the repr construction in Symbol.__init__, the repr of the lambda function contains the address of the lambda function, which is arbitrary.
- In Expr.__lt__ (ffc, not ufl), the repr is used to compare two expressions.
- In Sum.expand(), variables are passed to sorted(), which in turn uses < for comparison.
The end result is that an arbitrary ordering is applied to terms in a sum when running with -O.
I have no idea how to fix this, but I'm pretty sure the proper solution involves not passing a function but a string to Symbol.