scripting:start

This is an old revision of the document!


Scripting

From the technical point of view, there is no difference between commands you enter in the interactive session and the scripts: in both may any valid perl expression is accepted. The difference lies more in the psychology: while for simple interactive commands practically no knowledge in programming is needed, the scripting requires certain profoundness in the perl language.

But please don't be scared: even with minimal programming skills you can save a lot of time and typing. The very first scripting exercise can just consist of copying some lines from the interactive history buffer (available by invoking history; command or directly from the file ~/.polymake/history) into a separate script file. As you better get acquainted with perl and your programming experience grows, you'll be able to realize more and more complex ideas.

This page is not aimed, however, as an introduction in the perl language. There is a lot of excellent literature available on this topic, and even the man pages, otherwise notorious for their ineligibility for novices, are very instructive and rich in explanatory examples. Here you'll rather found details which are special to the polymake's “dialect” of perl.

A script can be called from the interactive polymake shell or from other scripts via the special function script:

script("scriptfile", arg1, ...);

As arguments any valid perl expressions may be passed. Alternatively, a script may be executed directly from the UNIX command line:

polymake --script scriptfile ARG1 ARG2

Here you can only pass strings (like file names) or numeric constants as arguments. Besides this restriction, you should keep in mind that if you execute a script in this fashion, the readline library isn't loaded at all, thus you won't be able to take any interactive actions like importing extensions or reconfiguring some rules. Normally you will hardly ever do it in your scripts, but should you need some interaction in some exotic case, just change the option from --script to --iscript .

A script can contain pretty anything allowed by perl syntax rules. However, to get access to polymake classes and functions, it needs a preamble:

use application "NAME";

It sets a default application for the rest of the enclosing lexical context (that is, normally, up to the next use application statement or the end of the script file, but may also be just the enclosing block). The notion of the default application has exactly the same meaning as the current application for the interactive shell: Functions and class names defined in or imported into the default application may be used without qualification, while names from other applications must be prefixed by the application name.

The script code is compiled in the package Polymake::User, the same as the interactive shell expressions are evaluated in. Thus the scripts can access non-local variables introduced in the shell and vice versa, having run the script once, you can use the variables and subroutines defined in the script. If you want to define additional packages, please define them as subpackages of Polymake::User or completely outside Polymake::, to prevent accidental clashes with polymake internal classes.

The script may define subroutines and/or contain file-level code. The latter is assembled together to a anonymous subroutine which is executed each time you call the script function; its return value is the last expression executed in the file-level code (or in a return statement, if any). The arguments are passed in the global array @ARGV, not in @_ as for usual subroutines. If you intend to use your script in both interactive and batch mode, you might want to build in some flexible recognition of argument types, for example, allowing for both ready objects and filenames to be passed:

  my $p=shift @ARGV;
  $p=load($p) unless is_object($p);

Since the script code may be repeatedly executed arbitrarily many times, you should put a special attention to variables requiring one-time initialization. Such initializations should be either put in a BEGIN block or guarded by ||= or //= operators. Also please note that if you introduce my variables on a file level and define other subroutines in the script which refer to these variables, the subroutines capture the values assigned during the first execution of the script. Even if your script changes the values of these my variables during each execution, the captured values in the subroutines will remain unaffected. (It'll be of no surprise for seasoned perl hackers familiar with the notion of closures).

You can modify the script file in a text editor without leaving the polymake session. The script function stores the timestamps of all executed script files, so the changes will be detected by the next call to script and the script file will be reloaded automatically.

Scripts can be kept in arbitrary folders. Unless the script you want to execute resides in the current directory, you must specify its full path in the script command. TAB completion assists you at this. There are, however, special locations, where the scripts are found just by name. Moreover, some locations impose special semantics on the scripts.

Neutral scripts, that is, those capable of working with arbitrary applications, and scripts explicitly switching the applications, can be kept at the following places:

  • $InstallTop/scripts – standard neutral scripts shipped with polymake
  • @lookup_scripts – additional directories of your choice containing your private scripts. This list is a custom variable stored in ~/.polymake/prefer.pl.
  • $Extension/scripts – neutral scripts coming from an extension

Application-specific scripts are kept in the applications' subtrees:

  • $InstallTop/apps/APPNAME/scripts – standard scripts shipped with polymake
  • $Extension/apps/APPNAME/scripts – scripts coming from an extension

These scripts don't need the preamble use application, it is automatically imposed. An application-specific script can be executed with the script command if its application is the current one or is imported by the current application.

Your collection of scripts will probably grow over the time, some scripts sharing common code parts. The common code is usually extracted in separate .pl or .pm files included with require statement. The lookup rules for these files is a bit different: the directories to be searched have to be inserted into the global array @INC. The appropriate push or unshift statements can be placed in ~/.polymake/prefer.pl somewhere in the package Polymake::User section. We recommend, however, to create a private extension and store the scripts and the included files in subdirectories scripts and perllib respectively. In this setting you don't have to manipulate any lookup list.

As already stated above, polymake speaks a slightly enhanced dialect of perl. It contains some additional notions and operations, partially borrowed from C++. Please note that all syntactical enhancements are only recognized in the rule files, scripts, and include/module files located in the applications' perllib subtrees (and, obviously, in the interactive shell). Modules loaded from elsewhere, including the whole standard library, are interpreted strictly according to the official perl syntax.

The name lookup rules are extended towards more resemblance with C++ namespaces. In standard perl, an unqualified variable or subroutine name always refers to the current package; a qualified name is always treated like an “absolute path”, starting in the global package main:: . In polymake, each package has a list of associated packages where all names are looked for; additional lookup lists can be introduced in each lexical scope. To prevent the threat of introducing bugs by mistyping or confusing variable names, all package variables have to be explicitly declared.

Variable declaration

Variables of whatever kind must be declared, either in a standard pragma statement:

use vars '$bla', '@blu', '%bli';

or using polymake's own function:

declare $bla=123;
declare @blu, %bli;

The declare function works exactly like my statement: it can contain several variables at once and it can be combined with initialization. The declare statement must be placed in the package owning the variables, hence qualified names are not allowed there. The declaration must be the first statement referring to the variables being introduced - first in the order of the program execution, but not necessary in lexical sense.

my (lexically scoped) variables are not affected by the namespace lookup rules at all and should be used whenever possible. our variables do not fit well in the namespace concept and should be avoided.

Special variables residing in the global package such as $_, @_, @ARGV, @INC, %ENV, %SIG, etc. don't need to be declared. The package inheritance list @ISA and sort placeholders $a and $b are also exempt from declaration.

Name lookup

Being in a package AA::BB::CC, an unqualified reference to the variable $x is interpreted as either $AA::BB::CC::x, $AA::BB::x, or $AA::x, depending on which package contains a declaration of $x. The lookup is done in the shown order (from the most inner package towards outside); it is performed only once, at the very first reference to $x. The lookup result is then stored in the symbol tables of all packages visited during the search. (Therefore it is important to have the declaration been executed before all other code referring to $x.) Note that the global package main:: is never involved in name lookup.

The same rule applies to subroutines not declared as methods: a call to y() is interpreted as either AA::BB::CC::y(), AA::BB::y(), or AA::y(), depending upon which package contains a subroutine sub y.

Names can be partially qualified if the leading part of the package path is the same as of the current package. For example, $BB::z will be treated as $AA::BB::z, and $CC::w will refer to $AA::BB::CC::w from within AA::BB, AA::BB::DD and any other enclosed package.

Package names themselves can be shortened too, but only in few contexts. The most important place is static function call, like a constructor call new C(1,2,3) . The package name must be spelled completely in bless statements, in @ISA assignments, in UNIVERSAL::isa() checks, and ref comparisons. As a partial alternative for the latter two cases there is a new expression instanceof CLASSNAME($object), delivering a boolean TRUE if $object is born in package CLASSNAME or derived thereof; here the CLASSNAME is subject to namespace lookup again.

The package statement used to switch between packages also requires full package names, but there are convenient shortcuts for switching between related packages: _ denotes the previous package, and __ denotes the one enclosing it. This notation should resemble the widely adopted designators of the “current directory” . and the “parent directory” ..

package AA::BB;
# this is interpreted in traditional way, as ::AA::BB

package _::CC;
# here the package AA::BB::CC starts

package __::DD;
# here the package AA::BB::DD starts

package __;
# here we've come back to AA::BB

Extending lookup scope

Each package compiled from polymake source code possesses its own lookup list. By default it consists of all enclosing packages, but can be easily extended by arbitrary further packages. This is accomplished with a statement

using namespaces "OTHER_PACKAGE", ...;

put somewhere in the package being extended. Please note that this is a real function call, not a pragma, therefore it must be executed early enough to have effect on the package code. The extension relation is transitive.

This extension mechanism is involved, for example, in the process of importing applications: the application polytope imports everything from applications graph and common, so the lookup list for the package Polymake::polytope contains Polymake:::graph and Polymake::common along with the enclosing Polymake. If some other application would import the application polytope, it would automatically inherit its entire lookup list.

Another form of extending the lookup scope are lexical lookup lists. They are established using pragma statements

use namespaces "OTHER_PACKAGE", ...;

Such a lookup list pertains to the lexical scope containing the pragma statement, it effects the code following it up to the end of the scope or the corresponding no namespaces …; statement regardless of the package boundaries. For scripting, you won't probably use this pragma directly, but rather its derivative

use application "APPNAME";

Please note the difference between the package-based and lexical lookup lists: if some name has been found using the former, its entry in the current package symbol table is modified as to point to the imported item, so that all other references to this name in the current package are redirected at once; but if the name lookup has been satisfied using the lexical list, only this very reference is redirected, but not other occurrences elsewhere in the code. This allows, for example, for different applications to define functions with identical names; the user's script can still call them using unqualified names, the correct instance being chosen depending upon which use application pragma is in effect at each place.

Analogously to C++ templates, polymake allows classes and functions to be parameterized with data types. Due to obscure technical reasons, definitions have to reside in rule files. However, you can use the templates in the scripts too, that's what we are describing here.

The class templates can be used in constructors and other static method calls:

$v=new Vector<Rational>(1,2,3);
if (instanceof Vector<Rational>($v)) { ... }

The function templates can be called, whenever appropriate, with explicit type arguments:

$v=unit_vector<Rational>(3);
$c=cast<LatticePolytope>(cube(3));

This syntax is resembling the C++ language as close as possible; unfortunately, this comfort was achieved at the price of severe mistreatment of perl parser. The consequence is that it can't always correctly recognize the end of the argument list. If you are going to put several expressions involving templates into a single list (for example, when calling a function), you must enclose each expression in an extra pair of parentheses:

$p=intersection( (new Polytope<Rational>(POINTS=>$p1)), (new Polytope<Rational>(POINTS=>$p2)) );

Hash Tables

Normally, hash tables store the keys as text strings. All other data types passed as keys get converted to strings before they are used in lookup and other operations. polymake allows to have hash tables keyed with references of any kind. Each reference is a unique key being equal only to itself. There are two restrictions, however:

  1. each hash table can have either string keys or reference keys, but not a mix of both; the allowed kind is determined by the first key being inserted in the table.
  2. reference keys stored in the table are weak, they don't contribute to the reference count of the objects they are pointing to. If some object has gone, and later a key referring to it is retrieved from the hash table, polymake will crash with high probability. Destroying the reference-keyed hash table, is, however, harmless at any moment.

Another enhancement related to hash tables is the push function merging hash tables together:

push %hash, %other_hash;

Localizations

The standard local operator is very useful as it allows to make exception-safe temporary changes. polymake enhances this functionality in two aspects:

  1. local works with package variables only. Using new functions local_scalar, local_array, local_hash, and local_sub, you can temporarily modify each data type passed by reference too:
    $a=[1,2];
    local_array($a, [3,4]);

    There are further functions doing temporary changes, which are reverted on leaving the block scope:
    local_incr($scalar, number) – add a number
    local_shift(\@array) – hide the first element
    local_pop(\@array) – hide the last element
    local_shorten(\@array, n) – hide abs(n) elements at the front (n>0) or back (n<0) end
    local_unshift(\@array, list) – prepend elements
    local_push(\@array, list) – append elements
    local_swap(\@array, i1, i2) – exchange two elements with given indices

  2. You can postpone the reverting actions to an outer enclosing block scope, even located in a different subroutine. You create a special guard object in the outer scope and pass it to other subroutines. All temporary modifications remain in effect until the guard object is destroyed:

    sub func {
      my $guard=shift;
      $guard->begin_locals;
      local $xyz=123;
      $guard->end_locals;
    }
    declare $xyz=0;
    {
      my $guard=new Scope;
      func($guard);
      # $xyz is still 123
    }
    # $xyz reverted to 0 now

    All local_XXX functions described above can be used in the block begin_locals .. end_locals too.

    There is one session-scope guard dwelling in the variable $Polymake::Scope (aka $Scope due to namespace lookup rules). Its lifetime ends with the complete interpretation of the current input line in the interactive shell, or complete execution of the script started in batch mode. This guard is used, among others, by prefer_now commands, as well as for purging temporary properties from the objects.

Classes

Construction

A new object may be constructed in three ways:

  1. Create an empty object

    new Polytope()

    with a type given literally and an undefined name

    new Polytope("name")

    … with given name

    $t->construct->()

    with a type given by an ObjectType expression

    An empty object may be filled with properties one by one, using take or direct assignment methods described further. The initialization phase is completed as soon as one of the following object methods is called:

    • commit, provide, or get_schedule
    • give or direct query method asking for a property not contained in the initial set

    After this, the object becomes immutable, that is, only properties declared as mutable may be changed and/or added.

  2. Create an object with all initial properties at once

    new Polytope(INEQUALITIES=>$ineq, EQUATIONS=>$eq)

    with a type given literally and an undefined name

    new Polytope("name", POINTS=>$pts)

    … with given name

    $t->construct->(POINTS=>$pts)

    with a type given by an ObjectType expression

    The object created this way is immutable from the very beginning.

  3. Create a copy of another object

    $p->copy

    as an exact copy less temporary properties

    new Polytope<Float>($p)

    as a copy of the same or related type.

    The types are called related if either one of them is derived from another, or both stem from the same parameterized type. If the target type is the ancestor of the source type, only properties being common to both types are copied, the rest is silently discarded. The same applies for locally extended subobject types: only properties defined for the independent object type are copied. Copying from one template instance to another implies a conversion of all properties whose types are dependent on the parameters being different between the target and the source.

    For example, let $p be a LatticePolytope:

    • new Polytope<Rational>($p) won't not copy LATTICE_VOLUME, SMOOTH, and any other lattice-specific property
    • new Polytope<Float>($p) will additionally convert VERTICES, FACETS, and all other coordinate-related properties from Matrix<Rational> to Matrix<Float>
    • new Graph($p->GRAPH) won't copy EDGE_DIRECTIONS as this property makes only sense for a graph of a polytope and not for an autonomous graph.
    • new PointConfiguration($p) is invalid, as the source and target types are unrelated.

    new Polytope($p, "VERTEX_LABELS" => $l)

    like above, but adding (or replacing) properties

    $p->copy_permuted(VERTICES => $v)

    first finds a permutation mapping the listed properties of the source object $p to the given values (here: ''%%$p→VERTICES'' => ''$v''), then applies it to all source properties and stores the results in the new object. Invariant properties are copied verbatim. If no suitable permutation can be found, an exception is raised. === Name and Description === The name is supposed to be a short string distinguishing the object from all others, but its uniqueness is only required for multiple subobjects (described further below). The name appears in the drawings, where appropriate, as part of drawable object identifiers (in visualization engines supporting it, e.g. povray or javaview). The name is also included as the stem into the default file name if the object is to be stored in a file. The name is optional; however, if a unnamed new object is assigned to a non-local variable (like this: ''$p=new Polytope(...);'' or ''$c=cube(3);'' then the variable name is used as the object name too. The description is an optional text kept together with the object. It may consist of several words, paragraphs, or even contain some markup like HTML. polymake core system does not use the description, but some construction clients leave sensible comments there. Both attributes are mutable, that is, they may be freely changed at any time using direct assignment: my $name=$p->name; my $descr=$p->description; $p->name = "NAME"; $p->description .= <<"_END_"; SOME LYRICS _END_ === Type === The type of the polymake "big" object is represented by a //prototype// object of class ''[[#ObjectType|Polymake::Core::ObjectType]]''. It keeps the entire knowledge base about the object type, but most of it is used in the guts of polymake. For scripting purposes, the following operations on types might be of interest: ? ''$t=$p→type;'' :: returns the [[#ObjectType|prototype object]] ? ''$p->type==$q→type'' :: evaluates to TRUE if both objects are exactly of the same type ? ''$p→isa(“TightSpan”)'' :: returns TRUE if ''$p'' is of the given type or of a derived one ? ''$p->isa($q→type)'' :: returns TRUE if ''$p'' is of the same type as ''$q'' or of a derived one ? ''cast< TighSpan<Rational> >($p)'' :: changes the type of ''$p'', returns ''$p'' (no copy) .. The allowance for a type cast is more restricted than for a copy constructor: only moving from basis to derived type and back is allowed. Casting to a basis type leads to the lost of properties defined for the original (derived) type only. === Properties === ? ''$l=$p→lookup(“VERTEX_LABELS”);'' :: returns the property value or ''undef'' if the property does not exist. [[reference:rules#shortcuts|Shortcut rules]] may be applied if necessary. ? ''$p→VERTICES'' ? ''$p→give(“VERTICES”)'' :: returns the property value; applies [[reference:rules|production rules]] if the property does not exist yet. An ''undef'' as result means that the object does not have enough initial properties to compute the requested one, or that all production rules delivering it turned out to be infeasible due to [[reference:rules#preconditions|preconditions]]. ? ''$p→VERTICES | POINTS'' ? ''$p→give(“VERTICES | POINTS”)'' :: retrieves the value of one of the alternatives, whatever exists or is cheaper to compute ? ''$p->FACETS=$f;'' ? ''$p->take("FACETS", $f);'' :: creates a property. Unless the property is declared as //mutable//, this operation is only permitted during the object initialization phase or in a production rule. ? ''$p->FACETS_THRU_VERTICES(temporary)=$F;'' ? ''$p->take("FACETS_THRU_VERTICES", temporary, $F);'' :: creates a property temporarily. It survives until the end of the execution cycle. In the interactive mode, the cycle ends after the complete evaluation of the last input line; in scripting mode it is the termination of the script. Under no circumstances can temporary values be stored in the XML file; an explicit ''save'' of the object discards the temporary values instantly. However, please note the difference to older versions of polymake: now it's a concrete value that can be declared as temporary, while earlier it was an inherent attribute of a property. ? ''$p→remove(“POINTS_IN_FACETS”, …);'' :: removes one or more properties from the object. A //mutable// property can be removed at any time. A normal property may be removed only if the object contains enough data to reconstruct this property later. A production rule may remove a property only if it is one of its sources. Methods ''give'' and ''take'' do exactly the same as the operations using the unquoted property name as a method. The only difference lies in small additional costs for the former caused by property name lookup performed in each call. Thus you should generally prefer the direct property-named methods unless your code must handle flexible property names. === Subobjects === A subobject is an Object attached to another (parent) Object as a property. Subobjects are stored together with their parents in the XML files. Apart from that, they are just normal "big" objects with their own properties, rules, and methods. ? ''$g=$p→GRAPH;'' ? ''$g=$p→give(“GRAPH”);'' :: returns the reference to a subobject; please notice that this looks completely identical to the retrieval of simple properties. ? ''$g→parent'' :: returns the reference to the parent object (here: ''$p''); for top-level (autonomous) objects this operation yields ''undef''. ? ''$g→property'' :: returns an object of type ''Polymake::Core::Property'' describing the property under which ''$g'' is stored in its parent; here ''$g→property→name'' will return ''"GRAPH"'' ? ''$p→GRAPH→ADJACENCY'' ? ''$p→give(“GRAPH.ADJACENCY”)'' :: an example of querying a property through several layers of object hierarchy. If the property or even the entire subobject does not exist yet, it will be created, as usual, by production rules. ? ''$p->GRAPH->ADJACENCY=$a;'' ? ''$p->take("GRAPH.ADJACENCY", $a);'' :: creates a property in a subobject; the same constraints apply as in the simple case above. ? ''$p→GRAPH→remove(“NODE_DEGREES”);'' ? ''$p→remove(“GRAPH.NODE_DEGREES”);'' :: removes a property in a subobject. The same restriction apply here: either the property must be //mutable//, or the object must have enough data to reconstruct it later. ? ''$p→remove(“GRAPH”)'' :: removes the complete subobject. The restriction about being mutable or reconstructible applies to all properties of this subobject, also recursively to all deeper subobjects. One can also create a subobject property from a complete object like this: ''$p->GRAPH=$g;'' If ''$g'' is autonomous and does not stem from a XML file, it is directly attached to ''$p'', otherwise a copy is created and ''$g'' stays unchanged. If ''$p'' is still in an initialization phase, ''$g'' (or its copy) is reopened for filling with properties as well. === Multiple subobjects === While normal (singular) subobjects are treated much like other properties, multiple subobjects require special methods capable of choosing the desired instance or iterating over them all. Multiple subobject instances must have non-empty, unique names; uniqueness (among all instances of the same property in one parent object) is enforced. == Construction of multiple subobjects == The method ''add'' is used for creating and/or attaching multiple subobjects to the parent. It has several flavors, resembling the construction modes of top-level objects: ? ''$lp=$p→add(“LP”);'' :: creates a new empty subobject to be consequently filled with initial properties. Its name will be of the form "unnamed#NN". ? ''$lp=$p→add(“LP”, LINEAR_OBJECTIVE ⇒ Vector, …);'' :: creates a new subobject and initializes it with given properties. No further immutable properties can be added to it afterwards. ? ''$lp=$p→add(“LP”, “name”, …);'' :: creates a new subobject with a desired name. If there is already another sibling subobject with identical name, the name will be amended with a suffix #NN in order to ensure its uniqueness. ? ''$my_tr=$p→add(“TRIANGULATION”, $tr);'' :: takes an existing object ''$tr'' and attach it as a subobject. Usually, the object ''$tr'' itself will become a property of ''$p'', but in some cases, as explained above, a copy will be created. The return value is always the child subobject of ''$p''. ? ''$my_tr=$p→add(“TRIANGULATION”, $tr, WEIGHTS ⇒ Vector, …);'' :: takes an existing object, adds further properties to it, and attaches it as a subobject. In any flavor, you can add an option ''temporary'' directly after the name of the property, specifying that the subobject is to be added temporarily. **Note:** For the sake of convenience, the very first instance of a multiple subobject may be created in the same manner as singular subobjects, that is, using ''take()'', providing initial property values in the constructor list, or assigning values to property access methods during the initialization phase. == Examining multiple subobjects == Having added a subobject instance, you can retrieve it later using numerous search criteria. Some of the retrieval methods can even create a new subobject on the fly, as described in the corresponding remarks: ? ''$p→LP'' ? ''$p→give(“LP.MAXIMAL_VALUE”)'' :: access the default subobject instance, that is, a subobject with ordinal number 0. If no subobject of the requested type exists and there are production rules creating new instances, plans the cheapest rule chain and returns a new instance. ? ''$p->LP->[$i]'' ? ''$p->give("LP")->[$i]'' :: select the subobject instance by its ordinal number ? ''$p→LP(“name”)'' ? ''$p→give(“LP”, “name”)'' :: select the subobject by its name; if no match found, returns ''undef'' ? ''$p->LP(sub { $_→MAXIMAL_VALUE >= 0 })'' ? ''$p->give("LP", sub { $_→MAXIMAL_VALUE >= 0 })'' :: select the first subobject satisfying the filter expression; if none satisfies the criteria, returns ''undef''. The subobject under test is passed in the variable ''$_'' . ? ''$p→SCHLEGEL_DIAGRAM(FACET ⇒ 1, …)'' ? ''$p→give(“SCHLEGEL_DIAGRAM”, FACET ⇒ 1)'' :: select the subobject by the values of given properties; if no match found, a new subobject will be created and initialized with exactly these properties ? ''$p→give(“SCHLEGEL_DIAGRAM”, FACET ⇒ 1, temporary)'' :: the same as above, but in the non-match case the new subobject will be created temporarily ? ''foreach $lp (@{$p→LP}) { … }'' :: iterates over all existing instances ? ''$p→LP→list_names'' :: returns the list of names of all existing instances ? ''$p→LP(…)→select_as_default'' :: permanently makes the selected instance the default one, which simply means moving it at the front of the instance list ? ''$p→LP(…)→select_as_default_now'' :: temporarily makes the selected instance the default one; the change is reverted at the end of the current user cycle == Removing multiple subobjects == ? ''$p->remove($lp, …);'' :: removes the given instance of a multiple subobject. Several instances may be removed at once. ? ''$p→remove(“LP”);'' .. removes //all// subobjects attached as a LP property. === Rule planning === If a rich set of properties to be computed for an object is known in advance, it might be advantageous to find a rule sequence delivering all of them in a bundle rather than resolve the single requests one by one: $p->provide("VERTICES", "SMOOTH", "N_LATTICE_POINTS", ...); will find an optimal rule sequence and execute it, so that all requested properties will be stored in an object upon its termination. If the initial state of the object is insufficient for successful computations, an exception will be thrown. Sometimes you have to perform some computations on a family of similar objects, starting from the same set of initial properties. You can save a significant amount of rule planning time if you create a tinned rule schedule using one representative of the object family and then apply this schedule to all objects in turn. $s=$p->get_schedule("VERTICES", "SMOOTH", "N_LATTICE_POINTS", ...); delivers such a [[#RuleChain]] object. It returns ''undef'' if the initial set of properties is not sufficient for the computations. Please note that albeit ''$p'' won't in general have the requested properties after the completion of ''get_schedule'', it can be nevertheless changed in the course of planning, if any rule preconditions had to be evaluated. To get all the requested properties, you have to apply the schedule to ''$p''. === Attachments === Attachments are arbitrary data pieces stored together with the object. They are treated much like mutable properties, that is, they can be added, changed, and removed at any time. Each attachment is identified by its name, which is supposed to be a short string. Attached data must be either a perl primitive scalar (a number or a text string) or a data structure of a declared property type. Anonymous arrays or hashes are not allowed. ? ''$p->attach("NAME", $data);'' :: adds an attachment or replaces the existing one under the same name ? ''$p→get_attachment(“NAME”)'' :: retrieves the value of the attachment or ''undef'' if it does not exist ? ''$p→remove_attachment(“NAME”)'' :: removes the attachment ? ''$p→list_attachments'' :: returns a list of attachment names Attachments are taboo for production rules, as they are inherently volatile and don't contribute to the stable state of the object. === Miscellaneous === ? ''$p→list_properties'' :: returns a list of names of properties stored in the object. Useful command for printing: :: ''print join(“, ” $p→list_properties);'' ? ''$p→list_properties(1)'' :: recursively descends in subobjects. To get more legible print-out, separate entries with line feeds: :: ''print map { “$_\n" } $p→list_properties(1);'' ? ''$p1->diff($p2)'' :: returns 0 if both objects are of the same type and have pairwise equal sets of properties. ? ''$p→dont_save'' :: clears the //changed// flag of the object. If it was initially loaded from a data file, the file remains untouched. Please be aware that this method alone is not equivalent to a //undo// function: if you want to revert all changes, you have to reload the object afterwards: ''$p=load(“FILENAME”);'' ==== ObjectType ==== ObjectType class is primarily used in the deep internals of polymake, first of all in the rule planning. For scripting purposes, only few methods may be of interest: ? ''$t=$p→type;'' :: gets a type of an Object ? ''$t=typeof Polytope<Rational>;'' :: constructs a literally specified type; it must be known in the current application. ? ''$t=Polytope->type($t2→type);'' :: constructs a type with dynamic parameters ? ''$t=application->eval_type($string);'' :: constructs a type specified by its full name in string form, e.g. ''$string=“Polytope<Rational>”;'' .. The type must be known in the current application. ? ''$t=application("appname")->eval_type($string);'' :: constructs a type defined in the specified application. ? ''$t→isa(“Polytope”)'' ? ''$t->isa($t2)'' :: returns TRUE if the type represented by ''$t'' is the same or derived from the other one. ? ''$t→generic_name'' :: returns the (generic) name as a string, e.g. "Polytope" ? ''$t→full_name'' :: returns the fully parameterized name, e.g. "Polytope<Rational>" ? ''$t→lookup_property(“NAME”);'' :: returns an object of the class ''Polymake::Core::Property'' or ''undef'' if no such property is known Calling any other methods or, even more, changing anything within ObjectType instances will inevitably lead to heavy disfunction or crash of polymake. Unfortunately, perl practically does not provide any protection of methods, hence this warning. ==== RuleChain ==== This is a tinned sequence of production rules. RuleChain instances are obtained by calling ''get_schedule'' method of an object. Once created, a schedule can be applied to an arbitrary number of objects, including the one used to compute the schedule: $s=$p->get_schedule("PROPERTY1", "PROPERTY2", ...); $s->apply($p); $s->apply($p2); Please be aware that the rule sequence will stay optimal only for objects being similar to the one used to compute the schedule, that is, having the same set of initial properties and satisfying all involved preconditions. ''apply'' returns TRUE if and only if all preconditions and rules in the sequence have successfully finished. Otherwise it //silently// terminates and returns ''undef''. The reason of being silent in the case of a failure is that the object still has a consistent state and the lacking properties can be computed in a usual way via on-demand scheduling (albeit no more optimally from a global point of view). If you are really interested in watching the schedule execution, increase the value of ''$Verbose::rules'' before applying it. RuleChain offers only one more useful method, namely listing the rule headers in the planned execution order: print map { "$_\n" } $s->list; ===== Using C++ objects ===== For many C++ classes from polymake template library, as well as for some classes from STL and third-party libraries bundled with polymake, convenient perl wrappers are provided, allowing to work with the objects directly from perl code. The following gives a rough overview of the most common use cases; to get the complete picture, please refer to the release documentation and study the rulefiles, especially common::basic_types and other ones included there. Starting with this examples, you will be able to easily define further wrappers for your favorite C++ classes and functions in your own extensions. The first and most important common design principle of all wrappers for C++ objects which you should always keep in mind is that they appear as references on the perl side. This means in particular that an assignment of two perl variables ''$a=$b;'' does not involve copying of C++ objects: you still have a single object, just now hanging on two references. If you change the object via ''$a'', you will see these changes via ''$b'' too. However, this does not apply to //all// changes. For classes with overloaded arithmetic operators like ++ or +=, perl automagically (it's not a typo, this word indeed occurs in the perl documentation) clones the object to be changed prior to executing the operator. Thus be careful and watch out. If in doubt, you can always create an explicit copy calling the copy constructor: ''$a=new TYPE($b);'' ==== Construction ==== Except for few special cases, all wrappers for C++ classes are equipped with a set of standard constructors. Besides the already mentioned copy constructor, these are the default constructor ''new TYPE()'' and the parsing constructor ''new TYPE(“string”)'' taking a printable representation of the value. Container types like Array or Vector also have a list constructor ''new TYPE([ value, value, … ])'' taking an arbitrary list of elements. If you definitely know that more than one element is going to be passed, you can also omit the square brackets. The same applies for composite types like Pair, they always have a constructor taking the list of initializers of their members. Furthermore, type-specific constructors may be defined in the [[reference:rulefiles#property_type_scope_definitions|rulefiles]]. ==== Common operations ==== Almost all C++ classes wrapped in polymake have an automatic conversion to character strings, allowing to print them directly to the screen or write into text files. Also almost all classes have comparison operators ''== !='' , and if they support full ordering, all other comparison operators ''< > ⇐ >='' as well. ==== Numbers ==== The GMP-based classes Integer and Rational behave much like normal numeric scalars. All possible arithmetic operators are overloaded, as well as the most common operations like ''gcd'' and ''lcm''. Literal integer constants having more digits than fit into a normal scalar (18 on a 64-bit platform) are automatically converted to Integer objects; a fraction of two literal integers like ''1/2'' is automatically converted to a Rational object. The special //infinite// values can be obtained by calling static functions ''Integer::inf()'' and ''Rational::inf()''. ==== Vectors ==== Container classes like Array or Vector support iteration in a loop: foreach my $elem (@{$v}) { ... } and in all iterating functions like ''map'' and ''grep''. As far as the underlying C++ class supports random access to its elements, it is available as a normal array access in both flavors, scalar and list: $v->[$i] @{$v}[$i..$j] Unless the C++ objects are write-protected (which normally happens to properties of "big" objects), the elements retrieved via random access may also be modified by assignment, overloaded operators, etc. Sparse containers like SparseVector behave like dense containers when iterated this way, that is, they expose all implicit zeroes in the gaps as if they were physically stored there. If you want to iterate over non-zero elements only, you must create an iterator object and dereference it like a normal scalar reference. Here is an excerpt from an interactive session: > $v=new SparseVector<Rational>([0, 1/2, 0, 0, 3/4, -5/6, 0]); > print $v->dim; # dimension 7 > print $v->size; # number of non-zero elements 3 > print "@{$v}" # pretends to be dense here 0 1/2 0 0 3/4 -5/6 0 > for ($iter = entire($v); $iter; ++$iter) { print "[", $iter->index, "]=", $$iter, "\n" } [1]=1/2 [4]=3/4 [5]=-5/6 > print $v->[2]; # an implicit zero element 0 > ++$v->[2]; # now it becomes a physical 1 > print "@{$v}" 0 1/2 1 0 3/4 -5/6 0 Classes Vector and SparseVector should only be used with numerical element types, because they overload all possible arithmetic operators. Class Array is a normal random-access container, which does not impose any requirements on the type of its elements. ==== Matrices ==== Matrix<Element> and SparseMatrix<Element> behave like two-dimensional arrays, stored in the column-changes-first order. They can be constructed from a two-dimensional list of elements, or from a list of vectors (taken as rows), or from a multi-line text string, or even from a file input iterator delivering the rows in printable form: $m=new Matrix<Rational>([[11, 12], [21, 22]]); $m=new Matrix<Rational>($vec1, $vec2); $m=new Matrix<Rational>(<<"_END_"); 11 12 21 22 _END_ open F, "<file.txt"; $m=new Matrix(<F>); Single elements can be accessed and, if permitted, modified via ''$m->($i, $j)%%'' . Single rows and columns can be dealt with like Vectors: ''%%$m→row($i); $m→col($j);'' To iterate over all rows and columns, use functions: foreach (@{rows($m)}) { ... } foreach (@{cols($m)}) { ... } Method ''$m→minor(row_set, column_set)%% allows to work with a minor like with a separate matrix object. As arguments for selecting rows and columns you can pass any containers with integer indices (including plain perl arrays), their complements (use a prefix operator ~ for this), or a special constant All. Block matrices can be built together from several matrix and vector objects using operator / for row-wise concatenation, operator | for column-wise concatenation, and function diag(x,y) for block-diagonal matrices. Basic linear algebra operations are available as functions transpose, inv, det, lin_solve, basis, etc. ==== Associative Containers ==== Class Map<Key,Value> behaves much like a hash table. You can retrieve and modify its elements: $m→{$key}=$value; exists $m→{$key} delete $m→{$key} and iterate over it using each, keys, and values. Class Set<Element> also supports exists and delete operators, but otherwise behaves like a normal one-dimensional container without random access. It also offers set-theoretic operations like union, intersection, difference, and symmetric difference, masqueraded as overloaded operators. Class IncidenceMatrix behaves like a two-dimensional sparse matrix. It supports the same access patterns as if it were a SparseMatrix<Bool>, while its rows and columns have the Set-like interface. ==== Complex objects ==== Some more complex objects like Graphs or Polynomials are also wrapped for directed use from perl code. Please refer to the release documentation and the tutorials for more details. ===== Debugging ===== You can use the perl debugger to debug your scripts. The only problem is how to set the initial breakpoint into your script: When you start polymake under the debugger <code>perl -dS polymake</code> not only your script, but also all the applications and even some of the polymake core modules are still not loaded. Stepping through polymake bootstrap is very tedious. A much more convenient way is to insert a call to stop_here(); at the very beginning of your script. This little function does nothing but prints its arguments to STDOUT. And it has a big advantage to be defined directly in the polymake main script, so you can just enter b stop_here c on the debugger prompt and call your script from the interactive shell or in batch mode. When stopped in the stop_here function, type r once and you'll get back into your script. Running polymake in debugger has one inconvenience you ought to be warned about: the debugger and the interactive shell share the same instance of the readline'' input line editor, so that the history buffer is filled by polymake commands only. You have to retype the debugger commands completely each time you want to repeat them; fortunately, most of them are just one letter short. And you may not try TAB completion entering debugger commands - you would land in polymake shell routines!

  • scripting/start.1445206629.txt.gz
  • Last modified: 2015/10/18 22:17
  • by gawrilow