← Back to team overview

dolfin team mailing list archive

Re: Strange error from function.py

 

2008/12/3 Johan Hake <hake@xxxxxxxxx>:
> On Tuesday 02 December 2008 23:53:32 Anders Logg wrote:
>> On Tue, Dec 02, 2008 at 11:39:40PM +0100, Johan Hake wrote:
>> > On Tuesday 02 December 2008 23:15:34 Anders Logg wrote:
>> > > On Tue, Dec 02, 2008 at 10:35:31PM +0100, Johan Hake wrote:
>> > > > On Tuesday 02 December 2008 15:42:53 Anders Logg wrote:
>> > > > > On Tue, Dec 02, 2008 at 12:12:14PM +0100, Anders Logg wrote:
>> > > > > > I've tried adding a new class Constant in function.py:
>> > > > > >
>> > > > > > class Constant(ffc.Constant, dolfin.cpp_Function):
>> > > > > >
>> > > > > >     def __init__(self, domain, value):
>> > > > > >         "Create constant-valued function."
>> > > > > >         print domain
>> > > > > >         print value
>> > > > > >         #ffc.Constant.__init__(self, domain)
>> > > > > >         #dolfin.cpp_Constant.__init__(self, value)
>> > > > > >
>> > > > > > But I get the following error message:
>> > > > > >
>> > > > > >   File
>> > > > > >
>> > > > > > "/scratch/fenics/dolfin/dolfin-dev/local/lib/python2.5/site-packa
>> > > > > >ges/ dolf in/function.py", line 411, in <module> class
>> > > > > > Constant(ffc.Constant, dolfin.cpp_Function):
>> > > > > > TypeError: Error when calling the metaclass bases
>> > > > > >     function() argument 1 must be code, not str
>> > > > > >
>> > > > > > How is this possible? There should be no metaclasses involved
>> > > > > > here (except the built-in Python metaclass type that is always
>> > > > > > there).
>> > > >
>> > > > Meta classes is always involed when you try to create a class. This
>> > > > time it is not the FunctionCreator meta class, even if you first
>> > > > think so.
>> > > >
>> > > > If you ever tried to subclass a python function, which you are
>> > > > trying, you
>> > > >
>> > > > will get the same error:
>> > > >   >>> def jada():pass
>> > > >
>> > > >   ...
>> > > >
>> > > >   >>> class Jada(jada):pass
>> > > >
>> > > >   ...
>> > > >   Traceback (most recent call last):
>> > > >     File "<stdin>", line 1, in <module>
>> > > >   TypeError: Error when calling the metaclass bases
>> > > >       function() argument 1 must be code, not str
>> > > >
>> > > > ffc.Constant is a function not a class...
>> > >
>> > > Sorry, my fault. It looks like someone did a clever trick when
>> > > implementing Constant in FFC as a function and not a class... :-)
>> > >
>> > > > > I get this error even if I just try to create a class named
>> > > > > anything that inherits from ffc.Constant.
>> > > > >
>> > > > > Does the metaclass construction in function.py have side-effects?
>> > > >
>> > > > Well, for it's use I think it works fine. The user should never
>> > > > bother with the metaclass, as the newly added quote to the docstring
>> > > > is mentioning.
>> > > >
>> > > > What we need to do is to assert the user use the Function class
>> > > > correct. I have tried to put in some checks with hopefully
>> > > > informative error messages. But I dont know if I have covered all
>> > > > bases. We need user feedback for this.
>> > >
>> > > I'm sure the checks are very good, but could the logic be broken up
>> > > to first split on the different types, so there's essentially one case
>> > > for each of the different options in the Function docstring, and then
>> > > a function is called for each case to do the work?
>> > >
>> > > > > I don't remember if we discussed this before, but would it be
>> > > > > possible (at least simpler) to instead define a simple Python
>> > > > > function that returns a "function" instance:
>> > > > >
>> > > > > class FooFunction(ffc.Function, ...):
>> > > > >     ...
>> > > > > class BarFunction(dolfin.Function, ...):
>> > > > >     ...
>> > > > >
>> > > > > def Function(V, *arg):
>> > > > >
>> > > > >     if foo:
>> > > > >         return FooFunction(...)
>> > > > >     elif bar:
>> > > > >         return BarFunction(...)
>> > > > >
>> > > > > This seems to be an easier solution. It would still be dynamic.
>> > > >
>> > > > This is mre or less what is going on right now. Instead of
>> > > > predefining the different classes they are defined dynamically in the
>> > > > __new__ function. We need to do this because the compiled functions
>> > > > are created at runtime.
>> > > >
>> > > > I can try to untangle the code a bit as asked for previously. The
>> > > > suggestion below which will extract the instantiation of compiled
>> > > > functions and discrete functions is one more radical example.
>> > >
>> > > ok.
>> > >
>> > > > > The only drawback would be that we can't do
>> > > > >
>> > > > >   isinstace(v, Function)
>> > > >
>> > > > This is not possible now either, as we are dynamically creating
>> > > > classes in __new__, and returning instances of other classes than
>> > > > Function
>> > > >
>> > > > Basically we are now dynamically creating Function classes that are
>> > > > all having the name "Function" but they are not of the same class.
>> > > > This can be illustrated by this:
>> > > >
>> > > >   class Function(object):
>> > > >       def __new__(cls,cppexpr=None):
>> > > >           if cppexpr is None:
>> > > >               return object.__new__(cls)
>> > > >           class CompiledClass(object):pass
>> > > >           class Function(CompiledClass):pass
>> > > >           return Function()
>> > > >
>> > > >   f  = Function()
>> > > >   f2 = Function(cppexpr="")
>> > > >
>> > > >   isinstance(f,Function)  # This will return True
>> > > >   isinstance(f2,Function) # This will return False
>> > > >
>> > > > I cannot see how we can avoid this, as we need to create classes at
>> > > > runtime. As you mention we could add a FunctionBase class that we
>> > > > could check against. But I was convinced by Martins argument
>> > > > previously that in PyDOLFIN code we never want to check if a function
>> > > > is both a cpp_Function and a ffc.Function, but rather for one of
>> > > > them. We could write this as a note for developers of PyDOLFIN, and
>> > > > live with it.
>> > > >
>> > > > I am a bit more worried for advanced python users that do not know of
>> > > > this. They could write user code that include isinstance(f,Function)
>> > > > statements, and it wont behave as expected.
>> > > >
>> > > > We could add a
>> > > >
>> > > >   CompileFunction(cppexpr=None, cppcode=None)
>> > > >
>> > > > which behave in the same manner as Function(cppexpr/cppcode) does
>> > > > today. In this way we can dynamically create functions that inherits,
>> > > > cpp_JitCompiledFunction, ffc.Function _and_ dolfin.Function, where
>> > > > the latter is a class which is either an empty class or one that
>> > > > holds the FunctionSpace. If we do this we also need to have an
>> > > > additional function/class to instantiate discrete functions.
>> > > >
>> > > > This way to handle the isinstance problem can also be implemented for
>> > > > all cases where Function is subclassed, i.e., both for Jit compiled
>> > > > and ordinary python Functors.
>> > > >
>> > > > But I have a feeling we add more complexity than we remove...
>> > >
>> > > If I understand this correctly, it seems to be a simplification, or at
>> > > least it makes it more explicit which we all know is better (import
>> > > this).
>> > >
>> > > But I would like a Function class (or function) on top that delegates
>> > > the creation of Function objects to these classes.
>> > >
>> > > > > This can be solved by creating an empty class FunctionBase that all
>> > > > > the special types inherit from.
>> > > >
>> > > > This is the easiest way to fix the isinstance problem, but I think it
>> > > > is a bit unintuitive to check for FunctionBase when you have created
>> > > > a Function object.
>> > > >
>> > > > So I have scretched three possible ways to have it:
>> > > >
>> > > >   1) As it is now (with clearer code). Everything is handled with
>> > > >      the Function class. You cannot check for isinstance(f,Function)
>> > > >
>> > > >   2) Add CompiledFunction and DiscreteFunction. You can now
>> > > >      check for isinstance(f,Function) everywhere.
>> > > >
>> > > >   3) Add FunctionBase, you can now check for
>> > > > isinstance(f,FunctionBase)
>> > > >
>> > > > I go for either 1 or 2.
>> > >
>> > > I'd like all of the above! :-)
>> > >
>> > > 1. One single Function class using the metaclass trick to return
>> > > instances with dynamic inheritance.
>> >
>> > Ok.
>> >
>> > Just for repetition:
>> > The metaclass is used to construct new classes that dynamically inherit
>> > Jit compiled classes, not instances. The latter is done in the __new__
>> > function.
>> >
>> > > 2. Add classes CompiledFunction, DiscreteFunction etc that handle all
>> > > the different ways in which the C++ part of a Function can behave.
>> >
>> > My point was to add these for the user when he/she want to instantiate
>> > one fo these classes. This would be instead of the instantiation
>> > functioanlity handled by __new__ in the present Function class, and the
>> > whole point with introducing them would be to be able to use
>> >
>> >   isinstance(u,Function)
>> >
>> > where u can be any of the possible Functions we want to have.
>>
>> Maybe we could add CompiledFunction since it's rather special, but I'd
>> like to avoid adding DiscreteFunction in the user interface. It would
>> be good if the Python interface could be kept very uniform with the
>> C++ interface:
>>
>>   Function v(V);
>>
>>   v = Function(V)
>>
>> > > The created instances will inherit from ffc.Function (later
>> > > ufl.Function) and one of these classes.
>> >
>> > I am not with you here, or you are not with me ;)
>> >
>> > I mean that the created Function need to inherit FFC.Function together
>> > with what ever cpp_Function and a dummy dolfin.Function that the user
>> > could use to check, as showed above.
>> >
>> > We need to do this because a function that is instantiated from a
>> > dynamically created class using Function.__new__ cannot inherit Function,
>> > we will get an inherit recursion.
>> >
>> > But if we split out the instantiation magic we can create classes that
>> > all inherit Function (my 2), or we can keep the instantiation magic and
>> > introduce a FunctionBase class (my 3).
>>
>> My only concerns are:
>>
>> 1. A simple interface that corresponds closely to the C++
>> interface. In C++, we have
>>
>>   Function: can be either discrete or user-defined (subclassed)
>>
>>   Constant: special case for constants to avoid too many different
>>             constructors in Function
>>
>> In Python, we could then have the two above, plus an extra version
>> which does not exist in C++ (for obvious reasons):
>>
>>   CompiledFunction
>>
>> Would this help?
>
> W.r.t. solving the isinstance(f,Function) problem, I think no. As the Function
> class then always have to be a discrete function. If we let a compiled
> function inherit Function to facilitate isinstance, we also force it to be a
> discrete function, which probably would make the compiled function abit
> schizofreniac.
>
> This goes to the core of the isinstance problem. Now Function is a versatile
> class that can do many things but we cannot use it to check isinstance. If we
> desides that this is important, we need to split out the instantiation of the
> compiled _and_ discrete functions, if not we're fine.
>
> Btw, is there any special function that defines a class's isinstance protocol?
> If there was we could define our own __isinstancecheck__?

You're insane. I'm happy to say there is no such thing.
(I think Python 3 adds some way of saying
"MyClass supports this protocol" without inheritance)

>> 2. A very explicit implementation which clearly separates the
>> different cases and makes the magic explicit in function.py.
>
> I will fix this.
>
> Johan


--
Martin


Follow ups

References