Previous: Node record, Up: Solver


7.4 Writing new principles

In this section, we explain how you can write new principles. Principles must be written in Mozart/Oz. In order to write an principle, you need to provide two things:

  1. the principle definition functor
  2. the constraint functors

7.4.1 Principle definition functor

You write the principle definition in the IL, and embed it into a functor. The functor has to export the principle definition as Principle, and has to reside in Solver/Principles.

7.4.1.1 Example (graph principle)

We display an example principle definition functor of the graph principle (Solver/Principles/Graph.oz) below:

     %% Copyright 2001-2008
     %% by Ralph Debusmann <rade@ps.uni-sb.de> (Saarland University) and
     %%    Denys Duchier <duchier@ps.uni-sb.de> (LIFO, Orleans) and
     %%    Jorge Marques Pelizzoni <jpeliz@icmc.usp.br> (ICMC, Sao Paulo) and
     %%    Jochen Setz <info@jochensetz.de> (Saarland University)
     %%
     functor
     export
        Principle
     define
        Principle =
        elem(tag: principledef
     	id: elem(tag: constant
     		 data: 'principle.graph')
     	dimensions: [elem(tag: variable
     			  data: 'D')]
     	model:
     	   elem(tag: 'type.record'
     		args:
     		   [elem(tag: constant
     			 data: 'mothers')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'daughters')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'up')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'down')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'eq')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'equp')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'eqdown')#
     		    elem(tag: 'type.ints')
     		    %%
     		    elem(tag: constant
     			 data: 'labels')#
     		    elem(tag: 'type.set'
     			 arg: elem(tag: 'type.labelref'
     				   arg: elem(tag: variable
     					     data: 'D')))
     		    %%
     		    elem(tag: constant
     			 data: 'mothersL')#
     		    elem(tag: 'type.vec'
     			 arg1: elem(tag: 'type.labelref'
     				    arg: elem(tag: variable
     					      data: 'D'))
     			 arg2: elem(tag: 'type.ints'))
     		    %%
     		    elem(tag: constant
     			 data: 'daughtersL')#
     		    elem(tag: 'type.vec'
     			 arg1: elem(tag: 'type.labelref'
     				    arg: elem(tag: variable
     					      data: 'D'))
     			 arg2: elem(tag: 'type.ints'))
     		    %%
     		    elem(tag: constant
     			 data: 'upL')#
     		    elem(tag: 'type.vec'
     			 arg1: elem(tag: 'type.labelref'
     				    arg: elem(tag: variable
     					      data: 'D'))
     			 arg2: elem(tag: 'type.ints'))
     		    %%
     		    elem(tag: constant
     			 data: 'downL')#
     		    elem(tag: 'type.vec'
     			 arg1: elem(tag: 'type.labelref'
     				    arg: elem(tag: variable
     					      data: 'D'))
     			 arg2: elem(tag: 'type.ints'))
     		   ])
     	constraints: [elem(tag: constant
     			   data: 'GraphMakeNodes')#
     		      elem(tag: integer
     			   data: 130)
     		      %%
     		      elem(tag: constant
     			   data: 'GraphConditions')#
     		      elem(tag: integer
     			   data: 120)
     		      %%
     		      elem(tag: constant
     			   data: 'GraphMakeEdges')#
     		      elem(tag: integer
     			   data: 100)
     		      %%
     		      elem(tag: constant
     			   data: 'GraphDist')#
     		      elem(tag: integer
     			   data: 90)
     		     ]
            )
     end

The value of the id feature is an IL constant representing the unique principle identifier (here: 'principle.graph').

The value of the dimensions feature is a list of IL variables representing the dimension variables introduced by the principle (here: 'D').

The value of the model feature is a list of IL pairs representing the type of the model record introduced by the principle. Here, the graph principle introduces the following features:

The value of the constraints feature is a list of pairs of an IL constant and an IL integer, representing a mapping from constraint functor file names (modulo the suffix .ozf) to their priorities. Here, the constraint functor GraphMakeNodes has priority 130, GraphConditions 120, GraphMakeEdges 100, and GraphDist 90. The XDK solver invokes constraint functors with higher priority first. Changing the priority of the constraint functors can lead to less/more efficient solving.

7.4.1.2 Example (out principle)

The graph principle does not have any arguments. Therefore, we display another example principle definition functor of the out principle (Solver/Principles/Out.oz), since it does have an argument:

     %% Copyright 2001-2008
     %% by Ralph Debusmann <rade@ps.uni-sb.de> (Saarland University) and
     %%    Denys Duchier <duchier@ps.uni-sb.de> (LIFO, Orleans) and
     %%    Jorge Marques Pelizzoni <jpeliz@icmc.usp.br> (ICMC, Sao Paulo) and
     %%    Jochen Setz <info@jochensetz.de> (Saarland University)
     %%
     functor
     export
        Principle
     define
        Principle =
        elem(tag: principledef
     	id: elem(tag: constant
     		 data: 'principle.out')
     	dimensions: [elem(tag: variable
     			  data: 'D')]
     	args: [elem(tag: variable
     		    data: 'Out')#
     	       elem(tag: 'type.valency'
     		    arg: elem(tag: 'type.labelref'
     			      arg: elem(tag: variable
     					data: 'D')))]
     	defaults: [elem(tag: variable
     			data: 'Out')#
     		   elem(tag: featurepath
     			root: '_'
     			dimension: elem(tag: variable
     					data: 'D')
     			aspect: 'entry'
     			fields: [elem(tag: constant
     				      data: 'out')])]
     	constraints: [elem(tag: constant
     			   data: 'Out')#
     		      elem(tag: integer
     			   data: 130)])
     end

Here, the principle identifier is 'principle.out', the dimension variable is D, and the principle makes use of the constraint functor Out with priority 130.

The value of the args feature is a list of IL pairs representing the argument variables introduced by the principle and their types. Here, the out principle introduces the argument Out (a valency of edge labels on dimension D).

The value of the default feature is a list of IL pairs representing the default values of a subset of the argument variables introduced by the principle. Here, the default value of the Out argument is a feature path.

7.4.1.3 Integrate the principle definition

To integrate the principle definition into the XDK, you can either trust the perl-script scripts/addprinciple, or, by hand do the following:

7.4.2 Constraint functors

You write a constraint functor as an Oz functor exporting the procedure Constraint: Nodes G Principle FD FS Select -> U.

Constraint has six arguments:

where Principle is a record of the following type:
     principle(pIDA: PIDA
               modelLat: ModelLat
               dVA2DIDA: DVA2DIDA
               dIDAs: DIDAs
               argRecProc: ArgRecProc
               argsLat: ArgsLat
               dIDA: DIDA
               ...)

where we omit the features modelTn, constraints, edgeconstraints, dVA2DIDARec, argRec and argsTn, which are irrelevant for writing constraint functors.

where the value of pIDA is the principle identifier (e.g. principle.valency.

The value of modelLat is the lattice for the model record.

The value of dVA2DIDA is a function mapping dimension variables to dimension identifiers.

The value of argRecProc is a function of type ArgRecProc: A AXRec -> SL from a principle argument variable (A) and a record (AXRec) to the argument value bound to the argument variable.

The value of argsLat is the lattice for the argument record of the principle.

The value of DIDA is the dimension identifier of the dimension on which the principle is instantiated.

By convention, you should only access the argument values of a principle use through the ArgRecProc function. The record AXRec is a mapping from either Oz atom '_', '^', or 'ql' to either a node record ('_' and '^') or an Oz atom representing an edge label ('ql'). I.e., the record AXRec includes two mappings in one record:

In a constraint functor, you usually posit constraints over all nodes. In order to get a principle argument, you usually call ArgRecProc providing only the mapping of root variable '_' to the current node.

7.4.2.1 Example (binding root variables)

Here is an example of using ArgRecProc to get the value of the Out argument of the out principle, taken from the constraint functor implementing the out principle (Solver/Principles/Lib/Out.oz):

              for Node in Nodes do
                 LAOutMRec = {ArgRecProc 'Out' o('_': Node)}
              in
                 ...
              end

Here, in a for loop, the constraint functor posits a constraint over each node Node (in Nodes). The root variable '_' is bound to Node in the call of the ArgRecProc function to get the value of the argument variable Out for node Node.

7.4.2.2 Check model helper procedure

The functor Solver/Principles/Lib/Helpers.oz exports the procedure CheckModel: V Nodes DIDAATups -> B. By convention, CheckModel should be used to check whether all model record features accessed in the constraint functor are actually present (in order to prevent crashes of the XDK solver).

The CheckModel procedure has three arguments:

The procedure returns true if for a node Node (in Nodes), it is the case that for all pairs DIDA#A in DIDAATups, the model record of the node (Node.DIDA.model) has field A. If the model record lacks a feature, it returns false and the procedure prints out a warning to stderr.

By convention, you should call CheckModel before the constraint functor actually posits constraints, and the constraint functor should not posit any constraints if CheckModel returns false.

Here is an example of a use of the CheckModel procedure in the constraint functor implementing the barriers principle (Solver/Principles/Lib/Barriers.oz):

        proc {Constraint Nodes G Principle}
           DVA2DIDA = Principle.dVA2DIDA
           ArgRecProc = Principle.argRecProc
           %%
           D1DIDA = {DVA2DIDA 'D1'}
           D2DIDA = {DVA2DIDA 'D2'}
        in
           %% check features
           if {Helpers.checkModel 'Barriers.oz' Nodes
               [D2DIDA#down
                D1DIDA#mothers
                D2DIDA#up
                D2DIDA#labels]} then
              D2DownMs = {Map Nodes
                            fun {$ Node} Node.D2DIDA.model.down end}
              %%
              BlocksMs = {Map Nodes
                          fun {$ Node}
                             BlocksM = {ArgRecProc 'Blocks' o('_':Node)}
                          in
                             BlocksM
                          end}
           in
              for Node in Nodes do
                 %% get all nodes below my D1 mother on D2
                 %% down_D2_mothers_D1(v) =
                 %% union { down_D2(v') | v' in mothers_D1(v) }
                 D2DownD1MothersM =
                 {Select.union D2DownMs Node.D1DIDA.model.mothers}
                 %% from this set, keep only those nodes which are above me
                 %% these are then between my D1 mother and myself on D2
                 %% between(v) = down_D2_mothers_D1(v) intersect up_D2(v)
                 BetweenM =
                 {FS.intersect D2DownD1MothersM Node.D2DIDA.model.up}
                 %% get all edge labels which are blocked by the nodes in
                 %% between(v)
                 %% blocked(v) = union { blocks(v') | v' in between(v) }
                 BlockedLM = {Select.union BlocksMs BetweenM}
              in
                 %% my incoming edge labels set must be disjoint from the
                 %% set of blocked labels.
                 %% labels_D2(v) disjoint blocked(v)
                 {FS.disjoint Node.D2DIDA.model.labels BlockedLM}
              end
           end
        end

This constraint functor accesses the model record fields mothers on dimension D1, and down, up and labels on dimension D2. It makes use of the CheckModel procedure to check this.

7.4.2.3 Integrate the constraint functor

To integrate the constraint functor into the XDK, you can again either trust the perl-script scripts/addprinciple, or by hand do the following: