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

Y
Yu Yang 已提交
5
__all__ = ['Block', 'Variable', 'Program', 'Operator']
Y
Yu Yang 已提交
6 7


F
fengjiayi 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
def get_all_op_protos():
    """
    Get all registered op proto from PaddlePaddle C++ end.
    :return: A list of registered OpProto.
    """
    protostrs = core.get_all_op_protos()
    ret_values = []
    for pbstr in protostrs:
        op_proto = framework_pb2.OpProto.FromString(str(pbstr))
        ret_values.append(op_proto)
    return ret_values


class OpProtoHolder(object):
    @classmethod
    def instance(cls):
        if not hasattr(cls, '_instance'):
            cls._instance = cls()
        return cls._instance

    def __init__(self):
        assert not hasattr(
            self.__class__,
            '_instance'), 'Please use `instance()` to get OpProtoHolder opject!'
        op_protos = get_all_op_protos()
        self.op_proto_map = {}
        for proto in op_protos:
            sefl.op_proto_map[proto.type] = proto

    def get_op_proto(self, type):
        assert type in self.op_proto_map, "Operator with type \"%s\" has not been registered." % type
        return self.op_proto_map[type]


Y
Yu Yang 已提交
42 43 44 45 46 47 48
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_()
F
fengjiayi 已提交
49
        self.desc = self.block.desc.new_var(name)
Y
Yu Yang 已提交
50 51

        if shape is not None:
F
fengjiayi 已提交
52
            self.desc.set_shape(shape)
Y
Yu Yang 已提交
53 54 55

        if dtype is not None:
            # TODO(yuyang18): Convert dtype from numpy.dtype
F
fengjiayi 已提交
56
            self.desc.set_data_type(dtype)
Y
Yu Yang 已提交
57 58 59

        if lod_level is not None:
            # TODO(yuyang18): set_lod_level is not defined.
F
fengjiayi 已提交
60
            self.desc.set_lod_level(lod_level)
Y
Yu Yang 已提交
61 62

        self.block.vars[name] = self
Y
Yu Yang 已提交
63
        self.op = None
Y
Yu Yang 已提交
64 65 66 67 68 69 70 71

    # TODO(yuyang18): Get methods

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

Y
Yu Yang 已提交
72

Y
Yu Yang 已提交
73
class Operator(object):
F
Update  
fengjiayi 已提交
74
    def __init__(self, block, desc, type, inputs=None, outputs=None,
Y
Yu Yang 已提交
75 76
                 attrs=None):
        self.block = block
F
Update  
fengjiayi 已提交
77 78 79
        self.desc = desc
        self.proto = OpProtoHolder.instance().get_op_proto(type)
        self.desc.set_type(type)
F
Update  
fengjiayi 已提交
80

Y
Yu Yang 已提交
81
        if inputs is not None:
F
Update  
fengjiayi 已提交
82
            for in_proto in self.proto.inputs:
F
Update  
fengjiayi 已提交
83 84 85 86 87 88 89 90 91 92 93
                in_argus = inputs[in_proto.name]
                if not isinstance(in_argus, list):
                    in_argus = [in_argus]
                if not in_proto.duplicable and len(in_argus) > 1:
                    raise ValueError(
                        "Input %s expects only one input, but %d are given." %
                        (in_proto.name, len(in_argus)))
                in_argu_names = []
                for argu in in_argus:
                    in_argu_names.append(argu.name())
                self.desc.set_input(in_proto.name, in_argu_names)
F
Update  
fengjiayi 已提交
94

Y
Yu Yang 已提交
95
        if outputs is not None:
F
Update  
fengjiayi 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108
            for out_proto in self.proto.outputs:
                out_argus = outputs[out_proto.name]
                if not isinstance(out_argus, list):
                    out_argus = [out_argus]
                if not out_proto.duplicable and len(out_argus) > 1:
                    raise ValueError(
                        "Output %s expects only one output, but %d are given." %
                        (out_proto.name, len(out_argus)))
                out_argu_names = []
                for argu in out_argus:
                    out_argu_names.append(argu.name())
                self.desc.set_output(out_proto.name, out_argu_names)

Y
Yu Yang 已提交
109
        if attrs is not None:
F
Update  
fengjiayi 已提交
110 111 112 113 114 115 116 117
            for attr in self.proto.attrs:
                attr_name = attr.name
                if not attr_name in attrs:
                    continue
                if not isinstance(attrs[attr_name], Block):
                    self.desc.set_attr(attr_name, attrs[attr_name])
                else:
                    self.desc.set_block_attr(attr_name, attrs[attr_name].desc)
Y
Yu Yang 已提交
118 119 120 121

        # TODO: Getters


Y
Yu Yang 已提交
122 123
class Block(object):
    def __init__(self, program, idx):
F
fengjiayi 已提交
124
        self.desc = program.desc.block(idx)
Y
Yu Yang 已提交
125
        self.vars = dict()  # var_name --> var
Y
Yu Yang 已提交
126
        self.ops = collections.deque()  # operator list
Y
Yu Yang 已提交
127 128 129 130
        self.program = program

    @property
    def parent_idx(self):
F
fengjiayi 已提交
131
        return self.desc.parent
Y
Yu Yang 已提交
132 133 134

    @property
    def idx(self):
F
fengjiayi 已提交
135
        return self.desc.id
Y
Yu Yang 已提交
136

Y
Yu Yang 已提交
137 138 139
    def create_var(self, *args, **kwargs):
        return Variable(self, *args, **kwargs)

Y
Yu Yang 已提交
140
    def append_op(self, *args, **kwargs):
F
fengjiayi 已提交
141
        op_desc = self.desc.append_op()
F
Update  
fengjiayi 已提交
142
        op = Operator(self, op_desc, *args, **kwargs)
Y
Yu Yang 已提交
143 144 145 146
        self.ops.append(op)
        return op

    def prepend_op(self, *args, **kwargs):
F
fengjiayi 已提交
147 148
        op_desc = self.desc.prepend_op()
        op = Operator(self, op_desc, *args, **kwargs)
Y
Yu Yang 已提交
149 150 151
        self.ops.appendleft(op)
        return op

Y
Yu Yang 已提交
152 153

class Program(object):
Y
Yu Yang 已提交
154 155 156 157 158 159 160 161
    @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 已提交
162
    def __init__(self):
Y
Yu Yang 已提交
163 164
        assert not hasattr(self.__class__,
                           '_instance'), 'Do not call constructor directly!'
F
fengjiayi 已提交
165
        self.desc = core.ProgramDesc.instance()
Y
Yu Yang 已提交
166 167 168 169 170 171 172 173 174 175 176
        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)
F
fengjiayi 已提交
177
        self.desc.append_block(self.current_block().desc)
Y
Yu Yang 已提交
178 179 180 181 182 183 184 185 186
        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 已提交
187
g_program = Program.instance()