Prerequisits

using namespace polymake;

Overview

This class is defined in <operations.h> It is automatically included together with all top-level data structures.
This class is defined in <ContainerChain.h> It is automatically included together with all top-level data structures.
This class is defined in <IndexedSubset.h> It is automatically included together with all top-level data structures.
This class is defined in <assoc.h> It is automatically included together with all associative containers (Map, SparseVector, IncidenceMatrix, etc).
This class is defined in <normalize.h>
This class is defined in <comparators.h> It is automatically included together with all top-level data structures.
enum cmp_value { cmp_lt=-1, cmp_eq=0, cmp_gt=1 };
is the return value of each comparator object. As in Perl, -1 means that the left hand side operans is less than that on the right hand side, 1 vice versa, and 0 means equality.
class polymorphic operator or function semantics besides the built-in operators
operations::neg -X arithmetic negation (vector, matrix)
operations::add X+Y addition (vector, matrix,) set union
operations::sub X-Y subtraction (vector, matrix,) set difference
operations::mul X*Y multiplication (all combinations of scalars, vectors, and matrices,) set intersection
operations::square sqr(X) product of scalar, vector, or matrix with itself
operations::normalize X/=sqrt(sqr(X)) normalize a vector with floating-point coordinates
operations::tensor tensor_product(X,Y) tensor product of vectors
operations::div X/Y division (vector or matrix thru scalar), matrix rowwise concatenation
operations::mod X%Y -
operations::bitwise_not ~X set complement
operations::bitwise_or X|Y vector and matrix columnwise concatenation
operations::bitwise_and X&Y -
operations::bitwise_xor X^Y symmetric set difference
operations::cmp X<=>Y (in Perl) compare objects arithmetically (scalar) or lexicographically (container), returning cmp_value. This functor (and other functionally interchangeable with it) is called thru this documentation a comparator. It can be expected to work more efficiently than two subsequent comparisons X<Y; X>Y as practiced in STL code.
operations::positive X>0 -
operations::negative X<0 -
operations::zero X==0 The specialization for double can be constructed with an epsilon argument. All numbers such that abs(x)<=epsilon are mapped to true. Default value for epsilon is 10-8.
operations::eq X==Y All comparison operators are based on cmp. Therefore it is necessary to specialize only the latter for each new data type.
operations::ne X!=Y
operations::lt X<Y
operations::le X<=Y
operations::gt X>Y
operations::ge X>=Y
operations::max std::max(X,Y)
operations::min std::min(X,Y)
operations::clear X() construct an object with its default container; reset an object to the default state (assignment version)
operations::concat concatenate(X,Y) concatenation (container, vector, matrix - columnwise)
operations::slice - select a subset (vector slice) using given element indices
operations::associative X[Y] find an element in an associative container (map)

Interface

Functors are classes with special interface defined in STL. Briefly resembling, one functor class encodes a single operation, defined as operator(). Furthermore it must declare the type of the operation result as result_type and the types of the input parameter(s) as argument_type in case of a unary operation or as first_argument_type and second_argument_type in the binary case.

Functors in Polymake Template Library obey the same rules, but with slight modifications:

  1. In STL, all the typedef's mentioned above always refer to unqualified data types. Polymake functor may define them also as references and/or with const attribute:

    result_type
    is always the exact return type of operator(), with const and reference attributes if needed.
    argument_type, first_argument_type, second_argument_type
    if it is defined as a reference, it signals that the functor requires the argument passed to operator() be always a lvalue with suitable const qualification (that is, no temporary objects allowed.)
  2. If an operation has an assignment version (such as X+=Y), it is also accessible in the functor class as

    Left& assign(Left& X, const Right& Y);

    They always return the reference to their first argument, as the built-in assigment operators do.

  3. When performing a binary operation on elements of sparse containers (vectors, matrices, ...), it is useful to explicitly define the result in the situation when one operand is an implicitly given "zero". (It is much more efficient than creating a temporary zero and computing the result with the standard binary operator.)

    If the functor takes no precautions on this, the result is assumed to be zero too, and the computation is skipped at all. This is OK for multiplication or bitwise "and", but not for addition or concatenation. The functors like the latter overload the operator() with specializations looking like:

    template <typename Iterator>
    operator() (partial_left, first_argument_type x, Iterator y);
    template <typename Iterator>
    operator() (partial_right, Iterator x, second_argument_type y);
    partial_left and partial_right are empty tag classes and must be written verbatim.
    Iterator arguments point to the next explicit element in the sparse container (that is, after the gap); they can be safely ignored in the most cases.
  4. All functors defined in namespace pm::operations are class templates parameterized with types of input argument(s). In the most cases, these types can be automatically deduced from the context, which makes the explicit specification superfluous. (Moreover, in a nested expression involving function and class templates, it is not always easy to guess the input argument type right!)

    Unfortunately, the syntax rules of C++ do not allow to intermix classes with "naked" templates. Therefore there are two special classes introduced with the only aim to wrap the parameterless template:

    template <template <typename> class Operation> class BuildUnary;
    template <template <typename, typename> class Operation> class BuildBinary;

    In the most constructions taking a functor class as a class template parameter or function argument, it can be specified either as a normal class or as a functor template wrapped in BuildUnary or BuildBinary. Example:

    std::vector<int> V;
     
    int sum=accumulate(V, pm::operations::add<int,int>());

    and

    int sum=accumulate(V, BuildBinary<pm::operations::add>());

    are effectively identical: both compute the sum of all vector elements.

    The shortcuts defined in the namespace polymake::operations help to make the application code still more concise. The expression above can be equivalently rewritten as:

    int sum=accumulate(V, operations::add());

    If the advantage of the "naked" functor templates seems to you as not too much essential, try to sum the rows of a matrix without using them.

  5. In some special cases it is more convenient to pass an iterator to the functor instead of data items it is pointing to. Most often such functors are used in pseudo-containers. There are three possibilities to build a functor with this kind of interface:

    Whatever design you choose, this special interface will be automatically recognized in all pseudo-container classes and basic algorithms.

Overloaded operations

A functor encoding an operation overloaded for various input types must be specialized for each input type (or pair in the binary case) that yields a different result type. If you need to define your own overloaded functors, you can use the discriminant mechanism shown in the following examples:

  1. Unary operation (here: negation)
    template <typename OpRef,
              typename Discr=typename object_traits<typename deref<OpRef>::type>::generic_tag>
    struct neg_impl; 

    The first parameter is an input operand type; it can happen to be a reference (especially when instantiated via BuildUnary), therefore the pure data type must be first distilled using deref. generic_tag can take one of following types (the list is not exhausting):

    is_scalar
    if an operand is a number (int, double, Rational, and so on;)
    is_set
    if an operand is a GenericSet
    is_vector
    if an operand is a GenericVector
    is_matrix
    if an operand is a GenericMatrix
    is_incidence_matrix
    if an operand is a GenericIncidenceMatrix
  2. Binary operation (here: multiplication)
    template <typename LeftRef, typename RightRef, bool check=false,
    typename Discr=typename structural_discriminant<typename deref<LeftRef>::type,
                                                    typename deref<RightRef>::type>::type>
    struct mul_impl; 

    Works similar to the unary case; the output of the structural_discriminant is always a cons pair of discriminant values of operands:

    cons<is_scalar, is_scalar>
    means multiplication of two numbers;
    cons<is_vector, is_scalar>
    multiplication of a vector with a scalar from right;
    cons<is_matrix, is_matrix>
    matrix multiplication;
    cons<is_set, is_set>
    set intersection.

    ... and so on

The additional template parameter Discr should be hidden in the auxiliary class implementing the operation (such as neg_impl or mul_impl,) so that the front class (neg or mul) can be used in conjuction with BuildUnary or BuildBinary. The relation between the classes is obvious:

template <typename LeftRef, typename RightRef>
struct mul : public mul_impl<LeftRef,RightRef> { };