← Back to team overview

dolfin team mailing list archive

Re: Strange error from function.py

 

On Wednesday 03 December 2008 10:25:12 Martin Sandve Alnæs wrote:
> 2008/12/3 Anders Logg <logg@xxxxxxxxx>:
> > On Wed, Dec 03, 2008 at 09:27:15AM +0100, Johan Hake wrote:
> >> 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.
> >
> > The way the Function class is implemented in C++, its nature may
> > change dynamically. For example, it may start out as a user-defined
> > function and then become a discrete function. This happens at the
> > first call to Function::vector().
> >
> > The reason for this is to allow defining an initial condition for a
> > function by overloading eval() with say sin(x) and then use that same
> > function in a time-stepping algorithm:
> >
> > class InitialCondition
> > {
> >  void eval(...)  { values[0] = sin(x[0]); }
> > };
> >
> > InitialCondition u0(V);
> >
> > while (t < T)
> > {
> >   // Solve for u1
> >   ...
> >
> >   // Assign u0 to u1
> >   u0.vector() = u1.vector();
> > }
> >
> > This is very convenient to have in C++ but is not necessary to have in
> > Python, since one may dynamically project things to discrete
> > functions:
> >
> > u0 = project(InitialCondition(), V)
> >
> > I imagine it will work the same way for compiled functions. Anything
> > that inherits from dolfin::Function may potentially initialize a
> > vector of degrees of freedom.
> >
> >> 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.
> >
> > What is the big issue with checking
> >
> >  isinstance(v, FunctionBase)
> >
> > ?
>
> If I define compiled code outside of DOLFIN that contains a C++ subclass of
> dolfin::Function, it will not be a dolfin.FunctionBase and all this
> subclassing mess
> must be repeated in external user code for it to fit with other PyDOLFIN
> code.
>
> When you ask "isinstance(v, FunctionBase)", you're not likely to care if v
> is actually a subclass of this messy python wrapper stuff, you only care
> if it can be passed to DOLFIN C++ code (isinstance(v, cpp_Function))
> or to code expecting UFL (isinstance(v, ufl.Function)).

My point is that we internally, in e.g. assemble.py, should use 
isinstance(f,cpp_Function), where we have to check for that. 

FunctionBase is ment for a user. It maybe an overkill feature, but a user that 
writes code using isinstance(f,Function) wont get what he expect. There the 
FunctionBase comes in. 

A user that compiles his own cpp_Function that is _not_ a ufl/ffc.Function 
should know that he cannot check for isinstance(f,FunctionBase) and should 
never use it.

Mvh

Johan

> --
> Martin
> _______________________________________________
> DOLFIN-dev mailing list
> DOLFIN-dev@xxxxxxxxxx
> http://www.fenics.org/mailman/listinfo/dolfin-dev




References