Non-pipeline component programming
H. S. Teoh via Digitalmars-d
digitalmars-d at puremagic.com
Wed Sep 10 10:14:43 PDT 2014
Sorry for the very late reply; my internet connection was down for 2
weeks and only came back yesterday.
On Thu, Sep 04, 2014 at 08:03:51AM +0000, David Ledez via Digitalmars-d wrote:
> Dear T,
>
> I've read with a great attention your reply regarding ECS and OOP. You
> mention that you work on high-dimensional polytope and a data
> structure able to support fast topological query with compact storage.
> I would be very interested in having a look to your data structure. Do
> you have any document about your design and the concepts? Any github?
I'm sorry to disappoint, but I was merely alluding to the fact that
separating the range-based interface from the container allows one to
freely choose the most suitable structure to represent the polytope,
while using a proxy type to expose a range interface to allow generic
algorithms to work with it. My code is currently still very much a work
in progress, and I don't really have very much to show for it yet.
The basic design, however, is along these lines:
- A Polytope interface representing any abstract polytope-like structure
in the program (see below for details);
- A ConcretePolytope class (implementing a Polytope) that stores a face
lattice, represented as a graph, the nodes of which are obtained by
running a convex hull + lattice enumeration algorithm on the source
data.
- The Polytope interface has abstract methods for enumerating
sub-polytopes (as input/forward ranges), which concrete classes like
ConcretePolytope implement; the idea being that a face of a Polytope
is also a Polytope, and while the underlying implementation may be
different (e.g., the range of Polytopes representing the
(n-1)-dimensional facets may have a different representation from,
say, the 1-dimensional edges), they implement the same interface so
generic algorithms can work with them without special-casing. This is,
of course, just plain old OO, except perhaps for the range API part.
The sub-polytope enumeration methods return proxy types that implement
the range API, with Polytope as the element type, so you can use the
usual std.algorithm / std.range tools on them -- .find, .filter,
.chain, .cycle, .takeN, etc.. The proxy types are themselves
interfaces, meaning that you can write a proxy Polytope implementation
that performs some complex query over the lattice structure of
ConcretePolytope, and still present a nice range API over its
elements, and you can pass this to any of the usual range-based
algorithms for searching, filtering, composing, etc., with no code
change required in the algorithms. Again, this is just plain ole OO
(Liskov substitution, in particular), seasoned with some D-flavored
range-styled code. So you could, in theory ('cos I haven't actually
implemented this part yet), construct some complex query over the
polytope, and ask the program to enumerate, say, all 5D faces subject
to some given filter, say they must have a certain number of 3D
subfaces. In code, it could be as simple as:
ConcretePolytope p = ...;
auto query = new PolyQuery(...) // construct complex proxy Polytope from p
// The query proxy object is still a Polytope
assert(is(typeof(query) : Polytope));
query.faces(5) // get range of faces of dimension 5
.filter!((p) => p.faces(3).count > 10) // filter by faces with more than 10 3D faces
.copy((p) {
// print a list of face ID's in the result
writefln("%s", p.id);
});
Notice how the second block of code can actually apply directly to
ConcretePolytope (since it implements the Polytope interface), or to a
PolyQuery object (which represents some subset of the
ConcretePolytope's surface), or to anything, really, that implements
the Polytope interface -- perhaps a specific face of a
ConcretePolytope. Due to the range API implemented by the .faces
method, you can then process this range downstream using all of the
usual range-based algorithms.
Also note that by abstracting the implementation details of
ConcretePolytope away, it is also possible to implement a different kind
of concrete polytope, say one where the full face lattice isn't computed
beforehand, like I'm doing now, but which is lazily computed depending
on what kind of queries you run on it -- i.e., if you call faces(6),
then it will lazily construct the 6-dimensional faces of the polytope on
the fly as you iterate over the range, and if you iterate over, say, the
5-dimensional faces of a subset of the 6D faces, then the lattice
enumeration algorithm will only be run for those subset of faces, rather
than the entire polytope. As you probably know, the face lattice of a
general polytope can be exponential in the size of its initial
vertex/hyperplane representation, so allowing this kind of lazy
evaluation *without needing to change any downstream code* is a big
advantage.
It is also possible to implement a polytope whose faces are
mathematically-generated, allowing compact storage. For example, you
could generate it from a symmetry group, which alleviates the need to
store all vertices / hyperplanes (you can generate everything from the
vertex figures, for example). Again, none of the downstream code needs
to change; they will Just Work(tm). This isn't anything radically new,
really, it's just basic OO design, but just with a little range-based
spice added to it. :)
T
--
They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
More information about the Digitalmars-d
mailing list