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)
?
--
Anders
> Btw, is there any special function that defines a class's isinstance
> protocol?
> If there was we could define our own __isinstancecheck__?
>
> > 2. A very explicit implementation which clearly separates the
> > different cases and makes the magic explicit in function.py.
>
> I will fix this.
>
> Johan
signature.asc
Description: Digital signature
_______________________________________________ DOLFIN-dev mailing list [email protected] http://www.fenics.org/mailman/listinfo/dolfin-dev
