| ## @package net_builder | |
| # Module caffe2.python.net_builder | |
| from __future__ import absolute_import | |
| from __future__ import division | |
| from __future__ import print_function | |
| from __future__ import unicode_literals | |
| from caffe2.python import core, context | |
| from caffe2.python.task import Task, TaskGroup | |
| from caffe2.python.control_ops_util import add_if_op, add_while_op | |
| @context.define_context() | |
| class NetBuilder(object): | |
| """ | |
| Scope-driven mechanism for building nets, loops and conditional blocks. | |
| Arguments: | |
| name: NetBuilder's name | |
| initial_scope: list of blobs that are available for reading/writing | |
| Example: | |
| from caffe2.python.net_builder import NetBuilder, ops | |
| with NetBuilder() as nb: | |
| c = ops.Const(5) | |
| d = ops.Const(0) | |
| with ops.loop(): | |
| ops.stop_if(ops.LE([c, ops.Const(0)])) | |
| ops.Add([c, ops.Const(-1)], [c]) | |
| with ops.If(ops.GE([c, ops.Const(3)])): | |
| ops.Add([d, ops.Const(10)], [d]) | |
| ops.Print(c, []) | |
| ops.Print(d, []) | |
| step = core.to_execution_step(nb) | |
| """ | |
| def __init__(self, name=None, initial_scope=None, _stop_blob_required=False, | |
| _stop_blob=None, _fullname=None, _use_control_ops=False): | |
| parent = NetBuilder.current(required=False) | |
| assert not _fullname or not name, 'Cannot set both _fullname and name' | |
| assert not _use_control_ops or \ | |
| (not _stop_blob_required and not _stop_blob), \ | |
| 'Stop blobs are not used with control operators' | |
| self.name = _fullname or '/'.join( | |
| n for n in (parent.name if parent else None, name) if n | |
| ) | |
| self._frozen = False | |
| self._current_net = None | |
| self._children = [] | |
| if parent: | |
| # make sure parent has an up to date lexical scope computed | |
| parent._update_lexical_scope() | |
| self._init_lexical_scope = set(parent._lexical_scope) if parent else set() | |
| if initial_scope: | |
| self._init_lexical_scope |= set([str(b) for b in initial_scope]) | |
| self._lexical_scope = set(self._init_lexical_scope) | |
| self._stop_blob = _stop_blob | |
| self._stop_blob_required = _stop_blob_required | |
| self._use_control_ops = _use_control_ops | |
| def stop_blob(self): | |
| """ | |
| Returns the BlobReference to the stop_blob of this NetBuilder. | |
| If one is not yet available, creates one. | |
| This function assumes that the stop_blob() will be used immediatelly | |
| in the current net, so it doesn't initialize it if the current net is | |
| the first of the builder. | |
| """ | |
| assert not self._use_control_ops, \ | |
| 'Stop blobs are not used with control operators' | |
| if self._stop_blob is None: | |
| net = self.current_net() | |
| self._stop_blob = core.BlobReference( | |
| net.NextName('stop_blob'), net=net) | |
| net.Const(False, blob_out=self._stop_blob) | |
| if self._current_net != self._children[0]: | |
| self._children.insert(0, core.Net('stop_blob_init')) | |
| self._children[0].Const(False, blob_out=self._stop_blob) | |
| return self._stop_blob | |
| def stop_if(self, blob): | |
| assert not self._use_control_ops, \ | |
| 'Stop blobs are not used with control operators' | |
| stop_blob = self.stop_blob() | |
| ops.Or([stop_blob, blob], [stop_blob]) | |
| self._current_net = None | |
| def _assert_mutable(self): | |
| assert not self._frozen, ( | |
| 'This NetBuilder (%s) has been built already.' % self.name) | |
| def _update_lexical_scope(self): | |
| """ | |
| Updates lexical scope based on the current list of children. | |
| Lexical scope contains names of blobs that are currently available | |
| and were introduced in the net builder | |
| """ | |
| self._lexical_scope = set(self._init_lexical_scope) | |
| for child in self._children: | |
| if isinstance(child, core.Net): | |
| self._lexical_scope |= child.UsedBlobNames() | |
| elif isinstance(child, NetBuilder) and child._use_control_ops: | |
| self._lexical_scope |= child._lexical_scope | |
| def _reset_children(self): | |
| self._current_net = None | |
| self._children = [] | |
| self._lexical_scope = set(self._init_lexical_scope) | |
| def add(self, child): | |
| self._assert_mutable() | |
| if self._use_control_ops: | |
| assert isinstance(child, core.Net) or ( | |
| isinstance(child, NetBuilder) and child._use_control_ops), \ | |
| "Expected Net or NetBuilder with control ops" | |
| self._current_net = None | |
| self._children.append(child) | |
| # to-do : check it's not a dag net | |
| if isinstance(child, core.Net): | |
| self._current_net = child | |
| self._update_lexical_scope() | |
| return child | |
| def current_net(self, name=None): | |
| self._assert_mutable() | |
| if self._current_net is None or name is not None: | |
| self.add(core.Net(name)) | |
| return self._current_net | |
| def freeze(self): | |
| for child in self._children: | |
| if hasattr(child, 'freeze'): | |
| child.freeze() | |
| self._current_net = None | |
| self._frozen = True | |
| def get(self): | |
| self.freeze() | |
| return self._children | |
| def __exit__(self, etype, *args): | |
| if self._use_control_ops and len(self._children) > 0: | |
| _children = self._children | |
| self._reset_children() | |
| merged_net = NetBuilder.merge_nets( | |
| _children, self._lexical_scope) | |
| assert merged_net, "Expected a non-empty merge of children" | |
| self._children = [merged_net] | |
| self.freeze() | |
| if etype is not None: | |
| return | |
| assert (not self._stop_blob_required) or self._stop_blob is not None, ( | |
| 'This NetBuilder (%s) requires a stop condition ' % self.name + | |
| 'to be set with `stop` or `stop_if`') | |
| @staticmethod | |
| def merge_nets(nets_or_builders, outer_blob_names): | |
| # Only nets or builders with control ops are allowed. | |
| # Need to pay attention to external outputs, e.g. | |
| # ... | |
| # IfNet1 (cond_blob): | |
| # (Net1) | |
| # X = 1 | |
| # IfNet2 (...): | |
| # X = X + 1 | |
| # ... | |
| # In this example there're two children in then branch of IfNet1: | |
| # a subnet Net1 that creates blob X and sets its value to one, and | |
| # a net builder IfNet2 that (conditionally) increments X. | |
| # From IfNet2's point of view X is an external input | |
| # and output blob, it will be put into IfNet2 net's external_output. | |
| # At the same time, from the point of view of IfNet1 X is purely local. | |
| # Net.AppendNet just merges external outputs of the networks, so | |
| # without checking this the result of Net1.AppendNet(IfNet2's net) | |
| # would have blob X in external_output | |
| net = None | |
| for n in nets_or_builders: | |
| cur = None | |
| if isinstance(n, NetBuilder): | |
| assert n._use_control_ops, \ | |
| "Merging of NetBuilder supported only for control ops" | |
| nets = n.get() | |
| assert len(nets) == 1 and isinstance(nets[0], core.Net), \ | |
| "Invalid control op net builder" | |
| cur = nets[0] | |
| else: | |
| assert isinstance(n, core.Net) | |
| cur = n | |
| if net: | |
| net.AppendNet(cur) | |
| else: | |
| net = cur | |
| if net: | |
| # correct external output | |
| external_outputs = [o for o in net.Proto().external_output | |
| if o in outer_blob_names] | |
| net.Proto().external_output[:] = external_outputs | |
| return net | |
| def __str__(self): | |
| return self.name or 'Un-named NetBuilder' | |
| class Operations(object): | |
| """ | |
| Operations to be used in the context of a NetBuilder. | |
| """ | |
| def net(self, net=None, name=None): | |
| """ | |
| Retrieves the current net, or add a new net to the builder. | |
| Args: | |
| net: If provided, add the given net to the active builder. | |
| Else, returns the current Net or creates a new one as needed. | |
| name: if provided, creates a new Net with given name and makes | |
| it the new current net of the active builder. Cannot | |
| be provided if net is provided. | |
| """ | |
| assert name is None or net is None, ( | |
| 'Cannot provide both `net` and `name`.') | |
| if net is not None: | |
| NetBuilder.current().add(net) | |
| return net | |
| return NetBuilder.current().current_net(name=name) | |
| def __getattr__(self, op_type): | |
| """ | |
| Adds an operator call to the currently active Net. | |
| """ | |
| if op_type.startswith('__'): | |
| raise AttributeError() | |
| # We want hasattr to work properly even if no context is active. | |
| if NetBuilder.current(required=False) is None: | |
| raise AttributeError('No active NetBuilder.') | |
| return getattr(self.net(), op_type) | |
| def task_group(self): | |
| """ | |
| Creates a local task group which will execute as the next step of | |
| the current NetBuilder. | |
| """ | |
| from caffe2.python import task | |
| group = NetBuilder.current() | |
| with task.Cluster(): | |
| with task.Node('local'): | |
| tg = task.TaskGroup() | |
| group.add(tg) | |
| return tg | |
| def stop(self): | |
| """ | |
| Stop execution of the current execution step. | |
| Example: | |
| ops.Print(a, 0) | |
| ops.stop() | |
| ops.Print(b, 0) | |
| In the example, 'b' will never be printed. | |
| """ | |
| return self.stop_if(ops.Const(True)) | |
| def stop_if(self, blob): | |
| """ | |
| Stop execution of the current execution step if the | |
| condition `blob` is met. | |
| Example: | |
| ops.Print(a, 0) | |
| ops.stop_if(ops.LE([x, ops.Const(0)])) | |
| ops.Print(b, 0) | |
| In the example, 'b' will only be printed if the value of scalar | |
| tensor 'x' is greater than 0. | |
| """ | |
| return NetBuilder.current().stop_if(blob) | |
| def loop(self, iters=None, name=None): | |
| """ | |
| Creates a NetBuilder that will execute in a loop as the next step of | |
| the current NetBuilder. If `iters` is provided, the loop will execute | |
| for `iters` iterations and then stop. `iters` can be a constant or a | |
| BlobReference. If `iters` is not provided, the loop will execute | |
| until `ops.stop` or `ops.stop_if` is called. | |
| Examples: | |
| a = ops.Const(5) | |
| with ops.loop(): | |
| ops.stop_if(ops.LE([a, ops.Const(0)])) | |
| ops.Print(a, 0) | |
| ops.Add([a, ops.Const(-1)], [a]) | |
| Above, 'a' will be printed 5 times, with values 5 to 1. | |
| with ops.loop(10) as loop: | |
| ops.LogInfo(loop.iter()) | |
| This will print the numbers from 0 to 9. | |
| x = ops.Add([ops.Const(10), ops.Const(10)]) | |
| with ops.loop(x) as loop: | |
| ops.LogInfo(loop.iter()) | |
| This will print the numbers from 0 to 19. | |
| """ | |
| return NetBuilder.current().add(_Loop(iters, name=name)) | |
| def stop_guard(self, has_stopped_blob=None, name=None): | |
| """ | |
| Creates a NetBuilder that will execute once as the next step of the | |
| current NetBuilder. After execution, a bool tensor will indicate | |
| whether the inner execution was halted with `stop` or `stop_if`. | |
| Example: | |
| a = ops.Const(True) | |
| with ops.stop_guard() as sg1: | |
| ops.stop_if(a) | |
| ops.Print(ops.Const('did not stop')) | |
| b = ops.Const(False) | |
| with ops.stop_guard() as sg2: | |
| ops.stop_if(b) | |
| ops.Print(ops.Const('did not stop')) | |
| ops.Print(sg1.has_stopped(), []) | |
| ops.Print(sg2.has_stopped(), []) | |
| In the example, 'did not stop' will be printed once, | |
| followed by True and False. | |
| """ | |
| return NetBuilder.current().add( | |
| _StopGuard(has_stopped_blob=has_stopped_blob, name=name)) | |
| def If(self, cond, name=None): | |
| """ | |
| Creates a NetBuilder that will execute once as the next step of the | |
| current NetBuilder if the blob `cond` is True. | |
| Example: | |
| with ops.If(ops.Const(True)): | |
| ops.Print(ops.Const('Will print')) | |
| with ops.If(ops.Const(False)): | |
| ops.Print(ops.Const('Wont print')) | |
| The example will print 'Will print' once. | |
| """ | |
| return NetBuilder.current().add(_RunIf(cond, name=name)) | |
| def IfNet(self, cond, name=None): | |
| """ | |
| Same as If, but uses 'If' operator instead of execution step logic | |
| """ | |
| return NetBuilder.current().add(_RunIfNet(cond, name=name)) | |
| def Else(self, name=None): | |
| """ | |
| Else branch of IfNet, has to be specified immediately after IfNet. | |
| Example: | |
| with ops.IfNet(ops.LT([x, y])): | |
| ... | |
| with ops.Else(): | |
| ... | |
| """ | |
| return _RunElseNet(name=name) | |
| def WhileNet(self, name=None): | |
| """ | |
| NetBuilder for 'While' control operator | |
| """ | |
| return NetBuilder.current().add(_RunWhileNet(name=name)) | |
| def Condition(self, name=None): | |
| """ | |
| Loop's condition, executed within WhileNet context | |
| """ | |
| assert isinstance(NetBuilder.current(), _RunWhileNet), \ | |
| "Use of Condition outside of WhileNet" | |
| return _RunWhileCondition(name=name) | |
| def task_init(self): | |
| """ | |
| Defines operations that will be executed once at task startup. | |
| Useful when implementing processors, that don't have access to the Task | |
| top-level structure. | |
| This setup will be run only once, even if multiple instances of the task | |
| will run in parallel. For instance-local initialization, use | |
| `task_instance_init` instead. | |
| Example: | |
| def my_processor(rec): | |
| with ops.task_init(): | |
| one = ops.Const(1) | |
| two = ops.Const(1) | |
| return Tuple( | |
| ops.Add(rec[0](), zero), ops.Add(rec[1](), two)) | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.INIT) | |
| self.net().add_attribute(Task.TASK_SETUP, setup) | |
| return setup | |
| def task_exit(self): | |
| """ | |
| Define operations to be executed once at task shutdown. | |
| Useful when implementing processors, that don't have access to the Task | |
| top-level structure. | |
| This shutdown will be run only once, after all concurrent instances of | |
| the task have already finished. For instance-local shutdown, | |
| use `task_instance_exit` instead. | |
| Example: | |
| def read_queue(queue): | |
| with ops.task_exit(): | |
| queue.close(ops.net()) | |
| return queue.read(ops.net()) | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.EXIT) | |
| self.net().add_attribute(Task.TASK_SETUP, setup) | |
| return setup | |
| def task_instance_init(self): | |
| """ | |
| Defines operations that will be executed once at startup of each | |
| instance of a task. This can be seen as "thread_local" initialization. | |
| It is guaranteed to run only after all `task_init` logic finishes. | |
| This setup will be run concurrently for each instance of a task. | |
| For global task initialization, use `task_init` instead. | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.INIT) | |
| self.net().add_attribute(Task.TASK_INSTANCE_SETUP, setup) | |
| return setup | |
| def task_instance_exit(self): | |
| """ | |
| Defines operations that will be executed once at shutdown of each | |
| instance of a task. This can be seen as "thread_local" finalization. | |
| This shutdown will be run concurrently for each instance of a task. | |
| For global task shutdown, use `task_exit` instead. | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.EXIT) | |
| self.net().add_attribute(Task.TASK_INSTANCE_SETUP, setup) | |
| return setup | |
| def local_init(self): | |
| """ | |
| Similar to `task_init`, but executes at TaskGroup's startup instead, | |
| before any task of the group starts executing. This will run only | |
| once on each node, before initialization of any task, so it can be | |
| used e.g. to initialize blobs shared across tasks. | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.INIT) | |
| self.net().add_attribute(TaskGroup.LOCAL_SETUP, setup) | |
| return setup | |
| def local_exit(self, name=None): | |
| """ | |
| Similar to `task_exit`, but executes at TaskGroup's exit instead, | |
| after all tasks of the group finished execution. | |
| This will run only once on each node. | |
| """ | |
| setup = _SetupBuilder(_SetupBuilder.EXIT, name) | |
| self.net().add_attribute(TaskGroup.LOCAL_SETUP, setup) | |
| return setup | |
| def task_reporter(self, interval_ms=1000, name=None): | |
| """ | |
| Define operations to be executed at every time interval from | |
| task start-up to finish. These operations are guaranteed to | |
| execute at least once after all other operations of the task are | |
| finished. | |
| Example: | |
| with ops.task_reporter(interval_ms=10000): | |
| ops.LogInfo('10s elapsed') | |
| """ | |
| return _ReporterBuilder(interval_ms, net=self.net(), name=name) | |
| def local_reporter(self, interval_ms=1000, name=None): | |
| """ | |
| Similar to task_report, but operations defined within this block | |
| will run repeatedly for as long as any of the tasks in the current | |
| TaskGroup have not finished. | |
| """ | |
| return _ReporterBuilder(interval_ms, name=name) | |
| ops = Operations() | |
| class _ReporterBuilder(NetBuilder): | |
| def __init__(self, interval_ms, net=None, name=None): | |
| NetBuilder.__init__(self, name) | |
| self._net = net | |
| self.interval_ms = interval_ms | |
| def __exit__(self, etype, *args): | |
| if etype is None: | |
| step = core.to_execution_step(self) | |
| step.RunEveryMillis(self.interval_ms) | |
| if self._net: | |
| self._net.add_attribute(Task.REPORT_STEP, step) | |
| else: | |
| TaskGroup.current().report_step( | |
| step, interval_ms=self.interval_ms) | |
| NetBuilder.__exit__(self, etype, *args) | |
| class _SetupBuilder(NetBuilder): | |
| INIT = 'init' | |
| EXIT = 'exit' | |
| def __init__(self, type, name=None): | |
| NetBuilder.__init__(self, name) | |
| self.type = type | |
| def setup(self, net): | |
| if self.type == _SetupBuilder.INIT: | |
| return core.to_execution_step(self) | |
| def exit(self, net): | |
| if self.type == _SetupBuilder.EXIT: | |
| return core.to_execution_step(self) | |
| class _RunOnce(NetBuilder): | |
| def __init__(self, name=None): | |
| NetBuilder.__init__(self, name) | |
| def __exit__(self, etype, *args): | |
| if etype is None and self._stop_blob is not None: | |
| ops.stop() | |
| NetBuilder.__exit__(self, etype, *args) | |
| class _StopGuard(_RunOnce): | |
| def __init__(self, has_stopped_blob=None, name=None): | |
| _RunOnce.__init__(self, name) | |
| self._stopped = has_stopped_blob | |
| self._ran = False | |
| def __enter__(self): | |
| r = _RunOnce.__enter__(self) | |
| self._stopped = ops.Const(True, blob_out=self._stopped) | |
| return r | |
| def __exit__(self, etype, *args): | |
| if etype is None: | |
| self._ran = True | |
| ops.Const(False, blob_out=self._stopped) | |
| _RunOnce.__exit__(self, etype, *args) | |
| def has_stopped(self): | |
| """ | |
| Return a blob that will be set to scalar bool `True` after | |
| this net builder ran, iff it was halted early. | |
| """ | |
| assert self._ran, 'Context not used yet.' | |
| return self._stopped | |
| class _Loop(NetBuilder): | |
| def __init__(self, iters=None, name=None): | |
| NetBuilder.__init__(self, name, _stop_blob_required=True) | |
| if iters is not None: | |
| self._inc = ops.Const(1) | |
| self._iter = ops.Const(0) | |
| self._num_iters = ( | |
| iters if isinstance(iters, core.BlobReference) | |
| else ops.Const(iters)) | |
| else: | |
| self._num_iters = None | |
| def iter(self): | |
| assert self._num_iters is not None, ( | |
| 'This loop does not have a number of iterations.') | |
| assert self._iter is not None, ( | |
| 'iter() must be called from inside the loop context') | |
| return self._iter | |
| def __enter__(self): | |
| builder = NetBuilder.__enter__(self) | |
| if self._num_iters is not None: | |
| ops.stop_if(ops.GE([self._iter, self._num_iters])) | |
| return builder | |
| def __exit__(self, type, *args): | |
| if type is None and self._num_iters is not None: | |
| self.current_net().Add([self._iter, self._inc], [self._iter]) | |
| NetBuilder.__exit__(self, type, *args) | |
| class _RunIf(_RunOnce): | |
| def __init__(self, cond_blob=None, name=None, _already_ran=None): | |
| _RunOnce.__init__(self, name) | |
| assert cond_blob or _already_ran | |
| self._is_else = cond_blob is None | |
| if _already_ran is None: | |
| self._else_blob = ops.Not(cond_blob) | |
| self._already_ran = ops.Const(False) | |
| else: | |
| self._already_ran = _already_ran | |
| self._else_blob = _already_ran if cond_blob is None else ( | |
| ops.Or([_already_ran, ops.Not(cond_blob)])) | |
| def __enter__(self): | |
| r = _RunOnce.__enter__(self) | |
| ops.stop_if(self._else_blob) | |
| ops.Const(True, blob_out=self._already_ran) | |
| return r | |
| def Elif(self, cond, name=None): | |
| assert not self._is_else, 'Else not allowed for an Else.' | |
| return NetBuilder.current().add(_RunIf( | |
| cond, name=name or self.name, _already_ran=self._already_ran)) | |
| def Else(self, name=None): | |
| assert not self._is_else, 'Elif not allowed for an Else.' | |
| return NetBuilder.current().add( | |
| _RunIf(name=name or self.name, _already_ran=self._already_ran)) | |
| class _RunIfNet(NetBuilder): | |
| """ | |
| Generates a single net that uses If operator | |
| """ | |
| def __init__(self, cond_blob, name=None): | |
| NetBuilder.__init__(self, name=name, _use_control_ops=True) | |
| assert cond_blob, 'Conditional blob is not specified for an If net' | |
| self._cond_blob = cond_blob | |
| self._then_net = None | |
| self._else_net = None | |
| def add(self, child): | |
| return NetBuilder.add(self, child) | |
| def __exit__(self, type, *args): | |
| if type is None: | |
| _then_nets = self._children | |
| self._reset_children() | |
| self._then_net = NetBuilder.merge_nets( | |
| _then_nets, self._lexical_scope) | |
| if not self._then_net: | |
| self._then_net = core.Net('empty_then_net') | |
| if_net = core.Net(self.name + '/if_net') | |
| add_if_op(if_net, self._cond_blob, self._lexical_scope, | |
| self._then_net, self._else_net) | |
| self._current_net = if_net | |
| self._children = [if_net] | |
| NetBuilder.__exit__(self, type, *args) | |
| class _RunElseNet(NetBuilder): | |
| """ | |
| Else branch for _RunIfNet builder | |
| """ | |
| def __init__(self, name=None): | |
| NetBuilder.__init__(self, name=name, _use_control_ops=True) | |
| parent = NetBuilder.current(required=False) | |
| assert parent and len(parent._children) > 0 and \ | |
| isinstance(parent._children[-1], _RunIfNet), \ | |
| 'Invalid use of Else builder' | |
| self._if_builder = parent._children[-1] | |
| def __exit__(self, type, *args): | |
| if type is None: | |
| _else_nets = self._children | |
| self._reset_children() | |
| self._if_builder._else_net = NetBuilder.merge_nets( | |
| _else_nets, self._lexical_scope) | |
| if self._if_builder._else_net: | |
| if_else_net = core.Net(self.name + '/if_else_net') | |
| add_if_op( | |
| if_else_net, | |
| self._if_builder._cond_blob, | |
| self._lexical_scope, | |
| self._if_builder._then_net, | |
| self._if_builder._else_net) | |
| self._if_builder._current_net = if_else_net | |
| self._if_builder._children = [if_else_net] | |
| NetBuilder.__exit__(self, type, *args) | |
| class _RunWhileNet(NetBuilder): | |
| """ | |
| Generates a single net that uses While operator | |
| """ | |
| def __init__(self, name=None): | |
| NetBuilder.__init__(self, name=name, _use_control_ops=True) | |
| self._cond_builder = None | |
| def __exit__(self, type, *args): | |
| if type is None: | |
| assert self._cond_builder, \ | |
| 'Condition builder must be specified in While op' | |
| _cond_blob = self._cond_builder._cond_blob | |
| _cond_net = self._cond_builder._cond_net | |
| loop_body = self._children | |
| self._reset_children() | |
| loop_body_net = NetBuilder.merge_nets( | |
| loop_body, self._lexical_scope) | |
| if not loop_body_net: | |
| loop_body_net = core.Net('empty_loop_body_net') | |
| while_net = core.Net(self.name + '/while_net') | |
| add_while_op(while_net, _cond_blob, self._lexical_scope, | |
| loop_body_net, _cond_net) | |
| self._current_net = while_net | |
| self._children = [while_net] | |
| NetBuilder.__exit__(self, type, *args) | |
| class _RunWhileCondition(NetBuilder): | |
| """ | |
| Computes loop's condition, used in the context of WhileNet. | |
| Last operator must have a single scalar boolean output that will be used | |
| as a condition value, no other blobs created in the condition net are | |
| visible outside of it | |
| """ | |
| def __init__(self, name=None): | |
| NetBuilder.__init__(self, name=name, _use_control_ops=True) | |
| parent = NetBuilder.current(required=False) | |
| assert parent and isinstance(parent, _RunWhileNet), \ | |
| 'Invalid use of loop condition builder' | |
| assert not parent._cond_builder, \ | |
| 'Multiple loop condition builders specified' | |
| assert len(parent._children) == 0, \ | |
| 'Condition definition must be specified before the loop\'s body' | |
| parent._cond_builder = self | |
| self._cond_blob = None | |
| self._cond_net = None | |
| def __exit__(self, type, *args): | |
| if type is None: | |
| condition_body = self._children | |
| self._reset_children() | |
| self._cond_net = NetBuilder.merge_nets( | |
| condition_body, self._lexical_scope) | |
| assert self._cond_net, 'Invalid loop condition specified' | |
| assert len(self._cond_net.Proto().op) > 0, 'Invalid condition net' | |
| last_op = self._cond_net.Proto().op[-1] | |
| assert len(last_op.output) == 1, 'Invalid condition net' | |
| self._cond_blob = core.BlobReference(name=last_op.output[0], net=None) | |
| self._current_net = self._cond_net | |
| self._children = [self._cond_net] | |
| NetBuilder.__exit__(self, type, *args) |