<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def data_iterator_simple(load_func, num_examples, batch_size, shuffle=False, rng=None, with_memory_cache=True, with_file_cache=True, cache_dir=None, epoch_begin_callbacks=[], epoch_end_callbacks=[]):
 """A generator that ``yield`` s minibatch data as a tuple, as defined in ``load_func`` . It can unlimitedly yield minibatches at your request, queried from the provided data. Args: load_func (function):
 Takes a single argument `i`, an index of an example in your dataset to be loaded, and returns a tuple of data. Every call by any index `i` must return a tuple of arrays with the same shape. num_examples (int):
 Number of examples in your dataset. Random sequence of indexes is generated according to this number. batch_size (int):
 Size of data unit. shuffle (bool):
 Indicates whether the dataset is shuffled or not. Default value is False. rng (None or :obj:`numpy.random.RandomState`):
 Numpy random number generator. with_memory_cache (bool):
 If ``True``, use :py:class:`.data_source.DataSourceWithMemoryCache` to wrap ``data_source``. It is a good idea to set this as true unless data_source provides on-memory data. Default value is True. with_file_cache (bool):
 If ``True``, use :py:class:`.data_source.DataSourceWithFileCache` to wrap ``data_source``. If ``data_source`` is slow, enabling this option a is good idea. Default value is False. cache_dir (str):
 Location of file_cache. If this value is None, :py:class:`.data_source.DataSourceWithFileCache` creates file caches implicitly on temporary directory and erases them all when data_iterator is finished. Otherwise, :py:class:`.data_source.DataSourceWithFileCache` keeps created cache. Default is None. epoch_begin_callbacks (list of functions):
 An item is a function which takes an epoch index as an argument. These are called at the beginning of an epoch. epoch_end_callbacks (list of functions):
 An item is a function which takes an epoch index as an argument. These are called at the end of an epoch. Returns: :py:class:`DataIterator <nnabla.utils.data_iterator.DataIterator>`: Instance of DataIterator. Here is an example of `load_func` which returns an image and a label of a classification dataset. .. code-block:: python import numpy as np from nnabla.utils.image_utils import imread image_paths = load_image_paths() labels = load_labels() def my_load_func(i):
 ''' Returns: image: c x h x w array label: 0-shape array ''' img = imread(image_paths[i]).astype('float32') return np.rollaxis(img, 2), np.array(labels[i]) """

    return data_iterator(SimpleDataSource(load_func,
                                          num_examples,
                                          shuffle=shuffle,
                                          rng=rng),
                         batch_size=batch_size,
                         with_memory_cache=with_memory_cache,
                         with_file_cache=with_file_cache,
                         cache_dir=cache_dir,
                         epoch_begin_callbacks=epoch_begin_callbacks,
                         epoch_end_callbacks=epoch_end_callbacks)