From fb393d964d88c96d9de12e46f87fc29d0163aa1f Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr>
Date: Mon, 18 Feb 2019 21:31:35 +0100
Subject: [PATCH] better logs for the graph builder, fixed topology priority
 according to discretization

---
 hysop/core/graph/graph_builder.py  | 83 +++++++++++++++++-------------
 hysop/fields/field_requirements.py | 13 +++--
 2 files changed, 56 insertions(+), 40 deletions(-)

diff --git a/hysop/core/graph/graph_builder.py b/hysop/core/graph/graph_builder.py
index 8a9fc5069..52d8bd1d7 100644
--- a/hysop/core/graph/graph_builder.py
+++ b/hysop/core/graph/graph_builder.py
@@ -20,13 +20,21 @@ from hysop.operator.redistribute      import Redistribute, RedistributeNotImplem
 from hysop.operator.transpose         import Transpose, TranspositionNotImplementedError
 from hysop.operator.memory_reordering import MemoryReordering, MemoryReorderingNotImplementedError
 
-DEBUG_GRAPH_BUILDER=False
+# Debug level for graph building
+#   0: no debug logs
+#   1: print debug info about input/outputs and operator generation
+#   2: print debug info about topology states in addition
+GRAPH_BUILDER_DEBUG_LEVEL=1
 
 def gprint(*args, **kwds):
-    if DEBUG_GRAPH_BUILDER:
+    level = kwds.pop('level', 1)
+    if GRAPH_BUILDER_DEBUG_LEVEL >= level:
         __builtin__.print(*args, **kwds)
-    else:
-        dprint(*args, **kwds)
+
+def gprint2(*args, **kwds):
+    kwds['level'] = 2
+    gprint(*args, **kwds)
+
 
 def _op_info(op,
         istates=None, ostates=None,
@@ -101,8 +109,8 @@ class GraphBuilder(object):
         """
         check_instance(node, ComputationalGraph)
         self.target_node = node
-        gprint('::Graph builder::')
-        gprint(' >Initialized graph builder for ComputationalGraph {}'.format(node.name))
+        gprint('\n::Graph builder::')
+        gprint('>Initialized graph builder for ComputationalGraph {}'.format(node.name))
 
     def configure(self, current_level, outputs_are_inputs, **kwds):
 
@@ -291,35 +299,31 @@ class GraphBuilder(object):
                             ofields[ofield] = otopo
 
                 # iterate over subgraph operator input parameters
-                gprint('   >Input parameters')
                 if iparams:
+                    gprint('   >Input parameters')
                     for iparam in sorted(iparams.values(), key=lambda x: x.name):
                         gprint('     *{}'.format(iparam.short_description()))
                         parameter_handler.handle_input_parameter(iparam, opnode)
                         if (iparam.name not in output_params):
                             input_params[iparam.name] = iparam
-                else:
-                    gprint('     no input parameters')
 
                 # iterate over subgraph operator output parameters
-                gprint('   >Output parameters')
                 if oparams:
+                    gprint('   >Output parameters')
                     for oparam in sorted(oparams.values(), key=lambda x: x.name):
                         gprint('     *{}'.format(oparam.short_description()))
                         parameter_handler.handle_output_parameter(oparam, opnode)
                         output_params[oparam.name] = oparam
-                else:
-                    gprint('     no output parameters')
 
 
                 # iterate over subgraph operator input fields
-                gprint('   >Input fields')
                 input_states = {}
                 if ifields:
+                    gprint('   >Input fields')
                     for (ifield,itopo) in sorted(ifields.iteritems(), key=lambda x: x[0].name, reverse=True):
-                        gprint('     *{} on {}'.format(ifield.name,
-                             'unknown topology (to be determined)' if (itopo is None) \
-                                     else 'topology {}'.format(itopo.id)))
+                        gprint('     *{}{}'.format(ifield.name,
+                             ' on an unknown topology (to be determined)' if (itopo is None) \
+                                     else '.{}'.format(itopo.pretty_tag)))
                         if (itopo is None):
                             continue
                         if isinstance(op, Problem):
@@ -346,16 +350,14 @@ class GraphBuilder(object):
                         if is_new:
                             input_fields[ifield] = itopo
                             input_topology_states[ifield] = (ifreqs, dstate)
-                else:
-                    gprint('     no input fields')
 
                 # iterate over subgraph operator output fields
-                gprint('   >Output fields')
                 output_states = {}
                 if ofields:
+                    gprint('   >Output fields')
                     for (ofield,otopo) in sorted(ofields.iteritems(), key=lambda x: x[0].name, reverse=True):
                         assert (otopo is not None)
-                        gprint('     *{} on topo {}'.format(ofield.name, otopo.id))
+                        gprint('     *{}.{}'.format(ofield.name, otopo.pretty_tag))
                         if isinstance(op, Problem):
                             if ofield in op.final_output_topology_states.keys():
                                 ofreqs = op.final_output_topology_states[ofield][0]
@@ -374,8 +376,6 @@ class GraphBuilder(object):
                         output_fields[ofield] = otopo
                         output_states[ofield] = dstate
                         output_topology_states[ofield] = (None, dstate)
-                else:
-                    gprint('     no output fields')
 
                 if (current_level==0) and ((op,opnode) not in deferred_operators):
                     vertex_properties['op_info'][opnode] = _op_info(op, input_states,
@@ -384,9 +384,9 @@ class GraphBuilder(object):
                 op_input_topology_states[op]  = input_states
                 op_output_topology_states[op] = output_states
 
-        # iterate defered nodes
+        # iterate deferred nodes
         for (op,opnode) in deferred_operators:
-            gprint(' >Handling defered node {}'.format(op.name))
+            gprint(' >Handling deferred node {}'.format(op.name))
             ifields = op.input_fields
             input_states  = op_input_topology_states[op]
             output_states = op_output_topology_states[op]
@@ -1010,17 +1010,19 @@ class GraphBuilder(object):
                                 istate.memory_order = default_memory_order
                             else:
                                 istate.memory_order = allowed_memory_order
-                            gprint('       >Initial state set to {}'.format(istate))
+                            gprint2('       >Initial state set to {}'.format(istate))
                     else:
                         istate = dtopology_states.setdefault(target_topo,
                                                 CartesianTopologyState(dim))
-                        gprint('       >Input state is {}'.format(istate))
+                        gprint2('       >Input state is {}'.format(istate))
 
                 target_axes = target_dfield_requirements.axes
                 target_memory_order = target_dfield_requirements.memory_order
                 def topology_affinity(candidate_topo):
                     candidate_state = self.discrete_topology_states[candidate_topo]
-                    score  = (candidate_topo is target_topo) * 100000                      # skip redistribute
+                    score  = (candidate_topo is target_topo) * 1000000                     # skip redistribute
+                    score += (candidate_topo.grid_resolution 
+                              == target_topo.grid_resolution)*100000                       # skip multiresolution filter (not automatically handled yet)
                     score += ((target_axes is not None) and
                               (candidate_state.axes in target_axes))*10000                 # skip transpose
                     score += (candidate_topo.backend is target_topo.backend)*1000          # better bandwidth
@@ -1031,10 +1033,13 @@ class GraphBuilder(object):
                     return score
 
                 if (target_topo.backend.kind is Backend.HOST) and write_nodes:
-                    gprint('REDISTRIBUTE TO HOST')
-                    src_topos = write_nodes.keys()
                     # give source topo priority according to topology_affinity
-                    src_topo = sorted(src_topos, key=topology_affinity, reverse=True)[0]
+                    src_topos = write_nodes.keys()
+                    src_topos = sorted(src_topos, key=topology_affinity, reverse=True)
+                    src_topo = src_topos[0]
+                    if (src_topo is not target_topo):
+                        gprint('   >Redistributing field {} from up to date topologies {} to host topology {}.'.format(
+                                ifield.name, ' ,'.join(t.pretty_tag for t in src_topos), target_topo.pretty_tag))
                     self.transpose(src_topo, target_axes,
                             graph, vertex_properties, edge_properties)
                     self.redistribute(target_topo, graph,
@@ -1043,10 +1048,13 @@ class GraphBuilder(object):
                     self.reorder(target_topo, target_memory_order,
                             graph, vertex_properties, edge_properties)
                 elif (target_topo.backend.kind is Backend.OPENCL) and write_nodes:
-                    gprint('REDISTRIBUTE TO DEVICE')
                     # give source topo priority according to topology_affinity
                     src_topos = write_nodes.keys()
-                    src_topo = sorted(src_topos, key=topology_affinity, reverse=True)[0]
+                    src_topos = sorted(src_topos, key=topology_affinity, reverse=True)
+                    src_topo = src_topos[0]
+                    if (src_topo is not target_topo):
+                        gprint('   >Redistributing field {} from up to date topologies {} to device topology {}.'.format(
+                                ifield.name, ' ,'.join(t.pretty_tag for t in src_topos), target_topo.pretty_tag))
                     self.reorder(src_topo, target_memory_order,
                             graph, vertex_properties, edge_properties)
                     self.redistribute(target_topo, graph,
@@ -1055,14 +1063,13 @@ class GraphBuilder(object):
                     self.transpose(target_topo, target_axes,
                             graph, vertex_properties, edge_properties)
                 else:
-                    gprint('NOTHING TO REDISTRIBUTE')
                     self.transpose(target_topo, target_axes,
                             graph, vertex_properties, edge_properties)
                     self.reorder(target_topo, target_memory_order,
                             graph, vertex_properties, edge_properties)
 
                 istate = dtopology_states[target_topo]
-                gprint('       >Input state is now {}'.format(istate))
+                gprint2('       >Input state is now {}'.format(istate))
             else:
                 istate = None
 
@@ -1113,7 +1120,7 @@ class GraphBuilder(object):
                         ofield, output_topo)
             
             if invalidate_field:
-                gprint('INVALIDATE FIELD {}'.format(ofield.name))
+                gprint('   >Invalidating output field {} on all topologies but {} because is has been freshly written.'.format(ofield.name, output_topo.pretty_tag))
                 # add dependency to all operators that reads this field
                 # to prevent concurent read-writes.
                 if output_topo in read_nodes:
@@ -1125,6 +1132,10 @@ class GraphBuilder(object):
                 # remove read/write dependencies and states
                 write_nodes.clear()
                 dtopology_states.clear()
+            else:
+                gprint('   >Keeping output field {} up to date on all topologies because is has been marked as preserved by operator.'.format(ofield.name))
+                gprint('   >Up to date topologies for field {} are now {}, {}.'.format(ofield.name, output_topo.pretty_tag,
+                    ' ,'.join(t.pretty_tag for t in write_nodes)))
 
             # add the operator node as the one that lastly wrote this field.
             # no other operators can be reading as this topology just been written.
@@ -1136,7 +1147,7 @@ class GraphBuilder(object):
                 else:
                     ostate = operator.output_topology_state(ofield, input_topology_states)
                 dtopology_states[output_topo] = ostate
-                gprint('       >Output state is now {}'.format(ostate))
+                gprint2('       >Output state is now {}'.format(ostate))
             else:
                 ostate = None
 
diff --git a/hysop/fields/field_requirements.py b/hysop/fields/field_requirements.py
index 6f06ccb21..5cf0bf854 100644
--- a/hysop/fields/field_requirements.py
+++ b/hysop/fields/field_requirements.py
@@ -11,10 +11,15 @@ from hysop.core.graph.computational_node import ComputationalGraphNode
 from hysop.fields.continuous_field import ScalarField
 from hysop.fields.discrete_field import DiscreteScalarField
 
-DEBUG_TOPO_CREATION=True
+# Debug level for topology creation
+#   0: no debug logs
+#   1: topo creation summary for each field
+#   2: topo creation details for all discrete field requirements
+TOPO_CREATION_DEBUG_LEVEL=1
 
 def gprint(*args, **kwds):
-    if DEBUG_TOPO_CREATION:
+    level = kwds.pop('level', 2)
+    if TOPO_CREATION_DEBUG_LEVEL >= level:
         __builtin__.print(*args, **kwds)
 
 
@@ -426,9 +431,9 @@ class MultiFieldRequirements(object):
                 req.set_and_check_topology(topo)
             all_topologies.update(known_topologies)
         
-        gprint("SUMMARY OF CREATED TOPOLOGIES FOR FIELD {}:".format(self.field.name))
+        gprint("SUMMARY OF CREATED TOPOLOGIES FOR FIELD {}:".format(self.field.name), level=1)
         for topo in all_topologies:
-            gprint("   *{}".format(topo.short_description()))
+            gprint("   *{}".format(topo.short_description()), level=1)
         gprint("")
 
 
-- 
GitLab