There is a plenty of reference material about polymake API, classes, and coding conventions.
To help you with the orientation, we have listed some typical development tasks accompanied with design suggestions and
recommended reading. The cases are introduced in the order of increasing (at our opinion) complexity.
A new object construction (like a cube or prism):
depending on the complexity of the algorithm and on efficiency considerations, you can choose to implement it either as a C++
client or directly as a user function in perl.
If you decide to write a client, you should start with the client tutorial.
The most relevant reference material is put together under the "Client Programs" navigation topic, first of all the
communication API, the Template Library, and
tips about Makefile setup.
As soon as you get your client compiled and tested, you can move it to one of your favourite locations for executables
(listed in PATH) or install it together with the standard polymake clients, provided you have the write access to the
installation directory.
If you choose the implementation in perl, you should get acquainted with the general rule file syntax,
since the user functions have to be defined in the rule files. Furthermore, you will need to learn the Poly::Object interface, at least how to get object properties.
We don't recommend to put the user functions into the standard polymake rule files, as they will be unavoidably overwritten
by the next polymake update. Create your own collection of rule files instead, and store its location in your file
~/.polymake/prefer.pl (at the beginning of this file you will find detailed instructions how to do it).
polymake will automatically load your rules.
A new algorithm as a (partial) replacement of a standard polymake rule (say, a new convex hull algorithm):
Like in the previous paragraph, it can be implemented either as a C++ client or directly in perl.
However, to enforce the new algorithm to be taken instead of a standard one, you must first write a
production rule around it. In the case of a client, the body of the rule will consist of a
single line calling a Modules::client function.
Then you must make your new rule the preferred one. It can be done by giving it a preference label or a
smaller weight than that of the standard rule. Refer to the convex hull rules (rule file apps/polytope/rational.rules) as an elaborated example. If your algorithm should be preferred only for some
special kinds of input data, you may supply the rule with the means of dynamical control: preconditions,
variable weight, or both.
To say it again, keep your rules separate from the standard ones.
A new property of the object:
First of all, you should choose a suitable property type and declare the
property in your rule file. Then you have to write one or more production rules creating this
property, as described in the previous paragraph.
You may choose to declare your property temporary if its representation is too big to be stored permanently
in the data file, or the computations are so easy that it doesn't hurt to repeat them each time the property is needed.
A new object method:
Object methods are mainly used for two purposes. The first one is very close to the idea of temporary
properties, that is, they perform some computation, but don't store the results in the data file. Usually they print the
results to STDOUT instead. For this kind of methods you should be familiar with the Poly::Object
interface and the rule file syntax.
The second large group of object methods do some visualization of the objects. If you are going to develop a new visualization
method, you should additionally get acquainted with the family of perl classes implementing graphical primitives, such as
Visual::Polygon or Visual::Graph. They aren't currently described in this documentation, you will have to learn on
examples scattered over visual.rules in the polymake applications and modules. Your task becomes yet more complicated if
the standard graphical primitives don't suffice your needs (say, you need circles or curves.) Then you have not only to
implement the new graphical primitives (which must be always derived from Visual::Object), but also define corresponding draw methods in the visualization back-ends capable to display them. The examples in javaview.rules or postscript.rules
should give you an initial idea how these methods look like.
An interface to an external software package:
The form of the interface depends on the capabilities of the package and on the role it should play for a polymake user.
using the software to compute object properties:
You will need a production rule, which prepares the input data, writes them into
a temporary file, runs some executable, reads the results from another temporary
file (you might want to study perl regular expressions closer, man perlre ), and converts them back to polymake
representation.
If the external program works like a filter, that is, reads from stdin and writes to stdout, then you can spare the
temporary files and communicate with the program using Poly::ProgramPipe class.
preparing the input for the software, run it separately:
In this case you should write a user method, which creates a non-temporary input file for the software.
The file name can be passed as an argument or automatically derived from the object name. An example of such method is fundamental_group, which prepares an input file for GAP.
visualization software:
This is the most complicated case. As already mentioned, the visualization subsystem of polymake is not yet documented
completely. You will have to study the code of other visualization back-ends, such as Geomview or Postscript packages. On the other hand, there are no strict laws about how to implement a back-end. The only required
components are global methodsdraw accepting graphical primitives and the initialization method new_drawing.
If you are going to distribute your rule file to other people working on different computers, you should include a autoconfiguration section, which checks the presence of the software package. This will prevent your
customers from puzzling about error messages coming deep from the rule code. In the most cases a simple check with find_via_path suffices.
A new object type:
Technically the introduction of a new object type is easy: it is a one-line declaration. But this can be a
difficult design decision.
A new object type can be either independent or derived from another existing object type. An independent object type should be
introduced as soon as a new mathematical object appears on stage, which is rich in own properties and can't be modeled well
using the existing types without loss of clarity or mathematical correctness. Examples would be hyperplane arragements in the
application polytope or knots in the application topaz.
A derived object type, on the contrary, should be only introduced with the aim to refine some concept or solve a representation
problem. As long as you can add new properties to an existing object type without creating contradictions, you should do so.
Even if you are working with a large family of objects with magnificent classification of (mathematical) subtypes and special
cases, you can still model it adequately using such features as rule preconditions and
undefined property values.
For example, the application polytope purposely does not define special types for unbouded polyhedra or simplicial
polytopes; they are much simpler expressed by boolean properties, and those rules which are applicable only to bounded or
simplicial polytopes state this constraint by appropriate pre-condition expressions.
But there are two object types, RationalPolytope and FloatPolytope, which differ in the representation of the point
coordinates (exact and flating-point, respectively) It is clear, that both representations can't co-exist in
the same object without endangering its consistency, therefore for each object one allowed representation must be chosen from
its very birth. Dividing the polytopes into these two subtypes is the most natural technical solution for these requirement.
Having once declared a new object type, you should remember to switch to its package each time you want to
add new properties, rules, or methods belonging to the new type.
A new application:
You can found a new application if you have the feeling that plenty of the new objects and algorithms you have developed have
nothing or very little to do with existing applications. There is no strict law about when to start with a new application.
You can perfectly go on populating a standard application with dozens of new types, but you can have equally perfect reasons to
found an application with a single object type in it.
The technical part of the task is rather easy. You create a hierarchy of directories rooted at apps/NEW_NAME similar to any
of the standard application, that is, include and src with C++ sources, rules with rule files, perllib with perl
modules etc. Then you rerun make configure in order to create the build directory for the new application and
populate it with necessary Makefile files.
If you have ventured so far to write a new application, you might be interested in supporting tools like automated test
environment and documentation generator. They are not included in the public distribution; please contact the
development team in order to get them.