dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #05565
Re: About SparsityPattern
2007/10/8, Anders Logg <logg@xxxxxxxxx>:
> Martin Sandve Alnæs wrote:
> > 2007/10/8, Anders Logg <logg@xxxxxxxxx>:
> >> Martin Sandve Alnæs wrote:
> >>> 2007/10/6, Anders Logg <logg@xxxxxxxxx>:
> >>>> Martin Sandve Alnæs wrote:
> >>>> > 2007/10/5, Anders Logg <logg@xxxxxxxxx>:
> >>>> >> Martin Sandve Alnæs wrote:
> >>>> >> > Ola and I did some thinking on the SparsityPattern vs Epetra_CRSGraph
> >>>> >> > issue, ie the case where you want the Assembler to use an external
> >>>> >> > class for building the sparsity pattern. I think we came up with a
> >>>> >> > nice solution, in fact it looks much like the GenericTensor::create()
> >>>> >> > function we added earlier (as well as the ufc::form::create_*
> >>>> >> > functions).
> >>>> >> >
> >>>> >> > Basically, a GenericTensor knows the type of the
> >>>> >> > GenericSparsityPattern it expects in init(pattern). Thus we can do
> >>>> >> > this:
> >>>> >> >
> >>>> >> > in application:
> >>>> >> > A = Matrix()
> >>>> >> > assemble(A, ...)
> >>>> >> >
> >>>> >> > in assemble:
> >>>> >> > pattern = A.createPattern()
> >>>> >> > initPattern(pattern, ...)
> >>>> >>
> >>>> >> Is initPattern() a member function of Assembler?
> >>>> >>
> >>>> >> And that function computes the sparsity pattern from the mesh, dofmaps
> >>>> >> etc? Then it looks to me that it may only do so using the public
> >>>> >> interface of GenericSparsityPattern. Is that enough to build an
> >>>> >> Epetra_CRSGraph?
> >>>> >
> >>>> >
> >>>> > initPattern() is meant to be a member of Assembler, which runs over
> >>>> > the mesh and calls dofmap->tabulate_dofs(cell...) like during assembly
> >>>> > and passes rows and cols to insertBlock below, like the add function
> >>>> > in GenericTensor but without the values. However, I didn't think about
> >>>> > rank 1 and 0 tensor here, only matrices need this mesh iteration. And
> >>>> > I'm not on top of the parallell stuff...
> >>>> >
> >>>> >
> >>>> >> > A.init(pattern)
> >>>> >> > ...
> >>>> >> >
> >>>> >> > with
> >>>> >> >
> >>>> >> > class GenericSparsityPattern
> >>>> >> > {
> >>>> >> > virtual void insertBlock(rows, cols, ...) = 0;
> >>>> >> > ...
> >>>> >> > }
> >>>> >>
> >>>> >> What does insertBlock do? Is this all we need?
> >>>> >
> >>>> >
> >>>> > It adds the "element tensor" with given global row and col indices to
> >>>> > the sparsity
> >>>> > pattern. We probably need some more minor functions like setting
> >>>> dimensions.
> >>>> >
> >>>> >
> >>>> >> > class GenericTensor
> >>>> >> > {
> >>>> >> > GenericSparsityPattern * createPattern() const = 0;
> >>>> >> >
> >>>> >> > // similar to what we added earlier:
> >>>> >> > GenericTensor * create() const = 0;
> >>>> >> > }
> >>>> >> >
> >>>> >> > Then, for Epetra, we will have the classes:
> >>>> >> >
> >>>> >> > class EpetraMatrix: public GenericMatrix
> >>>> >> > class EpetraMatrixPattern: public GenericSparsityPattern
> >>>> >> > class EpetraVector: public GenericVector
> >>>> >> > class EpetraVectorPattern: public GenericSparsityPattern
> >>>> >> >
> >>>> >> > Other implementations can reuse the current SparsityPattern if they
> >>>> >> want to.
> >>>> >> >
> >>>> >> > In my opinion, the only ugly part is that
> >>>> >> > GenericTensor::init(GenericSparsityPattern & pattern) won't really
> >>>> >> > accept any subclass, but should have a type check and throw an
> >>>> >> > exception. But we can't get around that with a virtual init
> >>>> >> > function.
> >>>> >>
> >>>> >> The same problem shows up if we want to add mult() to the
> >>>> >> GenericMatrix interface (or any other function that involves
> >>>> >> interaction between the two different GenericFoo classes).
> >>>> >>
> >>>> >> So should we add type() as a method to all GenericFoo classes?
> >>>> >>
> >>>> >> If the GenericFoo classes should be extendable by a user (who is not
> >>>> >> allowed to edit GenericFoo), then type() could return a unique uint
> >>>> >> (not enum) for each backend (uBlas = 1, PETSc = 2, Epetra = 3). Then a
> >>>> >> user can add new backends 4, 5, etc.
> >>>> >
> >>>> >
> >>>> > What should we use that for? It smells of type switches, which means
> >>>> > code that uses this will have to be extended if a new backend is
> >>>> > added. It's better to have the functionality we need in the interface,
> >>>> > so the backend is purely specified by the GenericTensor object.
> >>>>
> >>>> Yes, I agree. But it looks to me like your suggestion (for sparsity
> >>>> pattern) would introduce the same type check.
> >>>>
> >>>> > In PyCC, Ola is currently rewriting CRS to be a direct implementation
> >>>> > of GenericMatrix. That's a typical task that users of other external
> >>>> > libraries might want to do, to use dolfin to assemble into their own
> >>>> > matrix formats without touching dolfin. If everything is contained in
> >>>> > the generic interface, that's possible.
> >>>>
> >>>> Yes, good. But to avoid the type switches (if we want to avoid them,
> >>>> see below) then maybe we should keep a single class SparsityPattern
> >>>> and force every backend to use it. This means we move the
> >>>> Epetra-specific stuff to EpetraMatrix::init(). The init function
> >>>> receives a SparsityPattern which should contain everything needed to
> >>>> build an Epetra-specific sparsity pattern.
> >>>>
> >>>> This means we need to copy some data, but I don't see how this can be
> >>>> avoided if we absolutely don't want any type switches.
> >>>>
> >>>> Avoiding type switches completely also means that we can't add
> >>>> something like mult() to GenericMatrix. I suspect we will be tempted
> >>>> to (re)introduce type switches (and a full envelope-letter design) at
> >>>> some point to have a more complete interface.
> >>>>
> >>>> /Anders
> >>> There's a huge difference between a _typeswitch_ in code that _uses_
> >>> the GenericTensor interface and a _typecheck_ in the _implementation_
> >>> of the interface. The typeswitch leads to dependencies all over the
> >>> code, the internal typecheck doesn't.
> >> I'm not sure I understand. Don't you want to add something like
> >> GenericMatrix::type()?
> >>
> >> Then you can use it either to simply throw exceptions when the type is
> >> wrong or have a switch if you want to handle the other cases.
> >>
> >> /Anders
> >
> > To check if "GenericVector * foo" is a MyVector *, the right way is to
> > use dynamic_cast<MyVector*>(foo) in C++, and isinstance(foo, MyVector)
> > in Python.
>
> ok, good. This is what I was looking for. I didn't realize that we have
> something that corresponds to isinstance() in C++.
If you do:
Foo* foo = dynamic_cast<Foo*>(bar);
foo will be NULL if bar wasn't a Foo* after all, or the correctly
typed pointer if it was.
> > If we need a switch to handle subtypes, a typeswitch, the code doesn't
> > scale to new backends. If I add my own backend in my own code separate
> > from dolfin (like the pycc::CRS matrix will be), it shouldn't be
> > necessary to touch dolfin. All dolfin code that uses this typeswitch
> > will fail to handle my backend. This destroys much of the advantages
> > of inheritance, and should be avoided if possible.
>
> I agree. But I thought your suggestion involved adding a type()
> function. I think you can have type() and type switches and still scale
> (by having each new backend "register" the new backend) but the longer
> we can avoid it the better.
Ok.
> > Do you have any particular cases that has to be implemented with a typeswitch?
>
> There are quite a few (all involving operating on objects with different
> backends), like
>
> x += y;
>
> where x is a PETSc vector and y is a uBlas vector.
I see, but do we really need or want to be able to do that? I'd think
that people usually will only want to use one backend at a time, but I
could be wrong.
> But in summary, I think your suggestion looks good.
Great.
> Just set up something like ~/martinal/hg/dolfin on fenics.org and we can
> pull your changes (preferrably in small portions).
>
> /Anders
I'll ask Ola to do it since he's working on implementing pycc::CRS as
a subclass of dolfin::GenericMatrix right now.
--
Martin
References