<?xml version="1.0" encoding="utf-8"?>
<iso:schema xmlns="http://purl.oclc.org/dsdl/schematron"
  xmlns:iso="http://purl.oclc.org/dsdl/schematron"
  xmlns:dp="http://www.pnml.org/version-2009/grammar/pnml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" queryBinding="xslt2" schemaVersion="ISO19757-3"
  see="http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html" defaultPhase="#ALL">
  <iso:title>A Schematron mini-schema for PNML Symmetric Nets constraints specification</iso:title>
  <iso:ns prefix="dp" uri="http://www.pnml.org/version-2009/grammar/pnml"/>
  <p> This schema provides Schematron rules you may use to further check the validity of a Symmetric
    Net Document, in addition to the original PNML Schema in RELAX NG. This schema reuses the rules
    designed to check PNML Core Model Documents, since Symmetric Nets are built upon the Core Model.
    
    File name                                  : SNetconstraints.sch 
    Requires                                   : CMConstraints.sch 
    Version                                    : 2010 
    Copyright notice                           : Copyright 2010 Lom Hillah (AFNOR)
    License                                    : GNU GPL v3 (See http://www.gnu.org/licenses/)
    Revisions - (Number/Date/Author/[Comment]) : 1/April 25, 2010/L.H./Inception
                                               : 2/April 27, 2010/L.H./New semantic rules
                                               : 3/June 16, 2010/L.H./Rules for operators arity
  </p>

  <iso:phase id="SNNet" see="http://www.pnml.org/version-2009/grammar/symmetricnet.pntd">
    <p> In this schema we provide a phase for Symmetric nets. Since the Core Model schema is
      included, its phase will be activated as well during validation. If you extends this schema
      with another phase, you may set a default phase for the validation in the @defaultPhase
      attribute of the iso:schema element. </p>

    <iso:active pattern="CheckDeclIds"/>
    <iso:active pattern="CheckArcSourceTargetDifferent"/>
    <iso:active pattern="CheckRefUserSort"/>
    <iso:active pattern="CheckRefUserOperator"/>
    <iso:active pattern="CheckMultisetSortNotInNamedSort"/>
    <iso:active pattern="CheckVariableRefVariableDecl"/>
    <iso:active pattern="CheckCartesianProduct"/>
    <iso:active pattern="CheckFiniteIntRangeConstant"/>
    <iso:active pattern="CheckNaturalPositive"/>
    <iso:active pattern="CheckNetDeclaration"/>
    <iso:active pattern="CheckPlaceType"/>
    <iso:active pattern="CheckInitialMarking"/>
    <iso:active pattern="CheckCondition"/>
    <iso:active pattern="CheckInscription"/>
    <iso:active pattern="CheckPartitionElementOfRef"/>
    <iso:active pattern="CheckPartitionElementRef"/>
    <iso:active pattern="CheckAll"/>
    <iso:active pattern="CheckSuccessorAndPredecessor"/>
    <iso:active pattern="CheckCartesianProduct"/>
    <iso:active pattern="CheckMultisetsOperatorsArity"/>
    <iso:active pattern="CheckBooleansOperatorsArity"/>
    <iso:active pattern="CheckFiniteIntRangesOperatorsArity"/>
    <iso:active pattern="CheckPartitionsOperatorsArity"/>
    <iso:active pattern="CheckIntegersOperatorsArity"/>
  </iso:phase>

  <xsl:key name="declId"
    match="dp:namedsort|dp:namedoperator|dp:variabledecl|dp:partition|dp:partitionelement|dp:feconstant"
    use="@id"/>

  <!-- Patterns for @id attributes -->
  <iso:pattern is-a="isAttr" id="CheckDeclIds">
    <title>Checks declarations' id attribute existence</title>
    <iso:param name="elem"
      value="dp:namedsort|dp:namedoperator|dp:variabledecl|dp:partition|
      dp:partitionelement|dp:feconstant"/>
    <iso:param name="att" value="@id"/>
  </iso:pattern>

  <!-- Structural constraint: check no arcs between nodes of the same kind-->
  <iso:pattern id="CheckArcSourceTargetDifferent">
    <iso:title>Checks that no arc connects two nodes of the same kind</iso:title>
    <iso:rule context="dp:arc[@source and @target]">
      <let name="placeSource"
        value="key('nodeId', current()/@source)/(self::dp:place|self::dp:referencePlace)[@id = current()/@source]"/>
      <let name="transitionTarget"
        value="key('nodeId', current()/@target)/(self::dp:transition|self::dp:referenceTransition)[@id = current()/@target]"/>
      <let name="transitionSource"
        value="key('nodeId', current()/@source)/(self::dp:transition|self::dp:referenceTransition)[@id = current()/@source]"/>
      <let name="placeTarget"
        value="key('nodeId', current()/@target)/(self::dp:place|self::dp:referencePlace)[@id = current()/@target]"/>
      <iso:assert
        test="(count($placeSource) = 1 and count($transitionTarget) = 1) or 
        (count($transitionSource) = 1 and count($placeTarget) = 1)"
        diagnostics="differentNodeKinds">
        The arc '<value-of select="@id"/>' must connect two different kinds of nodes.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <!-- High-level nets constraints-->
  <iso:pattern id="CheckRefUserSort">
    <iso:title>Checks every user sort actually references an existing named sort. Normally, even dot and
      bool should be explicitly declared, to preserve the completeness and consistency of ID/IDREF
      pairs between usersort and namedsort.
    </iso:title>
    <iso:rule context="dp:usersort[@declaration]">
      <iso:assert
        test="key('declId', current()/@declaration)/(self::dp:namedsort|self::dp:partition)[@id = current()/@declaration]">
        The referenced sort from <name/> whose @declaration is '<value-of select="current()/@declaration"/>' 
        must be an actual named sort or a partition.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckRefUserOperator">
    <iso:title>Checks every user operator actually references an existing named 
      operator or feconstant.
    </iso:title>
    <iso:rule context="dp:useroperator[@declaration]">
      <iso:assert
        test="key('declId', current()/@declaration)/
        (self::dp:namedoperator|self::dp:feconstant|self::dp:partitionelement)[@id = current()/@declaration]">
        The referenced operator from <name/> whose @declaration is '<value-of select="current()/@declaration"/>' must be
        an actual named operator (namedoperator), a finite enumeration constant (feconsant) or
        a partition element (partitionelement).
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckMultisetSortNotInNamedSort">
    <iso:title>Checks named sorts do not contain a multiset sort declaration</iso:title>
    <iso:rule context="dp:namedsort">
      <let name="multisetsortChildren" value="child::dp:multisetsort"/>
      <iso:assert test="count($multisetsortChildren) = 0"> The <name/> '<value-of select="@id"/>'
        should not contain a multiset sort declaration. </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckVariableRefVariableDecl">
    <iso:title>Checks variables used in expressions actually reference declared variables</iso:title>
    <iso:rule context="dp:variable[@refvariable]">
      <iso:assert
        test="key('declId', current()/@refvariable)/(self::dp:variabledecl)[@id = current()/@refvariable]">
        Attribute @refvariable '<value-of select="@refvariable"/>' of element <name/> should
        reference an actual declared variable. </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckCartesianProduct">
    <iso:title>Checks every cartesian product involves at least two sorts</iso:title>
    <iso:rule context="dp:productsort">
      <iso:assert test="count(child::*) >= 2">
        Product sort named '<value-of select="parent::dp:namedsort/@id"/>' must involve at least two sorts. 
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckFiniteIntRangeConstant">
    <iso:title>Checks finite int range constants belong to their range and that 
      their arity is zero.
    </iso:title>
    <iso:rule context="dp:finiteintrangeconstant">
      <iso:assert
        test="number(current()/@value) >= number(child::dp:finiteintrange/@start) and
        number(current()/@value) &lt;= number(child::dp:finiteintrange/@end)"> 
        Finite int range constant whose value is '<value-of select="@value"/>', must belong to the
        declared range '<value-of select="dp:finiteintrange/@start"/>'..'<value-of
          select="dp:finiteintrange/@end"/>' 
      </iso:assert>
      <iso:assert test="count(dp:subterm) = 0">
        As an operator, the arity of the finite int range constant whose value is '<value-of select="@value"/>'
        must be equal to 0.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckNaturalPositive">
    <iso:title>Checks naturals, and positives belong to their range. 
      Numeric values in XPath are integer, decimal and double... 
    </iso:title>
    <iso:rule context="dp:numberconstant">
      <iso:assert test="(child::dp:natural and number(current()/@value) >= 0) or
        (child::dp:positive and number(current()/@value) > 0)">
        Number constant whose value is '<value-of select="@value"/>' must belong to the declared
        range (<value-of select="name(child::dp:natural|child::dp:positive)"/>)
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <!-- Symmetric net specific constraints -->
  <iso:pattern id="CheckNetDeclaration">
    <iso:title>Checks every Symmetric net has at least a declarative part;
    there should not be sort declarations using infinite types such as natural, positive and integer.
    </iso:title>
    <iso:rule context="dp:net">
      <iso:assert test="dp:declaration/dp:structure/*" diagnostics="snDeclaration"> 
        Symmetric Net '<value-of select="@id"/>' must have a declarative part
        (element structure required and non-empty).
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:net//dp:declarations/dp:namedsort">
      <iso:assert test="not(dp:natural|dp:integer|dp:positive)">
         Infinite types such as Natural, Positive and Integer must not be used in 
         sort declarations in Symmetric nets (in net '<value-of select="ancestor::dp:net/@id"/>').
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckPlaceType">
    <iso:title>Checks every place is typed</iso:title>
    <iso:rule context="dp:place">
      <iso:assert test="dp:type/dp:structure/*" diagnostics="placeType"> 
        Place '<value-of select="@id"/>' must be typed (or have a domain) by a sort.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckInitialMarking">
    <iso:title>Checks every specified initial marking is non-empty</iso:title>
    <iso:rule context="dp:hlinitialMarking">
      <iso:assert test="dp:structure/*"> 
        Initial marking of place '<value-of select="parent::dp:place/@id"/>' must be structured by
        a term (element structure required and non-empty).
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckCondition">
    <iso:title>Checks every specified condition is non-empty; variables in a condition are of the same
      named sort; any variable referenced in a condition must appear at least once either in the input or 
      output arcs of the transition ; any value compared to a variable is valid in the sort of the variable.
    </iso:title>
    <iso:rule context="dp:condition">
      <iso:assert test="dp:structure/*">
        Condition of transition '<value-of select="parent::dp:transition/@id"/>' must be 
        structured by a term (element structure required and non-empty).
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:condition/dp:structure">
      <let name="variableDecl" value="key('declId', descendant::dp:variable/@refvariable)"/>
      <let name="namedSort" value="key('declId', ($variableDecl)/dp:usersort/@declaration)"/>
      <let name="connectedArcs" value="//dp:arc[@source = current()/ancestor::dp:transition/@id]|
        //dp:arc[@target = current()/ancestor::dp:transition/@id]"/>
      <let name="arcVariableDecl" value="key('declId', ($connectedArcs)/dp:hlinscription//dp:variable/@refvariable)"/>
      <iso:assert test="count ($namedSort) = 1">
        Variables in the condition of transition '<value-of select="ancestor::dp:transition/@id"/>' 
        must be of the same (and declared) named sort.
      </iso:assert>
      <iso:assert test="count($variableDecl intersect $arcVariableDecl) != 0">
        In the condition of transition '<value-of select="ancestor::dp:transition/@id"/>', any variable referenced in
        the condition must appear at least once either in the input or output arcs of the transition.
      </iso:assert>
    </iso:rule>
    <iso:rule
      context="dp:condition//dp:booleanconstant|dp:condition//dp:dotconstant|
      dp:condition//dp:numberconstant|dp:condition//dp:finiteintrangeconstant">
      <let name="variableDecl"
        value="key('declId', (parent::dp:subterm/
        (following-sibling::dp:subterm|preceding-sibling::dp:subterm)/dp:variable/@refvariable))"/>
      <let name="namedSort" value="key('declId', ($variableDecl)/dp:usersort/@declaration)"/>
      <iso:assert
        test="(self::dp:booleanconstant and ($namedSort)/dp:bool) or 
        (self::dp:dotconstant and ($namedSort)/dp:dot) or
        (self::dp:finiteintrangeconstant/dp:finiteintrange/@start = ($namedSort)/dp:finiteintrange/@start and 
        self::dp:finiteintrangeconstant/dp:finiteintrange/@end = ($namedSort)/dp:finiteintrange/@end) or 
        (self::dp:numberconstant/dp:natural and ($namedSort)/dp:natural) or 
        (self::dp:numberconstant/dp:positive and ($namedSort)/dp:positive) or 
        (self::dp:numberconstant/dp:integer and ($namedSort)/dp:integer)"> 
        In the condition of transition '<value-of select="ancestor::dp:transition/@id"/>', any value compared to
        a variable must be valid in the referenced sort of the variable.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckInscription">
    <iso:title>Checks every specified inscription is non-empty;
      any value in a valuation must be valid in the sort of the place connected to the arc (input or output);
      any variable in a valuation must be valid in the sort or domain (one sort of the cartesian product)
      of the place connected to the arc;
      Note : the last two rules do not take reference places into account!
    </iso:title>
    <iso:rule context="dp:hlinscription">
      <iso:assert test="dp:structure/*">
        Inscription of arc '<value-of select="parent::dp:arc/@id"/>' must be 
        structured by a term (element structure required and non-empty).
      </iso:assert>
    </iso:rule>
    <iso:rule
      context="dp:hlinscription//dp:booleanconstant|dp:hlinscription//dp:dotconstant|
      dp:hlinscription//dp:finiteintrangeconstant|dp:hlinscription//dp:useroperator">
      <let name="placeSource" value="key('nodeId', ancestor::dp:arc/@source)/(self::dp:place)"/>
      <let name="placeTarget" value="key('nodeId', ancestor::dp:arc/@target)/(self::dp:place)"/>
      <let name="namedSort" value="key('declId', ($placeSource|$placeTarget)/dp:type//dp:usersort/@declaration)"/>
      <iso:assert test="(self::dp:booleanconstant and ($namedSort)/dp:bool) or 
        (self::dp:dotconstant and ($namedSort)/dp:dot) or 
        (self::dp:finiteintrangeconstant/dp:finiteintrange/@start = ($namedSort)/dp:finiteintrange/@start and 
        self::dp:finiteintrangeconstant/dp:finiteintrange/@end = ($namedSort)/dp:finiteintrange/@end) or 
        (count(($namedSort)//dp:feconstant[@id = current()/@declaration]) = 1) or 
        (count(($namedSort)//dp:partitionelement[@id = current()/@declaration]) = 1)">
       In the inscription of arc '<value-of select="ancestor::dp:arc/@id"/>', any value must be valid in
        the sort of the place connected to this arc (input or output).
        Note: this rule does not catch reference places.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckPartitionElementOfRef">
    <iso:title>Checks every partitionelementof references an existing partition</iso:title>
    <iso:rule context="dp:partitionelementof">
      <iso:assert test="key('declId', current()/@refpartition)/self::dp:partition[@id = current()/@refpartition]">
        Referenced partition '<value-of select="@refpartition"/>' from 'partitionelementof' must be
        an actual declared partition.
      </iso:assert>
      <iso:assert test="count(dp:subterm) = 1">
        The arity of the '<name/>' operator must be equal to 1.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckPartitionElementRef">
    <iso:title>Checks the sort of every contained partitionelementconstant is the
      sort referenced by the containing partition.
    </iso:title>
    <iso:rule context="dp:partitionelement">
      <let name="useroperators" value="dp:useroperator"/>
      <iso:assert test="key('declId', ($useroperators)/@declaration)/ancestor::dp:namedsort[@id = 
        current()/(following-sibling::dp:usersort|preceding-sibling::dp:usersort)/@declaration]">
        The referenced sort by every contained useroperator of partitionelement '<value-of select="@id"/>'
        must be the same as the sort referenced by the usersort sibling of this partitionelement.
      </iso:assert>
    </iso:rule>
  </iso:pattern>

  <iso:pattern id="CheckAll">
    <iso:title>Checks the multiset all operator arity is zero; it is applied only on
      single sort declarations, not product sorts; its sort is the same as the sort of
      the place or arc (or place connected to that arc) it appears on.
    </iso:title>
    <iso:rule context="dp:all">
      <let name="placeSource" value="key('nodeId', ancestor::dp:arc/@source)"/>
      <let name="placeTarget" value="key('nodeId', ancestor::dp:arc/@target)"/>
      <let name="placeContainer" value="ancestor::dp:place"/>
      <let name="namedSort" value="key('declId', ($placeSource|$placeTarget)/dp:type//dp:usersort/@declaration)"/>
      <iso:assert test="count(dp:subterm) = 0">
        The arity of the '<name/>' operator in the subtree of element '<value-of select="(ancestor::dp:place|ancestor::dp:arc|ancestor::dp:transition)/@id"/>'
        must be equal to zero.
      </iso:assert>
      <iso:assert test="count(key('declId', dp:usersort/@declaration)/(self::dp:namedsort/dp:productsort))=0">
        The '<name/>' operator in the subtree of element '<value-of select="(ancestor::dp:place|ancestor::dp:arc|ancestor::dp:transition)/@id"/>'
        must be applied only on single sort declarations, not product sorts.
      </iso:assert>
      <iso:assert test="
        ($placeSource|$placeTarget|$placeContainer)/self::dp:place/dp:type//dp:usersort/@declaration = current()/dp:usersort/@declaration">
        The '<name/>' operator in the subtree of element '<value-of select="(ancestor::dp:place|ancestor::dp:arc)/@id"/>' must be
        applied on the same sort as the sort (of the place) associated to that element.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckSuccessorAndPredecessor">
    <iso:title>Checks the successor and predecessor operators are not used in initial markings,
      their arity is 1 and they are applied on variables whose declarations reference single sorts,
      not product sort.
      Note : we may have successor(successor($var)) or any arbitrary-long imbricated sequence. 
    </iso:title>
    <iso:rule context="dp:successor|dp:predecessor">
      <let name="variableDecl" 
        value="key('declId', dp:variable/@refvariable)/(self::dp:variabledecl)"/>
      <let name="namedSort" value="key('declId', ($variableDecl)/dp:usersort/@declaration)"/>
      <iso:assert test="not(ancestor::dp:hlinitialMarking)">
        The '<name/>' operator must not be used in the initial marking of place '<value-of select="(ancestor::dp:place)/@id"/>'.
      </iso:assert>
      <iso:assert test="count(dp:subterm) = 1">
        The arity of the '<name/>' operator in the subtree of element '<value-of select="(ancestor::dp:place|ancestor::dp:arc|ancestor::dp:transition)/@id"/>'
        must be equal to 1.
      </iso:assert>
      <iso:assert test="count($namedSort/dp:productsort) = 0">
        The '<name/>' operator in the subtree of element '<value-of select="(ancestor::dp:place|ancestor::dp:arc|ancestor::dp:transition)/@id"/>'
        must be applied only on variables whose declarations reference single sort declarations, not product sorts.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <!-- Check the arity of most operators as specified by OCL constraints in the standard -->
  <iso:pattern id="CheckMultisetsOperatorsArity">
    <iso:title>Checks the arity of multisets operators, except all which is already
    checked above.
    </iso:title>
    <iso:rule context="dp:cardinalityof|dp:contains|dp:scalarproduct|dp:numberof|dp:subtract">
      <iso:assert test="count(dp:subterm) = 2">
        The arity of the multiset '<name/>' operator must be equal to 2.
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:add">
      <iso:assert test="count(dp:subterm) >= 2">
        The arity of the multiset '<name/>' operator must be greater or equal to 2.
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:cardinality">
      <iso:assert test="count(dp:subterm) = 1">
        The arity of the multiset '<name/>' operator must be equal to 1.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckBooleansOperatorsArity">
    <iso:title>Checks the arity of booleans operators.</iso:title>
    <iso:rule context="dp:equality|dp:inequality|dp:or|dp:and|dp:imply">
      <iso:assert test="count(dp:subterm) = 2">
        The arity of the boolean '<name/>' operator must be equal to 2.
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:not">
      <iso:assert test="count(dp:subterm) = 1">
        The arity of the booolean '<name/>' operator must be equal to 1.
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:booleanconstant">
      <iso:assert test="count(dp:subterm) = 0">
        The arity of the boolean '<name/>' operator must be equal to 0. 
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckFiniteIntRangesOperatorsArity">
    <iso:title>Checks the arity of finite int ranges operators, 
    except finiteintrangeconstant which is already checked above.
    </iso:title>
    <iso:rule context="dp:lessthan|dp:lessthanorequal|dp:greaterthan|dp:greaterthanorequal">
      <iso:assert test="count(dp:subterm) = 2">
        The arity of the finite int range '<name/>' operator must be equal to 2.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckPartitionsOperatorsArity">
    <iso:title>Checks the arity of partitions operators,
    except partitionelementof already checked above.
    </iso:title>
    <iso:rule context="dp:ltp|dp:gtp">
      <iso:assert test="count(dp:subterm) = 2">
        The arity of the partition '<name/>' operator must be equal to 2.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern id="CheckIntegersOperatorsArity">
    <iso:title>Integers are indirectly used in Symmetric nets.
      Therefore, we need to also check their operators
    </iso:title>
    <iso:rule context="dp:lt|dp:leq|dp:gt|dp:geq|dp:addition|dp:subtraction|dp:mult|dp:div|dp:mod">
      <iso:assert test="count(dp:subterm) = 2">
        The arity of the integer '<name/>' operator must be equal to 2.
      </iso:assert>
    </iso:rule>
    <iso:rule context="dp:numberconstant">
      <iso:assert test="count(dp:subterm) = 0">
        The arity of the '<name/>' operator must be equal to 0.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <!--
    Include all the children of the referenced schematron mini-schema for Core Model.
    The referenced schema must be located in the same folder as this one.
    See http://www.eccnet.com/pipermail/schematron/2009-April/000178.html
  -->
  <iso:extends href="CMconstraints.sch"/>


  <iso:diagnostics>
    <iso:diagnostic id="differentNodeKinds"> In a Symmetric net, an arc may connect a place to a
      transition or vice versa. It may also connect a place to a referenceTransition (or vice versa)
      or a transition to a referencePlace (or vice versa). It may also connect a referencePlace to a
      referenceTransition (or vice versa). </iso:diagnostic>
    <iso:diagnostic id="placeType"> In a Symmetric net, a place must be typed, i.e., have a domain.
      That domain must refer to an existing built-in sort (e.g., dot, bool) or a named sort,
      declared by the user in the declarative part of the net. The corresponding 'type' element,
      child of 'place' element, must have a non-empty 'structure' element. </iso:diagnostic>
    <iso:diagnostic id="snDeclaration" see="http://www.pnml.org/version-2009/grammar/pt-hlpng.pntd">
      A Symmetric net must have a declarative part. In the case your net doesn't, you must therefore
      declare it as a P/T net in high-level notation, where only built-in sorts Dot and Bool are
      used, and not required to be declared beforehand. In that case, you may just use elements
      'numberconstant', 'dotconstant' and 'booleanconstant' in the terms of the places' initial
      marking and arcs' inscriptions. </iso:diagnostic>
  </iso:diagnostics>

</iso:schema>

