• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020-2021 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""container"""
16from __future__ import absolute_import
17
18from collections import OrderedDict, abc
19from abc import abstractmethod
20
21from mindspore.nn.cell import Cell
22
23__all__ = ['SequentialCell', 'CellList', 'CellDict']
24
25
26def _valid_index(cell_num, index, op_name=None):
27    """Internal function, used to detect the value and type of index."""
28    msg_prefix = f"For '{op_name}', the" if op_name else "The"
29    if not isinstance(index, int):
30        raise TypeError(f"{msg_prefix} type of 'index' must be int, but got {type(index).__name__}.")
31    if not -cell_num <= index < cell_num:
32        raise IndexError(f"{msg_prefix} value of 'index' must be a number in range [{-cell_num}, {cell_num}), "
33                         f"but got {index}.")
34    return index % cell_num
35
36
37def _valid_index_for_inserting(cell_num, index, op_name=None):
38    """
39    Internal function, used to detect the value and type of index for inserting Cell in
40    SequentialCell or CellList.
41    """
42    msg_prefix = f"For '{op_name}', the" if op_name else "The"
43    if not isinstance(index, int):
44        raise TypeError(f"{msg_prefix} type of 'index' must be int, but got {type(index).__name__}.")
45    if not -cell_num <= index <= cell_num:
46        raise IndexError(f"{msg_prefix} value of 'index' must be a number in range [{-cell_num}, {cell_num}], "
47                         f"but got {index}.")
48    return index % cell_num if (cell_num != 0 and index != cell_num) else index
49
50
51def _valid_cell(cell, op_name=None):
52    """Internal function, used to check whether the input cell is a subclass of Cell."""
53    if issubclass(cell.__class__, Cell):
54        return True
55    msg_prefix = f"For '{op_name}'," if op_name else ""
56    raise TypeError(f'{msg_prefix} each cell must be subclass of Cell, but got {type(cell).__name__}.')
57
58
59def _get_prefix_and_index(cells):
60    """get prefix and index of parameter name in sequential cell or cell list."""
61    prefix = ""
62    index = 0
63    if not cells:
64        return prefix, index
65
66    cell_list = list(cells.items())
67    first_param, first_key = None, None
68    second_param, second_key = None, None
69    for key, cell in cell_list:
70        try:
71            _, param = next(cell.parameters_and_names())
72        except StopIteration:
73            continue
74        if first_param is None:
75            first_param = param
76            first_key = key
77            continue
78        second_param = param
79        second_key = key
80        break
81
82    if first_param is None:
83        return prefix, index
84
85    split_names = first_param.name.split(".")
86    for idx, name in enumerate(split_names):
87        if name == first_key:
88            prefix = ".".join(split_names[:idx])
89            prefix = prefix + "." if prefix else prefix
90            index = idx
91            if second_param is not None and second_param.name.split(".")[idx] == second_key:
92                break
93    return prefix, index
94
95
96class _CellListBase:
97    """
98    An interface for base the Cell as list.
99
100    The sequential Cell may be iterated using the construct method using for-in statement.
101    But there are some scenarios that the construct method built-in does not fit.
102    For convenience, we provide an interface that indicates the sequential
103    Cell may be interpreted as list of Cells, so it can be accessed using
104    iterator or subscript when a sequential Cell instantiate is accessed
105    by iterator or subscript, it will be interpreted as a list of Cells.
106    """
107    def __init__(self):
108        """Initialize _CellListBase."""
109        self.__cell_as_list__ = True
110
111    @abstractmethod
112    def __len__(self):
113        pass
114
115    @abstractmethod
116    def __getitem__(self, index):
117        pass
118
119    def construct(self):
120        raise NotImplementedError
121
122
123class SequentialCell(Cell):
124    """
125    Sequential Cell container. For more details about Cell, please refer to
126    `Cell <https://www.mindspore.cn/docs/en/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell>`_.
127
128    A list of Cells will be added to it in the order they are passed in the constructor.
129    Alternatively, an ordered dict of cells can also be passed in.
130
131    Args:
132        args (list, OrderedDict): List or OrderedDict of subclass of Cell.
133
134    Inputs:
135        - **x** (Tensor) - Tensor with shape according to the first Cell in the sequence.
136
137    Outputs:
138        Tensor, the output Tensor with shape depending on the input `x` and defined sequence of Cells.
139
140    Raises:
141        TypeError: If the type of the `args` is not list or OrderedDict.
142
143    Supported Platforms:
144        ``Ascend`` ``GPU`` ``CPU``
145
146    Examples:
147        >>> import mindspore
148        >>> from mindspore import Tensor, nn
149        >>> import numpy as np
150        >>>
151        >>> conv = nn.Conv2d(3, 2, 3, pad_mode='valid', weight_init="ones")
152        >>> relu = nn.ReLU()
153        >>> seq = nn.SequentialCell([conv, relu])
154        >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype = mindspore.float32)
155        >>> output = seq(x)
156        >>> print(output)
157        [[[[27. 27.]
158           [27. 27.]]
159          [[27. 27.]
160           [27. 27.]]]]
161        >>> from collections import OrderedDict
162        >>> d = OrderedDict()
163        >>> d["conv"] = conv
164        >>> d["relu"] = relu
165        >>> seq = nn.SequentialCell(d)
166        >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype=mindspore.float32)
167        >>> output = seq(x)
168        >>> print(output)
169        [[[[27. 27.]
170           [27. 27.]]
171          [[27. 27.]
172           [27. 27.]]]]
173    """
174    def __init__(self, *args):
175        """Initialize SequentialCell."""
176        super(SequentialCell, self).__init__()
177        self._is_dynamic_name = []
178        if len(args) == 1:
179            cells = args[0]
180            if isinstance(cells, Cell):
181                cell = cells
182                self.insert_child_to_cell(str(0), cell)
183                cell.update_parameters_name(str(0) + ".")
184                self._is_dynamic_name.append(True)
185            elif isinstance(cells, list):
186                for index, cell in enumerate(cells):
187                    self.insert_child_to_cell(str(index), cell)
188                    cell.update_parameters_name(str(index) + ".")
189                    self._is_dynamic_name.append(True)
190            elif isinstance(cells, OrderedDict):
191                for name, cell in cells.items():
192                    self.insert_child_to_cell(name, cell)
193                    cell.update_parameters_name(name + ".")
194                    self._is_dynamic_name.append(False)
195            else:
196                raise TypeError(f"For '{self.__class__.__name__}', the 'args[0]' must be Cell, list or orderedDict, "
197                                f"but got {type(cells).__name__}")
198        else:
199            for index, cell in enumerate(args):
200                self.insert_child_to_cell(str(index), cell)
201                cell.update_parameters_name(str(index) + ".")
202                self._is_dynamic_name.append(True)
203        self.cell_list = list(self._cells.values())
204
205    def __getitem__(self, index):
206        if isinstance(index, slice):
207            return self.__class__(
208                OrderedDict(list(self._cells.items())[index]))
209        index = _valid_index(len(self), index, self.__class__.__name__)
210        return list(self._cells.values())[index]
211
212    def __setitem__(self, index, cell):
213        cls_name = self.__class__.__name__
214        if _valid_cell(cell, cls_name):
215            prefix, _ = _get_prefix_and_index(self._cells)
216            index = _valid_index(len(self), index, cls_name)
217            key = list(self._cells.keys())[index]
218            self._cells[key] = cell
219            cell.update_parameters_name(prefix + key + ".")
220            self.cell_list = list(self._cells.values())
221
222    def __delitem__(self, index):
223        cls_name = self.__class__.__name__
224        if isinstance(index, int):
225            index = _valid_index(len(self), index, cls_name)
226            key = list(self._cells.keys())[index]
227            del self._cells[key]
228            del self._is_dynamic_name[index]
229        elif isinstance(index, slice):
230            keys = list(self._cells.keys())[index]
231            for key in keys:
232                del self._cells[key]
233            del self._is_dynamic_name[index]
234        else:
235            raise TypeError(f"For '{cls_name}', the type of index must be int type or slice type, "
236                            f"but got {type(index).__name__}")
237        prefix, key_index = _get_prefix_and_index(self._cells)
238        temp_dict = OrderedDict()
239        for idx, key in enumerate(self._cells.keys()):
240            cell = self._cells[key]
241            if self._is_dynamic_name[idx]:
242                for _, param in cell.parameters_and_names():
243                    param.name = prefix + str(idx) + "." + ".".join(param.name.split(".")[key_index+1:])
244                temp_dict[str(idx)] = cell
245            else:
246                temp_dict[key] = cell
247        self._cells = temp_dict
248        self.cell_list = list(self._cells.values())
249
250    def __bool__(self):
251        return len(self._cells) != 0
252
253    def __len__(self):
254        return len(self._cells)
255
256    def set_grad(self, flag=True):
257        self.requires_grad = flag
258        for cell in self._cells.values():
259            cell.set_grad(flag)
260
261    def append(self, cell):
262        """
263        Appends a given Cell to the end of the list.
264
265        Args:
266            cell(Cell): The Cell to be appended.
267
268        Examples:
269            >>> import mindspore
270            >>> from mindspore import Tensor, nn
271            >>> import numpy as np
272            >>>
273            >>> conv = nn.Conv2d(3, 2, 3, pad_mode='valid', weight_init="ones")
274            >>> bn = nn.BatchNorm2d(2)
275            >>> relu = nn.ReLU()
276            >>> seq = nn.SequentialCell([conv, bn])
277            >>> seq.append(relu)
278            >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype=mindspore.float32)
279            >>> output = seq(x)
280            >>> print(output)
281            [[[[26.999863 26.999863]
282               [26.999863 26.999863]]
283              [[26.999863 26.999863]
284               [26.999863 26.999863]]]]
285        """
286        if _valid_cell(cell, self.__class__.__name__):
287            prefix, _ = _get_prefix_and_index(self._cells)
288            cell.update_parameters_name(prefix + str(len(self)) + ".")
289            self._is_dynamic_name.append(True)
290            self._cells[str(len(self))] = cell
291        self.cell_list = list(self._cells.values())
292
293    def construct(self, input_data):
294        for cell in self.cell_list:
295            input_data = cell(input_data)
296        return input_data
297
298    def _insert(self, index, cell):
299        """
300        Inserts a given Cell before a given index in the list.
301
302        Args:
303            index(int): The Insert index in the CellList.
304            cell(Cell): The Cell to be inserted.
305        """
306        cls_name = self.__class__.__name__
307        idx = _valid_index_for_inserting(len(self), index, cls_name)
308        _valid_cell(cell, cls_name)
309        length = len(self)
310        prefix, key_index = _get_prefix_and_index(self._cells)
311        while length > idx:
312            if self._auto_prefix:
313                tmp_cell = self._cells[str(length-1)]
314                for _, param in tmp_cell.parameters_and_names():
315                    param.name = f'{prefix}{str(length)}{"."}{".".join(param.name.split(".")[key_index+1:])}'
316            self._cells[str(length)] = self._cells[str(length - 1)]
317            length -= 1
318        self._cells[str(idx)] = cell
319        if self._auto_prefix:
320            cell.update_parameters_name(prefix + str(idx) + ".")
321        self.cell_list = list(self._cells.values())
322        self._is_dynamic_name.insert(index, True)
323
324
325class CellList(_CellListBase, Cell):
326    """
327    Holds Cells in a list. For more details about Cell, please refer to
328    `Cell <https://www.mindspore.cn/docs/en/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell>`_.
329
330    CellList can be used like a regular Python list, the Cells it contains have been initialized and
331    the types of Cells it contains can not be CellDict.
332    Unlike the SequentialCell, the cells in CellList are not connected.
333
334    Args:
335        args (list, optional): List of subclass of Cell.
336
337    Supported Platforms:
338        ``Ascend`` ``GPU`` ``CPU``
339
340    Examples:
341        >>> import mindspore as ms
342        >>> import numpy as np
343        >>>
344        >>> conv = ms.nn.Conv2d(100, 20, 3)
345        >>> bn = ms.nn.BatchNorm2d(20)
346        >>> relu = ms.nn.ReLU()
347        >>> cell_ls = ms.nn.CellList([bn])
348        >>> cell_ls.insert(0, conv)
349        >>> cell_ls.append(relu)
350        >>> cell_ls.extend([relu, relu])
351        >>> cell_ls_3 = cell_ls[3]
352        >>> input1 = ms.Tensor(np.ones([2, 3]), ms.float32)
353        >>> output = cell_ls_3(input1)
354        >>> print(output)
355        [[1. 1. 1.]
356        [1. 1. 1.]]
357    """
358    def __init__(self, *args, **kwargs):
359        """Initialize CellList."""
360        auto_prefix = kwargs["auto_prefix"] if "auto_prefix" in kwargs.keys() else True
361        _CellListBase.__init__(self)
362        Cell.__init__(self, auto_prefix)
363        if len(args) == 1:
364            self.extend(args[0])
365
366    def __getitem__(self, index):
367        cls_name = self.__class__.__name__
368        if isinstance(index, slice):
369            return CellList(list(self._cells.values())[index])
370        if isinstance(index, int):
371            index = _valid_index(len(self), index, cls_name)
372            return self._cells[str(index)]
373        raise TypeError(f"For '{cls_name}', the type of 'index' must be int or slice, "
374                        f"but got {type(index).__name__}.")
375
376    def __setitem__(self, index, cell):
377        cls_name = self.__class__.__name__
378        if not isinstance(index, int) and _valid_cell(cell, cls_name):
379            raise TypeError(f"For '{cls_name}', the type of 'index' must be int, "
380                            f"but got {type(index).__name__}.")
381        index = _valid_index(len(self), index, cls_name)
382        if self._auto_prefix:
383            prefix, _ = _get_prefix_and_index(self._cells)
384            cell.update_parameters_name(prefix + str(index) + ".")
385        self._cells[str(index)] = cell
386
387    def __delitem__(self, index):
388        cls_name = self.__class__.__name__
389        if isinstance(index, int):
390            index = _valid_index(len(self), index, cls_name)
391            del self._cells[str(index)]
392        elif isinstance(index, slice):
393            keys = list(self._cells.keys())[index]
394            for key in keys:
395                del self._cells[key]
396        else:
397            raise TypeError(f"For '{cls_name}', the type of 'index' must be int or slice, "
398                            f"but got {type(index).__name__}.")
399        # adjust orderedDict
400        prefix, key_index = _get_prefix_and_index(self._cells)
401        temp_dict = OrderedDict()
402        for idx, cell in enumerate(self._cells.values()):
403            if self._auto_prefix:
404                for _, param in cell.parameters_and_names():
405                    param.name = prefix + str(idx) + "." + ".".join(param.name.split(".")[key_index+1:])
406            temp_dict[str(idx)] = cell
407        self._cells = temp_dict
408
409    def __bool__(self):
410        return len(self._cells) != 0
411
412    def __len__(self):
413        return len(self._cells)
414
415    def __iter__(self):
416        return iter(self._cells.values())
417
418    def __iadd__(self, cells):
419        self.extend(cells)
420        return self
421
422    def insert(self, index, cell):
423        """
424        Inserts a given Cell before a given index in the list.
425
426        Args:
427            index(int): The Insert index in the CellList.
428            cell(Cell): The Cell to be inserted.
429        """
430        cls_name = self.__class__.__name__
431        idx = _valid_index_for_inserting(len(self), index, cls_name)
432        _valid_cell(cell, cls_name)
433        length = len(self)
434        prefix, key_index = _get_prefix_and_index(self._cells)
435        while length > idx:
436            if self._auto_prefix:
437                tmp_cell = self._cells[str(length-1)]
438                for _, param in tmp_cell.parameters_and_names():
439                    param.name = prefix + str(length) + "." + ".".join(param.name.split(".")[key_index+1:])
440            self._cells[str(length)] = self._cells[str(length - 1)]
441            length -= 1
442        self._cells[str(idx)] = cell
443        if self._auto_prefix:
444            cell.update_parameters_name(prefix + str(idx) + ".")
445
446    def extend(self, cells):
447        """
448        Appends Cells from a Python iterable to the end of the list.
449
450        Args:
451            cells(list): The Cells to be extended, the types of Cells can not be CellDict.
452
453        Raises:
454            TypeError: If the argument cells are not a list of Cells.
455        """
456        cls_name = self.__class__.__name__
457        if not isinstance(cells, list):
458            raise TypeError(f"For '{cls_name}', the new cells wanted to append "
459                            f"should be instance of list, but got {type(cells).__name__}.")
460        prefix, _ = _get_prefix_and_index(self._cells)
461        for cell in cells:
462            if isinstance(cell, CellDict):
463                raise TypeError(f"For '{cls_name}', the type of cell can not be CellDict, "
464                                f"but got {type(cell).__name__}.")
465            if _valid_cell(cell, cls_name):
466                if self._auto_prefix:
467                    cell.update_parameters_name(prefix + str(len(self)) + ".")
468                self._cells[str(len(self))] = cell
469        return self
470
471    def append(self, cell):
472        """
473        Appends a given Cell to the end of the list.
474
475        Args:
476            cell(Cell): The subcell to be appended.
477        """
478        if _valid_cell(cell, self.__class__.__name__):
479            if self._auto_prefix:
480                prefix, _ = _get_prefix_and_index(self._cells)
481                cell.update_parameters_name(prefix + str(len(self)) + ".")
482            self._cells[str(len(self))] = cell
483
484    def set_grad(self, flag=True):
485        self.requires_grad = flag
486        for cell in self._cells.values():
487            cell.set_grad(flag)
488
489    def construct(self, *inputs):
490        raise NotImplementedError
491
492
493class _CellDictBase:
494    """
495    An interface for base the Cell as dict.
496
497    The sequential Cell may be iterated using the construct method using for-in statement.
498    But there are some scenarios that the construct method built-in does not fit.
499    For convenience, we provide an interface that indicates the sequential
500    Cell may be interpreted as dict of Cells, so it can be accessed using
501    key when a sequential Cell instantiate is accessed by key,
502    it will be interpreted as a dict of Cells.
503    """
504    def __init__(self):
505        """Initialize _CellDictBase."""
506        self.__cell_as_dict__ = True
507
508    @abstractmethod
509    def __len__(self):
510        pass
511
512    @abstractmethod
513    def __getitem__(self, index):
514        pass
515
516    def construct(self):
517        raise NotImplementedError
518
519
520class CellDict(_CellDictBase, Cell):
521    """
522    Holds Cells in a dictionary. For more details about `Cell` , please refer to :class:`mindspore.nn.Cell` .
523
524    `CellDict` can be used like a regular Python dictionary.
525
526    Args:
527        args (iterable, optional): An iterable of key-value pairs of (key, Cell), the type of key-value pairs is
528                                   (string, Cell); Or a mapping(dictionary) from string to Cell.
529                                   The type of Cell can not be CellDict, CellList or SequentialCell.
530                                   The key can not be same with the attributes of class Cell, can not contain '.',
531                                   can not be an empty string.
532                                   The key of type string is used to search corresponding Cell in the CellDict.
533        kwargs (dict): Reserved for keyword argument to be expanded.
534
535    Supported Platforms:
536        ``Ascend`` ``GPU`` ``CPU``
537
538    Examples:
539        >>> import collections
540        >>> from collections import OrderedDict
541        >>> import mindspore as ms
542        >>> import numpy as np
543        >>> from mindspore import Tensor, nn
544        >>>
545        >>> cell_dict = nn.CellDict({'conv': nn.Conv2d(10, 6, 5),
546        ...                          'relu': nn.ReLU(),
547        ...                          'max_pool2d': nn.MaxPool2d(kernel_size=4, stride=4)})
548        >>> print(len(cell_dict))
549        3
550        >>> cell_dict.clear()
551        >>> print(len(cell_dict))
552        0
553        >>> ordered_cells = OrderedDict([('conv', nn.Conv2d(10, 6, 5, pad_mode='valid')),
554        ...                              ('relu', nn.ReLU()),
555        ...                              ('max_pool2d', nn.MaxPool2d(kernel_size=2, stride=2))])
556        >>> cell_dict.update(ordered_cells)
557        >>> x = Tensor(np.ones([1, 10, 6, 10]), ms.float32)
558        >>> for cell in cell_dict.values():
559        ...     x = cell(x)
560        >>> print(x.shape)
561        (1, 6, 1, 3)
562        >>> x = Tensor(np.ones([1, 10, 6, 10]), ms.float32)
563        >>> for item in cell_dict.items():
564        ...     x = item[1](x)
565        >>> print(x.shape)
566        (1, 6, 1, 3)
567        >>> print(cell_dict.keys())
568        odict_keys(['conv', 'relu', 'max_pool2d'])
569        >>> pop_cell = cell_dict.pop('conv')
570        >>> x = Tensor(np.ones([1, 10, 6, 5]), ms.float32)
571        >>> x = pop_cell(x)
572        >>> print(x.shape)
573        (1, 6, 2, 1)
574        >>> print(len(cell_dict))
575        2
576    """
577    def __init__(self, *args, **kwargs):
578        """Initialize CellDict."""
579        auto_prefix = kwargs["auto_preifx"] if "auto_prefix" in kwargs.keys() else True
580        _CellDictBase.__init__(self)
581        Cell.__init__(self, auto_prefix)
582        if len(args) == 1:
583            self.update(args[0])
584
585    def __getitem__(self, key):
586        return self._cells[key]
587
588    def __setitem__(self, key, cell):
589        self._validate_key(key)
590        self._validate_cell_type(cell)
591        self._update_cell_para_name(key, cell)
592        self._cells[key] = cell
593
594    def __delitem__(self, key):
595        del self._cells[key]
596
597    def __len__(self):
598        return len(self._cells)
599
600    def __iter__(self):
601        return iter(self._cells)
602
603    def __contains__(self, key):
604        return key in self._cells
605
606    def _validate_key(self, key):
607        """validate key."""
608        cls_name = self.__class__.__name__
609        if not isinstance(key, str):
610            raise TypeError(f"For '{cls_name}', the type of key should be string "
611                            f"but got {type(key).__name__}.")
612        if hasattr(self, key) and key not in self._cells:
613            raise KeyError(f"For '{cls_name}', the key can not be same with the attributes of Cell, "
614                           f"but got key {key}.")
615        if '.' in key:
616            raise KeyError(f"For '{cls_name}', key can not contain \".\", "
617                           f"but got key {key}")
618        if key == '':
619            raise KeyError(f"For '{cls_name}', key can not be empty string \"\", "
620                           f"but got key {key}")
621
622    def _validate_cell_type(self, cell):
623        """validate cell type."""
624        cls_name = self.__class__.__name__
625        if cell is None:
626            raise TypeError(f"For '{cls_name}', cell can not be None.")
627        if not isinstance(cell, Cell):
628            raise TypeError(f"For '{cls_name}', the type of cell should be Cell, "
629                            f"but got {type(cell).__name__}.")
630        if isinstance(cell, (CellDict, CellList, SequentialCell)):
631            raise TypeError(f"For '{cls_name}', the type of cell can not be CellDict, CellList or SequentialCell, "
632                            f"but got {type(cell).__name__}.")
633
634    def _update_cell_para_name(self, key, cell):
635        """update cell para name."""
636        if self._auto_prefix:
637            prefix, _ = _get_prefix_and_index(self._cells)
638            cell.update_parameters_name(prefix + key + ".")
639
640    def clear(self):
641        """
642        Remove all Cells from the CellDict.
643        """
644        return self._cells.clear()
645
646    def pop(self, key):
647        """
648        Remove key from the CellDict and return its cell.
649
650        Args:
651            key (string): key to pop from the CellDict.
652
653        Raises:
654            KeyError: If `key` not exist in CellDict when attempt to access cell.
655        """
656        value = self[key]
657        del self[key]
658        return value
659
660    def keys(self):
661        """
662        Return an iterable of the CellDict keys.
663
664        Returns:
665            An iterable object.
666        """
667        return self._cells.keys()
668
669    def values(self):
670        """
671        Return an iterable of the CellDict values.
672
673        Returns:
674            An iterable object.
675        """
676        return self._cells.values()
677
678    def items(self):
679        """
680        Return an iterable of the CellDict key-value pairs.
681
682        Returns:
683            An iterable object.
684        """
685        return self._cells.items()
686
687    def update(self, cells):
688        """
689        Update the CellDict by overwriting the existing keys with the key-value pairs from a mapping or an iterable.
690
691        Args:
692            cells (iterable): An iterable of key-value pairs of (key, Cell), the type of key-value pairs is
693                              (string, Cell); Or a mapping(dictionary) from string to Cell.
694                              The type of Cell can not be CellDict, CellList or SequentialCell.
695                              The key can not be same with the attributes of class Cell, can not contain '.',
696                              can not be an empty string.
697
698        Note:
699            If the `cells` is a CellDict, an OrderedDict or an iterable containing key-value pairs,
700            the order of newly added elements is maintained.
701
702        Raises:
703            TypeError: If `cells` is not an iterable object.
704            TypeError: If key-value pairs in `cells` are not iterable objects.
705            ValueError: If the length of key-value pairs in `cells` is not 2.
706            TypeError: If the cell in `cells` is None.
707            TypeError: If the type of cell in `cells` is not Cell.
708            TypeError: If the type of cell in `cells` is CellDict, CellList or SequentialCell.
709            TypeError: If the type of key in `cells` is not string.
710            KeyError: If the key in `cells` is same with the attributes of class Cell.
711            KeyError: If the key in `cells` contain ".".
712            KeyError: If the key in `cells` is an empty string.
713        """
714        if not isinstance(cells, abc.Iterable):
715            raise TypeError("CellDict.update() should be called with an "
716                            "iterable of key-value pairs, but got " +
717                            type(cells).__name__)
718        if isinstance(cells, (OrderedDict, CellDict, abc.Mapping)):
719            for key, cell in cells.items():
720                self[key] = cell
721        else:
722            for id, k_v in enumerate(cells):
723                if not isinstance(k_v, abc.Iterable):
724                    raise TypeError("CellDict update sequence element "
725                                    "#" + str(id) + " should be Iterable; but got " +
726                                    type(k_v).__name__)
727                if len(k_v) != 2:
728                    raise ValueError("CellDict update sequence element "
729                                     "#" + str(id) + ", length should be 2; but has length " +
730                                     str(len(k_v)))
731                self[k_v[0]] = k_v[1]
732
733    def construct(self, *inputs):
734        raise NotImplementedError
735