Reroute the end of the tensors in each pair (t0,t1) in ts0 x ts1. This function is the back-bone of the Graph-Editor. It is essentially a thin wrapper on top of the tf.Operation._update_input. Given a pair of tensor t0, t1 in ts0 x ts1, this function re-route the end of t0 and t1 in three
(ts0, ts1, mode, can_modify=None, cannot_modify=None)
| 133 | |
| 134 | |
| 135 | def _reroute_ts(ts0, ts1, mode, can_modify=None, cannot_modify=None): |
| 136 | """Reroute the end of the tensors in each pair (t0,t1) in ts0 x ts1. |
| 137 | |
| 138 | This function is the back-bone of the Graph-Editor. It is essentially a thin |
| 139 | wrapper on top of the tf.Operation._update_input. |
| 140 | |
| 141 | Given a pair of tensor t0, t1 in ts0 x ts1, this function re-route the end |
| 142 | of t0 and t1 in three possible ways: |
| 143 | 1) The reroute mode is "a<->b" or "b<->a": the tensors' end are swapped. After |
| 144 | this operation, the previous consumers of t0 are now consumers of t1 and |
| 145 | vice-versa. |
| 146 | 2) The reroute mode is "a->b": the tensors' end of t0 are re-routed to the |
| 147 | tensors's end of t1 (which are left dangling). After this operation, the |
| 148 | previous consumers of t0 are still consuming t0 but the previous consumers of |
| 149 | t1 are not also consuming t0. The tensor t1 has no consumer. |
| 150 | 3) The reroute mode is "b->a": this mode is the symmetric of the "a->b" mode. |
| 151 | |
| 152 | Note that this function is re-routing the end of two tensors, not the start. |
| 153 | Re-routing the start of two tensors is not supported by this library. The |
| 154 | reason for that is the following: TensorFlow, by design, creates a strong bond |
| 155 | between an op and its output tensor. This Graph editor follows this design and |
| 156 | treats an operation A and its generating tensors {t_i} as an entity which |
| 157 | cannot be broken. In other words, an op cannot be detached from any of its |
| 158 | output tensors, ever. But it is possible to detach an op from its input |
| 159 | tensors, which is what this function concerns itself with. |
| 160 | |
| 161 | Warning: this function is directly manipulating the internals of the tf.Graph. |
| 162 | |
| 163 | Args: |
| 164 | ts0: an object convertible to a list of `tf.Tensor`. |
| 165 | ts1: an object convertible to a list of `tf.Tensor`. |
| 166 | mode: what to do with those tensors: "a->b" or "b<->a" for swaping and |
| 167 | "a->b" or "b->a" for one direction re-routing. |
| 168 | can_modify: iterable of operations which can be modified. Any operation |
| 169 | outside within_ops will be left untouched by this function. |
| 170 | cannot_modify: iterable of operations which cannot be modified. |
| 171 | Any operation within cannot_modify will be left untouched by this |
| 172 | function. |
| 173 | Returns: |
| 174 | The number of individual modifications made by the function. |
| 175 | Raises: |
| 176 | TypeError: if `ts0` or `ts1` cannot be converted to a list of `tf.Tensor`. |
| 177 | TypeError: if `can_modify` or `cannot_modify` is not `None` and cannot be |
| 178 | converted to a list of `tf.Operation`. |
| 179 | """ |
| 180 | a2b, b2a = _RerouteMode.check(mode) |
| 181 | ts0 = _util.make_list_of_t(ts0) |
| 182 | ts1 = _util.make_list_of_t(ts1) |
| 183 | _check_ts_compatibility(ts0, ts1) |
| 184 | if cannot_modify is not None: |
| 185 | cannot_modify = frozenset(_util.make_list_of_op(cannot_modify)) |
| 186 | if can_modify is not None: |
| 187 | can_modify = frozenset(_util.make_list_of_op(can_modify)) |
| 188 | nb_update_inputs = 0 |
| 189 | precomputed_consumers = [] |
| 190 | # precompute consumers to avoid issue with repeated tensors: |
| 191 | for t0, t1 in zip(ts0, ts1): |
| 192 | consumers0 = set(t0.consumers()) |
no test coverage detected