GraphDirectedAcyclic compiles
authorThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Tue, 22 Oct 2024 07:57:04 +0000 (07:57 +0000)
committerThomas Walker Lynch <xtujpz@reasoningtechnology.com>
Tue, 22 Oct 2024 07:57:04 +0000 (07:57 +0000)
developer/javac/GraphDirectedAcyclic.java
developer/javac/Label.java
developer/javac/LabelList.java
developer/javac/Node.java

index 3528bd8..51950d8 100644 (file)
@@ -81,24 +81,14 @@ public class GraphDirectedAcyclic extends Graph{
     constructors
   */
 
-  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list ,boolean verbose){
+  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,LabelList root_node_list ,int max_depth ,boolean verbose){
     super( node_map ,recognizer_f_list );
-
-    // Step 2: Run the cycle detection logic and mark up the graph.
-    TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,verbose );
-
-    // You can further process cycle detection results here if needed
-
+    TokenSet cycle_detection_result = graph_mark_cycles(root_node_list ,max_depth ,verbose);
   }
 
-
-  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,List<Label> root_node_list){
+  public GraphDirectedAcyclic(Map<Label ,Node> node_map ,ProductionList recognizer_f_list ,LabelList root_node_list){
     super( node_map ,recognizer_f_list );
-
-    // Step 2: Run the cycle detection logic and mark up the graph.
-    TokenSet cycle_detection_result = mark_cycle_graph( root_node_list ,this.debug );
-
-    // You can further process cycle detection results here if needed
+    TokenSet cycle_detection_result = graph_mark_cycles(root_node_list);
   }
 
 
@@ -181,22 +171,19 @@ public class GraphDirectedAcyclic extends Graph{
        item on the cycle stack would be used as root nodes for a new cycle
        search. Note the leftmost cycle on each recursive search on the leftmost
        node, will be the original cycle.
-
-
   */
 
   /*
-    Cycle detection is based on label compares. Lookup is done to mark the node.
-
-    1. Given a path.
+    Given a path to a node in the graph, `left_path`.
 
-    2. Checks if the rightmost item, k_n, in the path occurs earlier in the path, say at k_i.
-       If so, marks the nodes involved in the cycle.
-
-    3. if there is no cycle, returns null, otherwise returns the Interval [i ,n]
+    Checks if the rightmost node (referenced by label) recurs earlier in the path.
+    Presumably the rightmost node has been recently appended to the path.
 
+    If there is no cycle, returns null, otherwise returns the interval [i ,n],
+    of indexes into the path where the cycle is found. `n` will always be
+    the index of the rightmost node in the path list.
   */
-  private List<Integer>  path_find_cycle(List<LabelList> path_stack ,LabelList path ){
+  private List<Integer>  path_find_cycle(LabelList path ){
     if( path.size() <= 1 ) return null; 
 
     int rightmost_index = path.size() - 1;
@@ -214,13 +201,20 @@ public class GraphDirectedAcyclic extends Graph{
   }
 
   /*
-    When descending down the tree, if we find a cycle, we reset the iterator
-    to the top of the cycle. The caller will then return to its caller as though
-    having found a leaf node.
+    Given a path_stack, which is our graph iterator.  Also given `left_path`
+    which is derived from path_stack, and represents the left most neighbor
+    of each of the neighbor lists in the path_stack.
+
+    Calls `path_find_cycle` to see if the most recently added to the path node,
+    i.e. the rightmost node, forms a cycle with other node(s) in the path.  Upon
+    finding a cycle, unwinds the path_stack (decrements the iterator), to the top
+    of the cycle.
+
+    If a cycle is found, returns false, otherwise returns true.
   */
-  private boolean graph_descend_cycle_case( List<Label> left_path ,List<LabelList> path_stack ,boolean verbose ){
+  private boolean graph_descend_cycle_case( LabelList left_path ,List<LabelList> path_stack ,boolean verbose ){
 
-    List<Integer> cycle_index_interval = path_find_cycle( left_path ,verbose );
+    List<Integer> cycle_index_interval = path_find_cycle(left_path);
     if( cycle_index_interval == null ){
       return false; // No cycle found
     }
@@ -230,7 +224,7 @@ public class GraphDirectedAcyclic extends Graph{
 
     if(verbose) Util.print_list(
       "Found cycle:" 
-      ,left_path.subList( cycle_i0 ,n +1)
+      ,left_path.subList( cycle_i0 ,cycle_n + 1)
     );
 
     // Mark cycle members
@@ -238,11 +232,8 @@ public class GraphDirectedAcyclic extends Graph{
     for( int i = cycle_i0; i <= cycle_n; i++ ){
       Label node_label = left_path.get(i);
       Node node = super.lookup( node_label );
-      if( node != null ){
-        if( node.get("mark") == null ){
-          node.put( "mark" ,new HashTokenSet() );
-        }
-        ( (TokenSet)node.get("mark") ).add( "cycle_member" );
+      if (node != null){
+        node.mark(new Token("cycle_member"));
       }else{
         undefined_node_list.add( node_label );
       }
@@ -262,30 +253,29 @@ public class GraphDirectedAcyclic extends Graph{
   /*
     Given a path_stack initialized to the root nodes for the graph traversal.
 
-    Perform depth first descent from each node in succession. Algorithm
-    works from left to right in the child lists.
-
-    Searches for cycles in the descent paths.
+    Perform depth first descent from each node in succession while searching for
+    cycles by calling graph_descent_cycle_case. Algorithm follows the leftmost
+    nodes in the path_stack list of child lists.
 
+    Returns the descent termination condition.
   */
-  private static TokenSet graph_descend_set = new TokenSet();
-  static {
-    graph_descend_set.add( new Token("empty_path_stack") );
-    graph_descend_set.add( new Token("cycle_found") );
-    graph_descend_set.add( new Token("undefined_node") );
-    graph_descend_set.add( new Token("leaf") );
-    graph_descend_set.add( new Token("max_depth_exceeded") );
-  }
+  private static TokenSet graph_descend_set = new TokenSet() {{
+    add(new Token("empty_path_stack"));
+    add(new Token("cycle_found"));
+    add(new Token("undefined_node"));
+    add(new Token("leaf"));
+    add(new Token("max_depth_reached"));
+  }};
   private TokenSet graph_descend( List<LabelList> path_stack ,int max_depth ,boolean verbose ){
-    TokenSet ret_value = new HashTokenSet();
+    TokenSet ret_value = new TokenSet();
 
     if( path_stack.isEmpty() ){
-      ret_value.add( "empty_path_stack" );
+      ret_value.add( new Token("empty_path_stack") );
       return ret_value;
     }
 
     // a leftmost path through the neighbor lists of the leftmost nodes
-    LabelList left_path = new ArrayList<>();
+    LabelList left_path = new LabelList();
     for( LabelList neighbor_list : path_stack ){
       left_path.add( neighbor_list.get(0) );
     }
@@ -295,170 +285,138 @@ public class GraphDirectedAcyclic extends Graph{
 
       // cycle case
       if( graph_descend_cycle_case( left_path ,path_stack ,verbose ) ){
-        ret_value.add( "cycle_found" );
+        ret_value.add( new Token("cycle_found") );
         return ret_value;
       }
 
       // Non-cycle case, descend further into the tree.
       // Increment the graph iterator (path_stack) to go down a level.
-      Label it_pt_label = path_stack.get( path_stack.size() - 1 ).get(0);
-      Node it_pt_node = super.lookup( it_pt_label );
-      if( it_pt_node == null ){
-        ret_value.add( "undefined_node" );
+      Label it_node_label = path_stack.get( path_stack.size() - 1 ).get(0);
+      Node it_node = super.lookup( it_node_label );
+      if( it_node == null ){
+        ret_value.add( new Token("undefined_node") );
         return ret_value;
       }
         
-      NodeList neighbor_list = it_pt_node.get("neighbor");
-      if( neighbor_list == null || neighbor_list.isEmpty() ){
-        ret_value.add( "leaf" );
+      LabelList neighbor_list = it_node.neighbor_LabelList();
+      if( neighbor_list.isEmpty() ){
+        ret_value.add( new Token("leaf") );
         return ret_value;
       }
 
-      path_stack.add( new ArrayList<>((LabelList) neighbor_list) );
+      // The iterator will destroy the neighbor_list as we traverse the graph,
+      // so we give it a copy.
+      path_stack.add( new LabelList(neighbor_list) );
       Label it_next_label = neighbor_list.get(0);
-      left_path.add( it_next_label );  // Properly add the label to the left_path
+      left_path.add( it_next_label ); // also extend the left_path
 
       // bound the size of problem we are willing to work on
       // set max_depth <= 0 to have this test ignored
       if( max_depth > 0 ){
         max_depth--;  // Typo fixed
         if( max_depth == 0 ){
-          ret_value.add( "max_depth_exceeded" );
+          if(verbose){
+            Util.print_list("GraphDirectedAcyclic.GraphDescend:: max_depth reached, preternaturally ending the descent:" ,path_stack);
+          }
+          ret_value.add( new Token("max_depth_reached") );
           return ret_value;
         }
       }
 
-    }while( true ); // while descend
+    }while(true); // while descend
   }
 
    
   /*
-    Given root_node_label_list, marks up the graph and returns a set possibly
-    containing 'all_wellformed' and 'cycles_exist'.
+    Given root_node_label_list and a maximum depth for traversal.
 
-    Marks potentially added to each node include 'cycle_member' and 'wellformed'.
-    Note that these marks are independent.
-  */
-  // Saigens updates:
-  /*
-  Given root_node_label_list, marks up the graph and returns a set possibly
-  containing 'all_wellformed' and 'cycles_exist'.
+    Cycles are handled gracefully, rather the constraint `max_depth` is present
+    because the user is allowed to provide production *functions* for generating
+    nodes. Who knows what crazy graphs a user could come up with, see the
+    document on the algorithm for more info.
+  
+    Does a left first depth first traversal of the graph while marking
+    cycles. This routine pushes the graph traversal iterator to the right one
+    node after a left descent traversal, then it calls `graph_descend` descend
+    from said node.
 
-  Marks potentially added to each node include 'cycle_member' and 'wellformed'.
-  Note that these marks are independent.
-*/
-  /*
+    Returns one or more symbols that characterize the termination condition.
+  */
 
-public TokenSet mark_cycle_graph( LabelList root_node_label_list ,boolean verbose ){
-    TokenSet ret_value = new HashTokenSet();
+  public static TokenSet graph_mark_cycles_set = new TokenSet() {{
+    add(new Token("empty_root_label_list"));
+    add(new Token("cycle_exists"));
+    add(new Token("undefined_node_exists"));
+    add(new Token("bad_descent_termination"));
+    add(new Token("max_depth_reached"));
+  }};
+  public TokenSet graph_mark_cycles( LabelList root_node_LabelList ,int max_depth ,boolean verbose ){
+    TokenSet ret_value = new TokenSet();
     boolean exists_malformed = false;
     TokenSet result; // used variously
 
-    if( root_node_label_list.isEmpty() ) return ret_value;
-
-    // Initialize the DFS tree iterator.
-    List<LabelList> path_stack = new ArrayList<>();
-    path_stack.add( new ArrayList<>(root_node_label_list) );
-
-    // Iterate over left side tree descent, avoids complexity in cycle detection logic.
-    do{
-        result = graph_descend( path_stack ,verbose );
-        if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
-        if( result.contains("undefined_node") ) exists_malformed = true;  // Corrected flag
-
-        // Increment the iterator to the next leftmost path
-        LabelList top_list = path_stack.get( path_stack.size() - 1 );
-        top_list.remove(0);
-        if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
-
-    }while( !path_stack.isEmpty() );
-
-    if( !exists_malformed ) ret_value.add("all_wellformed");
-
-    if( verbose ){
-        if( exists_malformed ) System.out.println("One or more malformed nodes were found.");
-        boolean exists_cycle = ret_value.contains("cycle_exists");
-        if( exists_cycle ) System.out.println("One or more cyclic dependency loops found.");
-        if( exists_malformed || exists_cycle ) System.out.println("Will attempt to build unaffected nodes.");
+    if( root_node_LabelList.isEmpty() ){
+      ret_value.add(new Token("empty_root_label_list"));
+      return ret_value;
     }
 
-    return ret_value;
-}
-
-public TokenSet mark_cycle_graph( LabelList root_node_label_list ){
-    return mark_cycle_graph( root_node_label_list ,true );
-}
-//------------------
-
-  public TokenSet mark_cycle_graph(LabelList root_node_label_list ,boolean verbose){
-    TokenSet ret_value = new HashSet<>();
-    boolean exists_malformed = false;
-    TokenSet result; // used variously
-
-    if( root_node_label_list.isEmpty() ) return ret_value;
-
-    // Initialize the DFS tree iterator.
+    // `path_stack` is our graph iterator. It is initialized with the
+    // `root_node_LabelList`.
     List<LabelList> path_stack = new ArrayList<>();
-    path_stack.add( new ArrayList<>(root_node_label_list) );
+    path_stack.add( new LabelList(root_node_LabelList) );
 
-    // iterate over left side tree descent, not ideal as it starts at the
-    // root each time, but avoids complexity in the cycle detection logic.
+    // each call to graph_descend does a leftmost descent to a leaf
     do{
-      result = graph_descend(path_stack ,verbose);
-      if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
-      if( result.contains("undefined_node") ) exists_undefined = true;
+      result = graph_descend( path_stack ,max_depth ,verbose );
+      if( result.contains(new Token("cycle_found")) ) ret_value.add(new Token("cycle_exists"));
+      if( result.contains(new Token("undefined_node")) ) ret_value.add(new Token("undefined_node_exists"));
+      if( result.contains(new Token("max_depth_reached")) ) ret_value.add(new Token("max_depth_reached"));
+      if( !result.contains(new Token("leaf")) && !result.contains(new Token("cycle_found")) ) ret_value.add(new Token("bad_descent_termination"));
 
-      // increment the iterator to the next leftmost path
+      // Push the iterator to the right by one node
       LabelList top_list = path_stack.get( path_stack.size() - 1 );
       top_list.remove(0);
       if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
 
     }while( !path_stack.isEmpty() );
 
-    if( !exists_malformed ) ret_value.add("all_wellformed");
-
-    if( verbose ){
-      if( exists_malformed ) System.out.println("one or more malformed nodes were found");
-      boolean exists_cycle = ret_value.contains("cycle_exists");
-      if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
-      if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
+    if(verbose){
+      if( ret_value.contains("bad_descent_termination") ){
+        System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: graph_descend terminated with other than leaf or cycle found condition.");
+      }
+      if( ret_value.contains("cycle_exists") ){
+        System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: There are one or more cycles in the graph.");
+      }
+      if( ret_value.contains("undefined_node_exists") ){
+        System.out.println("GraphDirectedAcyclic.graph_mark_cycles:: There are one or more node label references that do not correspond to a defined node in this graph.");
+      }
     }
 
     return ret_value;
   }
-  public TokenSet mark_cycle_graph(LabelList root_node_label_list){
-    return mark_cycle_graph(root_node_label_list ,true);
+   public TokenSet graph_mark_cycles(LabelList root_node_LabelList){
+    return graph_mark_cycles(root_node_LabelList ,this.debug?40:-1 ,this.debug);
   }
-  */
+  
+
   /*--------------------------------------------------------------------------------
     Graph traversal
   */
 
   @Override
-  public Node lookup(Label node_label, boolean verbose) {
-    Node node = super.lookup( node_label ,verbose );
-
-    // Ensure the node exists and the mark property exists
-    if( node != null ){
-      TokenSet mark = (TokenSet)node.get("mark");
-
-      // Check if the mark property exists and contains "cycle_member"
-      if( mark != null && mark.contains("cycle_member") ){
-        if( verbose ){
-          System.out.println("Node is part of a cycle and will not be returned: " + node_label);
-        }
-        return null;  // Exclude nodes in cycles
+  public Node lookup(Label node_label, boolean verbose){
+    Node node = super.lookup(node_label, verbose);
+    if(node != null && node.has_mark(new Token("cycle_member"))){
+      if(verbose){
+        System.out.println("GraphDirectedAcyclic.lookup:: Node is part of a cycle so it will not be returned: " + node_label);
       }
+      return null;  // Exclude nodes in cycles
     }
-
     return node;
   }
 
-  // Overloaded lookup method with default verbosity (true)
   public Node lookup(Label node_label){
     return lookup(node_label ,this.debug);
   }
 
-
-
 }
index f65012c..3d13f45 100644 (file)
@@ -1,7 +1,7 @@
 package com.ReasoningTechnology.Ariadne;
 
 /*
-  A node label.
+  A value for the node.label property.
 
   This is a wrapper for a String. We can't instead use an alias by extending
   String, because String is a JavaScript 'final' type.
index 9b048d7..85acde8 100644 (file)
@@ -1,5 +1,6 @@
 // LabelList.java
 package com.ReasoningTechnology.Ariadne;
+import java.util.List; 
 import java.util.ArrayList;
 
 public class LabelList extends ArrayList<Label> {
@@ -7,4 +8,11 @@ public class LabelList extends ArrayList<Label> {
   public LabelList(){
     super();
   }
+  public LabelList(List<Label> labels){
+    super();  // Initialize the parent class
+    if (labels != null) {
+      this.addAll(labels);  // Copy all elements from the provided list
+    }
+  }
+
 }
index 1a0cd85..db0aa8d 100644 (file)
@@ -1,11 +1,34 @@
-// Node.java
 package com.ReasoningTechnology.Ariadne;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Node extends HashMap<String, Object>{
+
+  private static String mark_property_name = "mark";
+  private static String neighbor_property_name = "neighbor";
 
-public class Node extends HashMap<Label, Object> {
-  // Constructor
   public Node(){
     super();
+    // Initialize the neighbor list in the constructor
+    this.put(neighbor_property_name, new LabelList());
   }
+
+  public void mark(Token token){
+    if( this.get(mark_property_name) == null ){
+      this.put(mark_property_name, new TokenSet());
+    }
+    ((TokenSet)this.get(mark_property_name)).add(token);
+  }
+
+  public boolean has_mark(Token token){
+    TokenSet mark = (TokenSet)this.get(mark_property_name);
+    return mark != null && mark.contains(token);
+  }
+
+  // Return the neighbor list (always exists after initialization)
+  public LabelList neighbor_LabelList(){
+    return (LabelList)this.get(neighbor_property_name);
+  }
+
 }