graph.py 5.9 KB
Newer Older
Y
Yu Yang 已提交
1
import paddle.v2.framework.core as core
Y
Yu Yang 已提交
2
import collections
Y
Yu Yang 已提交
3
import numpy as np
Y
Yu Yang 已提交
4

Y
Yu Yang 已提交
5
__all__ = ['Block', 'Variable', 'Program', 'Operator']
Y
Yu Yang 已提交
6 7 8 9 10 11 12 13 14


class Variable(object):
    def __init__(self, block, name=None, shape=None, dtype=None,
                 lod_level=None):
        self.block = block

        if name is None:
            name = Variable._unique_var_name_()
Y
Yu Yang 已提交
15
        try:
16
            self.desc = self.block.desc.var(name)
Y
Yu Yang 已提交
17 18
            is_new_var = False
        except core.EnforceNotMet:
19
            self.desc = self.block.desc.new_var(name)
Y
Yu Yang 已提交
20
            is_new_var = True
Y
Yu Yang 已提交
21 22

        if shape is not None:
Y
Yu Yang 已提交
23
            if is_new_var:
24
                self.desc.set_shape(shape)
Y
Yu Yang 已提交
25 26 27 28 29 30 31 32
            else:
                old_shape = self.shape
                shape = tuple(shape)
                if shape != old_shape:
                    raise ValueError(
                        "Variable {0} has been created before. the previous "
                        "shape is {1}; the new shape is {2}. They are not "
                        "matched.".format(self.name, old_shape, shape))
Y
Yu Yang 已提交
33
        if dtype is not None:
Y
Yu Yang 已提交
34 35
            if not isinstance(dtype, core.DataType):
                dtype = Variable._convert_np_dtype_to_dtype_(dtype)
Y
Yu Yang 已提交
36
            if is_new_var:
37
                self.desc.set_data_type(dtype)
Y
Yu Yang 已提交
38 39 40 41 42 43 44 45
            else:
                old_dtype = self.data_type()
                if dtype != old_shape:
                    raise ValueError("Variable {0} has been created before. "
                                     "The previous data type is {1}; the new "
                                     "data type is {2}. They are not "
                                     "matched.".format(self.name, old_dtype,
                                                       dtype))
Y
Yu Yang 已提交
46 47

        if lod_level is not None:
Y
Yu Yang 已提交
48
            if is_new_var:
49
                self.desc.set_lod_level(lod_level)
Y
Yu Yang 已提交
50 51 52 53 54 55 56
            else:
                if lod_level != self.lod_level:
                    raise ValueError("Variable {0} has been created before. "
                                     "The previous lod_level is {1}; the new "
                                     "lod_level is {2}. They are not "
                                     "matched".format(self.name, self.lod_level,
                                                      lod_level))
Y
Yu Yang 已提交
57
        self.block.vars[name] = self
Y
Yu Yang 已提交
58
        self.op = None
Y
Yu Yang 已提交
59

Y
Yu Yang 已提交
60 61
    @property
    def name(self):
62
        return self.desc.name()
Y
Yu Yang 已提交
63 64 65 66

    @property
    def shape(self):
        # convert to tuple, make it as same as numpy API.
67
        return tuple(self.desc.shape())
Y
Yu Yang 已提交
68 69 70

    @property
    def data_type(self):
71
        return self.desc.data_type()
Y
Yu Yang 已提交
72 73 74

    @property
    def lod_level(self):
75
        return self.desc.lod_level()
Y
Yu Yang 已提交
76 77 78 79 80 81

    @staticmethod
    def _unique_var_name_():
        uid = core.unique_integer()  # unique during whole process.
        return "_generated_var_%d" % uid

Y
Yu Yang 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    @staticmethod
    def _convert_np_dtype_to_dtype_(np_dtype):
        dtype = np.dtype(np_dtype)
        if dtype == np.float32:
            return core.DataType.FP32
        elif dtype == np.float64:
            return core.DataType.FP64
        elif dtype == np.float16:
            return core.DataType.FP16
        elif dtype == np.int32:
            return core.DataType.INT32
        elif dtype == np.int16:
            return core.DataType.INT16
        elif dtype == np.int64:
            return core.DataType.INT64
        elif dtype == np.bool:
            return core.DataType.BOOL
        else:
            raise ValueError("Not supported numpy dtype " + str(dtype))

Y
Yu Yang 已提交
102

Y
Yu Yang 已提交
103 104 105
class Operator(object):
    def __init__(self,
                 block,
Y
Yu Yang 已提交
106
                 desc,
Y
Yu Yang 已提交
107 108 109 110 111
                 type=None,
                 inputs=None,
                 outputs=None,
                 attrs=None):
        self.block = block
Y
Yu Yang 已提交
112
        self.desc = desc
Y
Yu Yang 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125
        if type is not None:
            # TODO.
            pass
        if inputs is not None:
            # TODO
            pass
        if outputs is not None:
            # TODO
            pass
        if attrs is not None:
            # TODO
            pass

Y
Yu Yang 已提交
126
            # TODO: Getters
Y
Yu Yang 已提交
127 128


Y
Yu Yang 已提交
129 130
class Block(object):
    def __init__(self, program, idx):
Y
Yu Yang 已提交
131
        self.desc = program.desc.block(idx)
Y
Yu Yang 已提交
132
        self.vars = dict()  # var_name --> var
Y
Yu Yang 已提交
133
        self.ops = collections.deque()  # operator list
Y
Yu Yang 已提交
134 135 136 137
        self.program = program

    @property
    def parent_idx(self):
Y
Yu Yang 已提交
138
        return self.desc.parent
Y
Yu Yang 已提交
139 140 141

    @property
    def idx(self):
Y
Yu Yang 已提交
142
        return self.desc.id
Y
Yu Yang 已提交
143

Y
Yu Yang 已提交
144 145 146
    def create_var(self, *args, **kwargs):
        return Variable(self, *args, **kwargs)

Y
Yu Yang 已提交
147
    def append_op(self, *args, **kwargs):
Y
Yu Yang 已提交
148 149
        op_desc = self.desc.append_op()
        op = Operator(self, op_desc, *args, **kwargs)
Y
Yu Yang 已提交
150 151 152 153
        self.ops.append(op)
        return op

    def prepend_op(self, *args, **kwargs):
Y
Yu Yang 已提交
154 155
        op_desc = self.desc.prepend_op()
        op = Operator(self, op_desc, *args, **kwargs)
Y
Yu Yang 已提交
156 157 158
        self.ops.appendleft(op)
        return op

Y
Yu Yang 已提交
159 160

class Program(object):
Y
Yu Yang 已提交
161 162 163 164 165 166 167 168
    @classmethod
    def instance(cls):
        # From https://stackoverflow.com/questions/8212053
        # Making Program as a Singleton class.
        if not hasattr(cls, '_instance'):
            cls._instance = cls()
        return cls._instance

Y
Yu Yang 已提交
169
    def __init__(self):
Y
Yu Yang 已提交
170 171
        assert not hasattr(self.__class__,
                           '_instance'), 'Do not call constructor directly!'
Y
Yu Yang 已提交
172
        self.desc = core.ProgramDesc.instance()
Y
Yu Yang 已提交
173 174 175 176 177 178 179 180 181 182 183
        self.blocks = [Block(self, 0)]
        self.current_block_idx = 0

    def global_block(self):
        return self.blocks[0]

    def current_block(self):
        return self.blocks[self.current_block_idx]

    def create_block(self):
        new_block_idx = len(self.blocks)
Y
Yu Yang 已提交
184
        self.desc.append_block(self.current_block().desc)
Y
Yu Yang 已提交
185 186 187 188 189 190 191 192 193
        self.current_block_idx = new_block_idx
        self.blocks.append(Block(self, self.current_block_idx))
        return self.current_block()

    def rollback(self):
        self.current_block_idx = self.current_block().parent_idx


# program is a global instance.
Y
Yu Yang 已提交
194
g_program = Program.instance()