text: """MNIST example with static subgraph optimizations.
  
  This is a version of the Chainer MNIST example that has been modified
  to support the static subgraph optimizations feature. Note that
  the code is mostly unchanged except for the addition of the
  `@static_graph` decorator to the model chain's `__call__()` method.
  """
  from __future__ import print_function
  
  import argparse
  
  import chainer
  import chainer.functions as F
  import chainer.links as L
  from chainer import static_code
  from chainer import static_graph
  from chainer import training
  from chainer.training import extensions
  
  
  # Network definition
  class MLP(chainer.Chain):
  
      """A fully-connected neural network for digit classification.
  
      """
  
      def __init__(self, n_units, n_out):
          super(MLP, self).__init__()
          with self.init_scope():
              # the size of the inputs to each layer will be inferred
              self.l1 = L.Linear(None, n_units)  # n_in -> n_units
              self.l2 = L.Linear(None, n_units)  # n_units -> n_units
              self.l3 = L.Linear(None, n_out)  # n_units -> n_out
  
      @static_graph
      def __call__(self, x):
          h1 = F.relu(self.l1(x))
          h2 = F.relu(self.l2(h1))
          return self.l3(h2)
  
  
  class MLPSideEffect(chainer.Chain):
  
      """An example of a model with side-effects.
  
      This uses the same network as ``MLP`` except that it includes an
      example of side-effect code.
      """
  
      def __init__(self, n_units, n_out):
          super(MLPSideEffect, self).__init__()
          with self.init_scope():
              # the size of the inputs to each layer will be inferred
              self.l1 = L.Linear(None, n_units)  # n_in -> n_units
              self.l2 = L.Linear(None, n_units)  # n_units -> n_units
              self.l3 = L.Linear(None, n_out)  # n_units -> n_out
          self.side_effect_counter = 0
  
      @static_code
      def example_side_effect(self):
          # Any code that needs to run each iteration inside the `__call__()`
          # method should be wrapped inside of a method/function like this
          # using the `@static_code` decorator.
          self.side_effect_counter += 1
          if self.side_effect_counter % 1000 == 0:
              print("Side effect counter: ", self.side_effect_counter)
  
      @static_graph
      def __call__(self, x):
          # Example code with side effects:
          self.example_side_effect()
          # Define-by-run code:
          h1 = F.relu(self.l1(x))
          h2 = F.relu(self.l2(h1))
          return self.l3(h2)
  
  
  def main():
      parser = argparse.ArgumentParser(description='Chainer example: MNIST')
      parser.add_argument('--batchsize', '-b', type=int, default=100,
                          help='Number of images in each mini-batch')
      parser.add_argument('--epoch', '-e', type=int, default=20,
                          help='Number of sweeps over the dataset to train')
      parser.add_argument('--frequency', '-f', type=int, default=-1,
                          help='Frequency of taking a snapshot')
      parser.add_argument('--gpu', '-g', type=int, default=-1,
                          help='GPU ID (negative value indicates CPU)')
      parser.add_argument('--out', '-o', default='result',
                          help='Directory to output the result')
      parser.add_argument('--model', '-m', default='MLP',
                          help='Choose the model: MLP or MLPSideEffect')
      parser.add_argument('--resume', '-r', default='',
                          help='Resume the training from snapshot')
      parser.add_argument('--unit', '-u', type=int, default=1000,
                          help='Number of units')
      parser.add_argument('--noplot', dest='plot', action='store_false',
                          help='Disable PlotReport extension')
      args = parser.parse_args()
  
      print('GPU: {}'.format(args.gpu))
      print('# unit: {}'.format(args.unit))
      print('# Minibatch-size: {}'.format(args.batchsize))
      print('# epoch: {}'.format(args.epoch))
      print('')
  
      # Set up a neural network to train
      # Classifier reports softmax cross entropy loss and accuracy at every
      # iteration, which will be used by the PrintReport extension below.
      if args.model == 'MLP':
          model = L.Classifier(MLP(args.unit, 10))
      elif args.model == 'MLPSideEffect':
          model = L.Classifier(MLPSideEffect(args.unit, 10))
      if args.gpu >= 0:
          # Make a specified GPU current
          chainer.cuda.get_device_from_id(args.gpu).use()
          model.to_gpu()  # Copy the model to the GPU
  
      # Setup an optimizer
      optimizer = chainer.optimizers.Adam()
      optimizer.setup(model)
  
      # Load the MNIST dataset
      train, test = chainer.datasets.get_mnist()
  
      train_iter = chainer.iterators.SerialIterator(train, args.batchsize)
      test_iter = chainer.iterators.SerialIterator(test, args.batchsize,
                                                   repeat=False, shuffle=False)
  
      # Set up a trainer
      updater = training.updaters.StandardUpdater(
          train_iter, optimizer, device=args.gpu)
      trainer = training.Trainer(updater, (args.epoch, 'epoch'), out=args.out)
  
      # Evaluate the model with the test dataset for each epoch
      trainer.extend(extensions.Evaluator(test_iter, model, device=args.gpu))
  
      # Dump a computational graph from 'loss' variable at the first iteration
      # The "main" refers to the target link of the "main" optimizer.
      trainer.extend(extensions.DumpGraph('main/loss'))
  
      # Take a snapshot for each specified epoch
      frequency = args.epoch if args.frequency == -1 else max(1, args.frequency)
      trainer.extend(extensions.snapshot(), trigger=(frequency, 'epoch'))
  
      # Write a log of evaluation statistics for each epoch
      trainer.extend(extensions.LogReport())
  
      # Save two plot images to the result dir
      if args.plot and extensions.PlotReport.available():
          trainer.extend(
              extensions.PlotReport(['main/loss', 'validation/main/loss'],
                                    'epoch', file_name='loss.png'))
          trainer.extend(
              extensions.PlotReport(
                  ['main/accuracy', 'validation/main/accuracy'],
                  'epoch', file_name='accuracy.png'))
  
      # Print selected entries of the log to stdout
      # Here "main" refers to the target link of the "main" optimizer again, and
      # "validation" refers to the default name of the Evaluator extension.
      # Entries other than 'epoch' are reported by the Classifier link, called by
      # either the updater or the evaluator.
      trainer.extend(extensions.PrintReport(
          ['epoch', 'main/loss', 'validation/main/loss',
           'main/accuracy', 'validation/main/accuracy', 'elapsed_time']))
  
      # Print a progress bar to stdout
      trainer.extend(extensions.ProgressBar())
  
      if args.resume:
          # Resume from a snapshot
          chainer.serializers.load_npz(args.resume, trainer)
  
      # Run the training
      trainer.run()
  
  
  if __name__ == '__main__':
      main()
  