code: def link_components(info):
      networks, datasets, renamed = (
          info.networks,
          info.datasets,
          info.renamed_variables,
      )
  
      def _link_optimizer(info, networks, datasets, renamed):
          # optimizer
          for optimizer in info.optimizers.values():
              o = optimizer.proto
              optimizer.network = networks[o.network_name]
              optimizer.data_iterators = OrderedDict()
              for d in o.dataset_name:
                  optimizer.data_iterators[d] = datasets[
                      d
                  ].data_iterator
  
              optimizer.dataset_assign = OrderedDict()
              for d in o.data_variable:
                  optimizer.dataset_assign[
                      optimizer.network.variables[
                          renamed.get(d.variable_name, d.variable_name)
                      ]
                  ] = d.data_name
  
              optimizer.generator_assign = OrderedDict()
              for g in o.generator_variable:
                  optimizer.generator_assign[
                      optimizer.network.variables[
                          renamed.get(g.variable_name, g.variable_name)
                      ]
                  ] = _get_generator(g)
  
              # for debugging
              # optimizer.net_variables = optimizer.network.variables
              # optimizer.net_variables.update(optimizer.network.parameters)
  
              optimizer.loss_variables = []
              for l in o.loss_variable:
                  optimizer.loss_variables.append(
                      optimizer.network.variables[
                          renamed.get(l.variable_name, l.variable_name)
                      ]
                  )
  
              optimizer.parameter_learning_rate_multipliers = (
                  OrderedDict()
              )
              for p in o.parameter_variable:
                  param_variable_names = _get_matching_variable_names(
                      p.variable_name,
                      list(
                          itertools.chain(
                              optimizer.network.parameters.keys(),
                              optimizer.network.variables.keys(),
                          )
                      ),
                  )
                  for v_name in param_variable_names:
                      if v_name in optimizer.network.parameters:
                          optimizer.parameter_learning_rate_multipliers[
                              optimizer.network.parameters[v_name]
                          ] = p.learning_rate_multiplier
                      elif v_name in optimizer.network.variables:
                          optimizer.parameter_learning_rate_multipliers[
                              optimizer.network.variables[v_name]
                          ] = p.learning_rate_multiplier
  
              parameters = {}
              for (
                  v,
                  local_lr,
              ) in (
                  optimizer.parameter_learning_rate_multipliers.items()
              ):
                  if local_lr > 0.0:
                      parameters[v.name] = v.variable_instance
                  else:
                      v.variable_instance.need_grad = False
              optimizer.solver.set_parameters(parameters)
              optimizer.parameters = OrderedDict(
                  sorted(parameters.items(), key=lambda x: x[0])
              )
  
              # restore solver checkpoint if exists
              restore_optimizer_state(optimizer)
  
              for v in optimizer.loss_variables:
                  v.variable_instance.grad.fill(
                      1.0 / v.variable_instance.size
                  )
  
              if len(optimizer.loss_variables) == 1:
                  optimizer.target = optimizer.loss_variables[
                      0
                  ].variable_instance
              else:
                  optimizer.target = F.sink(
                      *[
                          v.variable_instance
                          for v in optimizer.loss_variables
                      ],
                      one_input_grad=False,
                  )
  
      def _link_monitor(info, networks, datasets, renamed):
          # monitor
          for monitor in info.monitors.values():
              m = monitor.proto
              monitor.network = networks[m.network_name]
  
              # for debugging
              # monitor.net_variables = monitor.network.variables
              # monitor.net_variables.update(monitor.network.parameters)
  
              monitor.data_iterators = OrderedDict()
              for d in m.dataset_name:
                  monitor.data_iterators[d] = datasets[d].data_iterator
  
              monitor.dataset_assign = OrderedDict()
              for d in m.data_variable:
                  monitor.dataset_assign[
                      monitor.network.variables[
                          renamed.get(d.variable_name, d.variable_name)
                      ]
                  ] = d.data_name
  
              monitor.generator_assign = OrderedDict()
              for g in m.generator_variable:
                  monitor.generator_assign[
                      monitor.network.variables[
                          renamed.get(g.variable_name, g.variable_name)
                      ]
                  ] = _get_generator(g)
  
              monitor.monitor_variables = []
              for e in m.monitor_variable:
                  monitor.monitor_variables.append(
                      monitor.network.variables[
                          renamed.get(e.variable_name, e.variable_name)
                      ]
                  )
  
              monitor.target = F.sink(
                  *[
                      v.variable_instance
                      for v in monitor.monitor_variables
                  ]
              )
  
      def _link_executor(info, networks, renamed):
          # executor
          for executor in info.executors.values():
              e = executor.proto
              executor.network = networks[e.network_name]
              executor.dataset_assign = OrderedDict()
              for d in e.data_variable:
                  executor.dataset_assign[
                      executor.network.variables[
                          renamed.get(d.variable_name, d.variable_name)
                      ]
                  ] = d.data_name
  
              executor.generator_assign = OrderedDict()
              for g in e.generator_variable:
                  executor.generator_assign[
                      executor.network.variables[
                          renamed.get(g.variable_name, g.variable_name)
                      ]
                  ] = _get_generator(g)
  
              executor.output_assign = OrderedDict()
              for o in e.output_variable:
                  executor.output_assign[
                      executor.network.variables[
                          renamed.get(o.variable_name, o.variable_name)
                      ]
                  ] = [o.type, o.data_name]
  
              executor.parameters = OrderedDict()
              for p in e.parameter_variable:
                  param_variable_names = _get_matching_variable_names(
                      p.variable_name,
                      list(
                          itertools.chain(
                              executor.network.parameters.keys(),
                              executor.network.variables.keys(),
                          )
                      ),
                  )
                  for v_name in param_variable_names:
                      if v_name in executor.network.parameters:
                          executor.parameters[
                              executor.network.parameters[v_name]
                          ] = v_name
                      if v_name in executor.network.variables:
                          executor.parameters[
                              executor.network.variables[v_name]
                          ] = v_name
  
              executor.forward_target = F.sink(
                  *[
                      v.variable_instance
                      for v in executor.output_assign.keys()
                  ]
              )
  
              if executor.need_back_propagation:
                  executor.loss_variables = []
                  for l in e.loss_variable:
                      executor.loss_variables.append(
                          executor.network.variables[l.variable_name]
                      )
  
                  executor.parameter_learning_rate_multipliers = (
                      OrderedDict()
                  )
                  for p in e.parameter_variable:
                      param_variable_names = _get_matching_variable_names(
                          p.variable_name,
                          list(
                              itertools.chain(
                                  executor.network.parameters.keys(),
                                  executor.network.variables.keys(),
                              )
                          ),
                      )
                      for v_name in param_variable_names:
                          if v_name in executor.network.parameters:
                              executor.parameter_learning_rate_multipliers[
                                  executor.network.parameters[v_name]
                              ] = p.learning_rate_multiplier
                          elif v_name in executor.network.variables:
                              executor.parameter_learning_rate_multipliers[
                                  executor.network.variables[v_name]
                              ] = p.learning_rate_multiplier
  
                  executor.backward_target = F.sink(
                      *[
                          v.variable_instance
                          for v in executor.loss_variables
                      ]
                  )
  
      _link_optimizer(info, networks, datasets, renamed)
      _link_monitor(info, networks, datasets, renamed)
      _link_executor(info, networks, renamed)
  
imports: import itertools
  import os
  import re
  from collections import OrderedDict
  import nnabla
  import nnabla.functions
  import nnabla.solver
  import numpy
  from nnabla.core.graph_optimizer import IdentityRemover
  from nnabla.initializer import NormalInitializer, UniformInitializer, ConstantInitializer, RangeInitializer
  from nnabla.logger import logger
  from nnabla.utils import nnabla_pb2
  from nnabla.utils.communicator_util import current_communicator, single_or_rankzero
  from nnabla.utils.create_cache import CreateCache
  from nnabla.utils.data_iterator import data_iterator_csv_dataset, data_iterator_cache
  from nnabla.utils.get_file_handle import get_initial_file_loader, load_files
  from nnabla.utils.learning_rate_scheduler import PolynomialScheduler, CosineScheduler, ExponentialScheduler, StepScheduler, LinearWarmupScheduler
label_desc: Use image node_id 16 for calling a global function with example usage: link_components(info) without return types