Repository: rlworkgroup/garage

Issue:
Broken plot in Tensorboard
Logger adds unnecessary evolving prefix to the name of tabular variables. Data points of a single viable are scattered into many plots, each with a single point. 

The name of plots look like the following:
```
_experiment_2019_03_25_16_00_26_0001__epoch__0___AverageDiscountedReturn
_experiment_2019_03_25_16_00_26_0001__epoch__0___AverageReturn
_experiment_2019_03_25_16_00_26_0001__epoch__0___Entropy
...
```


Discussion:

Resolving patch:
diff --git a/garage/experiment/local_tf_runner.py b/garage/experiment/local_tf_runner.py
--- a/garage/experiment/local_tf_runner.py
+++ b/garage/experiment/local_tf_runner.py
@@ -126,6 +126,7 @@ def setup(self, algo, env, sampler_cls=None, sampler_args=None):
         self.sampler = sampler_cls(algo, env, **sampler_args)
 
         self.initialize_tf_vars()
+        logger.log(self.sess.graph)
         self.has_setup = True
 
     def initialize_tf_vars(self):
diff --git a/garage/logger/simple_outputs.py b/garage/logger/simple_outputs.py
--- a/garage/logger/simple_outputs.py
+++ b/garage/logger/simple_outputs.py
@@ -58,8 +58,8 @@ class FileOutput(LogOutput, metaclass=abc.ABCMeta):
 
     def __init__(self, file_name, mode='w'):
         mkdir_p(os.path.dirname(file_name))
-        self._log_file = open(file_name,
-                              mode)  # Open the log file in child class
+        # Open the log file in child class
+        self._log_file = open(file_name, mode)
 
     def close(self):
         """Close any files used by the output."""
@@ -81,7 +81,7 @@ class TextOutput(FileOutput):
     def __init__(self, file_name, with_timestamp=True):
         super().__init__(file_name, 'a')
         self._with_timestamp = with_timestamp
-        self._delimiter = " | "
+        self._delimiter = ' | '
 
     @property
     def types_accepted(self):
diff --git a/garage/logger/tensor_board_output.py b/garage/logger/tensor_board_output.py
--- a/garage/logger/tensor_board_output.py
+++ b/garage/logger/tensor_board_output.py
@@ -39,7 +39,7 @@ def __init__(self, log_dir, flush_secs=120, histogram_samples=1e3):
     @property
     def types_accepted(self):
         """Return the types that the logger may pass to this output."""
-        return (TabularInput, )
+        return (TabularInput, tf.Graph)
 
     def record(self, data, prefix=''):
         """
@@ -50,13 +50,15 @@ def record(self, data, prefix=''):
         """
         if isinstance(data, TabularInput):
             self._waiting_for_dump.append(
-                functools.partial(self._record_tabular, data, prefix))
+                functools.partial(self._record_tabular, data))
+        elif isinstance(data, tf.Graph):
+            self._record_graph(data)
         else:
             raise ValueError('Unacceptable type.')
 
-    def _record_tabular(self, data, prefix, step):
+    def _record_tabular(self, data, step):
         for key, value in data.as_dict.items():
-            self._record_kv(prefix + key, value, step)
+            self._record_kv(key, value, step)
             data.mark(key)
 
     def _record_kv(self, key, value, step):
@@ -69,13 +71,14 @@ def _record_kv(self, key, value, step):
                 value.sample(self._histogram_samples))
             self._writer.add_histogram(key, samples, step)
 
+    def _record_graph(self, graph):
+        graph_def = graph.as_graph_def(add_shapes=True)
+        event = tbx.proto.event_pb2.Event(
+            graph_def=graph_def.SerializeToString())
+        self._writer.file_writer.add_event(event)
+
     def dump(self, step=None):
         """Flush summary writer to disk."""
-        if not self._added_graph:
-            # A static graph should only record once.
-            self._writer.add_graph(tf.get_default_graph())
-            self._added_graph = True
-
         # Log the tabular inputs, now that we have a step
         for p in self._waiting_for_dump:
             p(step or self._default_step)


Test patch:
diff --git a/tests/garage/logger/test_tensor_board_output.py b/tests/garage/logger/test_tensor_board_output.py
--- a/tests/garage/logger/test_tensor_board_output.py
+++ b/tests/garage/logger/test_tensor_board_output.py
@@ -3,14 +3,15 @@
 
 import matplotlib.pyplot as plt
 import numpy as np
+import tensorflow as tf
 import tensorflow_probability as tfp
 
 from garage.logger import TabularInput
 from garage.logger import TensorBoardOutput
-from tests.fixtures import TfTestCase
+from tests.fixtures import TfGraphTestCase
 
 
-class TBOutputTest(TfTestCase):
+class TBOutputTest(TfGraphTestCase):
     def setUp(self):
         super().setUp()
         self.log_dir = tempfile.TemporaryDirectory()
@@ -50,13 +51,11 @@ def setUp(self):
         super().setUp()
         self.mock_writer = self.tensor_board_output._writer
 
-    def test_graph_saved_only_once(self):
-        self.tabular.record('foo', 1.0)
-        self.tensor_board_output.dump()
-        self.tabular.record('foo', 2.0)
-        self.tensor_board_output.dump()
+    def test_record_graph(self):
+        foo = tf.constant(5)  # noqa: F841
+        self.tensor_board_output.record(self.graph)
 
-        self.mock_writer.add_graph.assert_called_once()
+        self.mock_writer.file_writer.add_event.assert_called_once()
 
     def test_record_scalar(self):
         foo = 5
@@ -89,8 +88,8 @@ def test_record_tabular(self):
         self.tensor_board_output.record(self.tabular, prefix='a/')
         self.tensor_board_output.dump()
 
-        self.mock_writer.add_scalar.assert_any_call('a/foo', foo, 0)
-        self.mock_writer.add_scalar.assert_any_call('a/bar', bar, 0)
+        self.mock_writer.add_scalar.assert_any_call('foo', foo, 0)
+        self.mock_writer.add_scalar.assert_any_call('bar', bar, 0)
 
     def test_record_tfp_distribution(self):
         histo_shape = np.ones((1000, 10))