Caffe2 - Python API
A deep learning, cross platform ML framework
control.py
1 
3 """
4 Implement functions for controlling execution of nets and steps, including
5  Do
6  DoParallel
7  For-loop
8  While-loop
9  Do-While-loop
10  Switch
11  If
12 """
13 
14 from __future__ import absolute_import
15 from __future__ import division
16 from __future__ import print_function
17 from __future__ import unicode_literals
18 
19 from caffe2.python import core
20 
21 
22 # Used to generate names of the steps created by the control functions.
23 # It is actually the internal index of these steps.
24 _current_idx = 1
25 _used_step_names = set()
26 
27 
28 def _get_next_step_name(control_name, base_name):
29  global _current_idx, _used_step_names
30  concat_name = '%s/%s' % (base_name, control_name)
31  next_name = concat_name
32  while next_name in _used_step_names:
33  next_name = '%s_%d' % (concat_name, _current_idx)
34  _current_idx += 1
35  _used_step_names.add(next_name)
36  return next_name
37 
38 
39 def _MakeList(input):
40  """ input is a tuple.
41  Example:
42  (a, b, c) --> [a, b, c]
43  (a) --> [a]
44  ([a, b, c]) --> [a, b, c]
45  """
46  if len(input) == 0:
47  raise ValueError(
48  'input cannot be empty.')
49  elif len(input) == 1:
50  output = input[0]
51  if not isinstance(output, list):
52  output = [output]
53  else:
54  output = list(input)
55  return output
56 
57 
58 def _IsNets(nets_or_steps):
59  if isinstance(nets_or_steps, list):
60  return all(isinstance(n, core.Net) for n in nets_or_steps)
61  else:
62  return isinstance(nets_or_steps, core.Net)
63 
64 
65 def _PrependNets(nets_or_steps, *nets):
66  nets_or_steps = _MakeList((nets_or_steps,))
67  nets = _MakeList(nets)
68  if _IsNets(nets_or_steps):
69  return nets + nets_or_steps
70  else:
71  return [Do('prepend', nets)] + nets_or_steps
72 
73 
74 def _AppendNets(nets_or_steps, *nets):
75  nets_or_steps = _MakeList((nets_or_steps,))
76  nets = _MakeList(nets)
77  if _IsNets(nets_or_steps):
78  return nets_or_steps + nets
79  else:
80  return nets_or_steps + [Do('append', nets)]
81 
82 
83 def GetConditionBlobFromNet(condition_net):
84  """
85  The condition blob is the last external_output that must
86  be a single bool
87  """
88  assert len(condition_net.Proto().external_output) > 0, (
89  "Condition net %s must has at least one external output" %
90  condition_net.Proto.name)
91  # we need to use a blob reference here instead of a string
92  # otherwise, it will add another name_scope to the input later
93  # when we create new ops (such as OR of two inputs)
94  return core.BlobReference(condition_net.Proto().external_output[-1])
95 
96 
97 def BoolNet(*blobs_with_bool_value):
98  """A net assigning constant bool values to blobs. It is mainly used for
99  initializing condition blobs, for example, in multi-task learning, we
100  need to access reader_done blobs before reader_net run. In that case,
101  the reader_done blobs must be initialized.
102 
103  Args:
104  blobs_with_bool_value: one or more (blob, bool_value) pairs. The net will
105  assign each bool_value to the corresponding blob.
106 
107  returns
108  bool_net: A net assigning constant bool values to blobs.
109 
110  Examples:
111  - BoolNet((blob_1, bool_value_1), ..., (blob_n, bool_value_n))
112  - BoolNet([(blob_1, net1), ..., (blob_n, bool_value_n)])
113  - BoolNet((cond_1, bool_value_1))
114  """
115  blobs_with_bool_value = _MakeList(blobs_with_bool_value)
116  bool_net = core.Net('bool_net')
117  for blob, bool_value in blobs_with_bool_value:
118  out_blob = bool_net.ConstantFill(
119  [],
120  [blob],
121  shape=[],
122  value=bool_value,
123  dtype=core.DataType.BOOL)
124  bool_net.AddExternalOutput(out_blob)
125 
126  return bool_net
127 
128 
129 def NotNet(condition_blob_or_net):
130  """Not of a condition blob or net
131 
132  Args:
133  condition_blob_or_net can be either blob or net. If condition_blob_or_net
134  is Net, the condition is its last external_output
135  that must be a single bool.
136 
137  returns
138  not_net: the net NOT the input
139  out_blob: the output blob of the not_net
140  """
141  if isinstance(condition_blob_or_net, core.Net):
142  condition_blob = GetConditionBlobFromNet(condition_blob_or_net)
143  else:
144  condition_blob = condition_blob_or_net
145 
146  not_net = core.Net('not_net')
147  out_blob = not_net.Not(condition_blob)
148  not_net.AddExternalOutput(out_blob)
149 
150  return not_net, out_blob
151 
152 
153 def _CopyConditionBlobNet(condition_blob):
154  """Make a condition net that copies the condition_blob
155 
156  Args:
157  condition_blob is a single bool.
158 
159  returns
160  not_net: the net NOT the input
161  out_blob: the output blob of the not_net
162  """
163  condition_net = core.Net('copy_condition_blob_net')
164  out_blob = condition_net.Copy(condition_blob)
165  condition_net.AddExternalOutput(out_blob)
166 
167  return condition_net, out_blob
168 
169 
170 def MergeConditionNets(name, condition_nets, relation):
171  """
172  Merge multi condition nets into a single condition nets.
173 
174  Args:
175  name: name of the new condition net.
176  condition_nets: a list of condition nets. The last external_output
177  of each condition net must be single bool value.
178  relation: can be 'And' or 'Or'.
179 
180  Returns:
181  - A new condition net. Its last external output is relation of all
182  condition_nets.
183  """
184  if not isinstance(condition_nets, list):
185  return condition_nets
186  if len(condition_nets) <= 1:
187  return condition_nets[0] if condition_nets else None
188 
189  merged_net = core.Net(name)
190  for i in range(len(condition_nets)):
191  net_proto = condition_nets[i].Proto()
192  assert net_proto.device_option == merged_net.Proto().device_option
193  assert net_proto.type == merged_net.Proto().type
194  merged_net.Proto().op.extend(net_proto.op)
195  merged_net.Proto().external_input.extend(net_proto.external_input)
196  # discard external outputs as we're combining them together
197  curr_cond = GetConditionBlobFromNet(condition_nets[i])
198  if i == 0:
199  last_cond = curr_cond
200  else:
201  last_cond = merged_net.__getattr__(relation)([last_cond, curr_cond])
202  # merge attributes
203  for k, v in condition_nets[i]._attr_dict.items():
204  merged_net._attr_dict[k] += v
205 
206  merged_net.AddExternalOutput(last_cond)
207 
208  return merged_net
209 
210 
211 def CombineConditions(name, condition_nets, relation):
212  """
213  Combine conditions of multi nets into a single condition nets. Unlike
214  MergeConditionNets, the actual body of condition_nets is not copied into
215  the combine condition net.
216 
217  One example is about multi readers. Each reader net has a reader_done
218  condition. When we want to check whether all readers are done, we can
219  use this function to build a new net.
220 
221  Args:
222  name: name of the new condition net.
223  condition_nets: a list of condition nets. The last external_output
224  of each condition net must be single bool value.
225  relation: can be 'And' or 'Or'.
226 
227  Returns:
228  - A new condition net. Its last external output is relation of all
229  condition_nets.
230  """
231  if not condition_nets:
232  return None
233  if not isinstance(condition_nets, list):
234  raise ValueError('condition_nets must be a list of nets.')
235 
236  if len(condition_nets) == 1:
237  condition_blob = GetConditionBlobFromNet(condition_nets[0])
238  condition_net, _ = _CopyConditionBlobNet(condition_blob)
239  return condition_net
240 
241  combined_net = core.Net(name)
242  for i in range(len(condition_nets)):
243  curr_cond = GetConditionBlobFromNet(condition_nets[i])
244  if i == 0:
245  last_cond = curr_cond
246  else:
247  last_cond = combined_net.__getattr__(relation)(
248  [last_cond, curr_cond])
249 
250  combined_net.AddExternalOutput(last_cond)
251 
252  return combined_net
253 
254 
255 def Do(name, *nets_or_steps):
256  """
257  Execute the sequence of nets or steps once.
258 
259  Examples:
260  - Do('myDo', net1, net2, ..., net_n)
261  - Do('myDo', list_of_nets)
262  - Do('myDo', step1, step2, ..., step_n)
263  - Do('myDo', list_of_steps)
264  """
265  nets_or_steps = _MakeList(nets_or_steps)
266  if (len(nets_or_steps) == 1 and isinstance(
267  nets_or_steps[0], core.ExecutionStep)):
268  return nets_or_steps[0]
269  else:
271  _get_next_step_name('Do', name), nets_or_steps)
272 
273 
274 def DoParallel(name, *nets_or_steps):
275  """
276  Execute the nets or steps in parallel, waiting for all of them to finish
277 
278  Examples:
279  - DoParallel('pDo', net1, net2, ..., net_n)
280  - DoParallel('pDo', list_of_nets)
281  - DoParallel('pDo', step1, step2, ..., step_n)
282  - DoParallel('pDo', list_of_steps)
283  """
284  nets_or_steps = _MakeList(nets_or_steps)
285  if (len(nets_or_steps) == 1 and isinstance(
286  nets_or_steps[0], core.ExecutionStep)):
287  return nets_or_steps[0]
288  else:
290  _get_next_step_name('DoParallel', name),
291  nets_or_steps,
292  concurrent_substeps=True)
293 
294 
295 def _RunOnceIf(name, condition_blob_or_net, nets_or_steps):
296  """
297  Execute nets_or_steps once if condition_blob_or_net evaluates as true.
298 
299  If condition_blob_or_net is Net, the condition is its last external_output
300  that must be a single bool. And this net will be executed before
301  nets_or_steps so as to get the condition.
302  """
303  condition_not_net, stop_blob = NotNet(condition_blob_or_net)
304  if isinstance(condition_blob_or_net, core.Net):
305  nets_or_steps = _PrependNets(
306  nets_or_steps, condition_blob_or_net, condition_not_net)
307  else:
308  nets_or_steps = _PrependNets(nets_or_steps, condition_not_net)
309 
310  def if_step(control_name):
312  _get_next_step_name(control_name, name),
313  nets_or_steps,
314  should_stop_blob=stop_blob,
315  only_once=True,
316  )
317 
318  if _IsNets(nets_or_steps):
319  bool_net = BoolNet((stop_blob, False))
320  return Do(name + '/_RunOnceIf',
321  bool_net, if_step('_RunOnceIf-inner'))
322  else:
323  return if_step('_RunOnceIf')
324 
325 
326 def _RunOnceIfNot(name, condition_blob_or_net, nets_or_steps):
327  """
328  Similar to _RunOnceIf() but Execute nets_or_steps once if
329  condition_blob_or_net evaluates as false.
330  """
331  if isinstance(condition_blob_or_net, core.Net):
332  condition_blob = GetConditionBlobFromNet(condition_blob_or_net)
333  nets_or_steps = _PrependNets(nets_or_steps, condition_blob_or_net)
334  else:
335  copy_net, condition_blob = _CopyConditionBlobNet(condition_blob_or_net)
336  nets_or_steps = _PrependNets(nets_or_steps, copy_net)
337 
339  _get_next_step_name('_RunOnceIfNot', name),
340  nets_or_steps,
341  should_stop_blob=condition_blob,
342  only_once=True,
343  )
344 
345 
346 def For(name, nets_or_steps, iter_num):
347  """
348  Execute nets_or_steps iter_num times.
349 
350  Args:
351  nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
352  a list nets.
353  iter_num: the number times to execute the nets_or_steps.
354 
355  Returns:
356  A ExecutionStep instance.
357  """
358  init_net = core.Net('init-net')
359  iter_cnt = init_net.CreateCounter([], init_count=iter_num)
360  iter_net = core.Net('For-iter')
361  iter_done = iter_net.CountDown([iter_cnt])
362 
363  for_step = core.scoped_execution_step(
364  _get_next_step_name('For-inner', name),
365  _PrependNets(nets_or_steps, iter_net),
366  should_stop_blob=iter_done)
367  return Do(name + '/For',
368  Do(name + '/For-init-net', init_net),
369  for_step)
370 
371 
372 def While(name, condition_blob_or_net, nets_or_steps):
373  """
374  Execute nets_or_steps when condition_blob_or_net returns true.
375 
376  Args:
377  condition_blob_or_net: If it is an instance of Net, its last
378  external_output must be a single bool.
379  nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
380  a list nets.
381 
382  Returns:
383  A ExecutionStep instance.
384  """
385  condition_not_net, stop_blob = NotNet(condition_blob_or_net)
386  if isinstance(condition_blob_or_net, core.Net):
387  nets_or_steps = _PrependNets(
388  nets_or_steps, condition_blob_or_net, condition_not_net)
389  else:
390  nets_or_steps = _PrependNets(nets_or_steps, condition_not_net)
391 
392  def while_step(control_name):
394  _get_next_step_name(control_name, name),
395  nets_or_steps,
396  should_stop_blob=stop_blob,
397  )
398 
399  if _IsNets(nets_or_steps):
400  # In this case, while_step has sub-nets:
401  # [condition_blob_or_net, condition_not_net, nets_or_steps]
402  # If stop_blob is pre-set to True (this may happen when While() is
403  # called twice), the loop will exit after executing
404  # condition_blob_or_net. So we use BootNet to set stop_blob to
405  # False.
406  bool_net = BoolNet((stop_blob, False))
407  return Do(name + '/While', bool_net, while_step('While-inner'))
408  else:
409  return while_step('While')
410 
411 
412 def Until(name, condition_blob_or_net, nets_or_steps):
413  """
414  Similar to While() but execute nets_or_steps when
415  condition_blob_or_net returns false
416  """
417  if isinstance(condition_blob_or_net, core.Net):
418  stop_blob = GetConditionBlobFromNet(condition_blob_or_net)
419  nets_or_steps = _PrependNets(nets_or_steps, condition_blob_or_net)
420  else:
421  stop_blob = core.BlobReference(str(condition_blob_or_net))
422 
424  _get_next_step_name('Until', name),
425  nets_or_steps,
426  should_stop_blob=stop_blob)
427 
428 
429 def DoWhile(name, condition_blob_or_net, nets_or_steps):
430  """
431  Execute nets_or_steps when condition_blob_or_net returns true. It will
432  execute nets_or_steps before evaluating condition_blob_or_net.
433 
434  Args:
435  condition_blob_or_net: if it is an instance of Net, tts last external_output
436  must be a single bool.
437  nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
438  a list nets.
439 
440  Returns:
441  A ExecutionStep instance.
442  """
443  condition_not_net, stop_blob = NotNet(condition_blob_or_net)
444  if isinstance(condition_blob_or_net, core.Net):
445  nets_or_steps = _AppendNets(
446  nets_or_steps, condition_blob_or_net, condition_not_net)
447  else:
448  nets_or_steps = _AppendNets(nets_or_steps, condition_not_net)
449 
450  # If stop_blob is pre-set to True (this may happen when DoWhile() is
451  # called twice), the loop will exit after executing the first net/step
452  # in nets_or_steps. This is not what we want. So we use BootNet to
453  # set stop_blob to False.
454  bool_net = BoolNet((stop_blob, False))
455  return Do(name + '/DoWhile', bool_net, core.scoped_execution_step(
456  _get_next_step_name('DoWhile-inner', name),
457  nets_or_steps,
458  should_stop_blob=stop_blob,
459  ))
460 
461 
462 def DoUntil(name, condition_blob_or_net, nets_or_steps):
463  """
464  Similar to DoWhile() but execute nets_or_steps when
465  condition_blob_or_net returns false. It will execute
466  nets_or_steps before evaluating condition_blob_or_net.
467 
468  Special case: if condition_blob_or_net is a blob and is pre-set to
469  true, then only the first net/step of nets_or_steps will be executed and
470  loop is exited. So you need to be careful about the initial value the
471  condition blob when using DoUntil(), esp when DoUntil() is called twice.
472  """
473  if not isinstance(condition_blob_or_net, core.Net):
474  stop_blob = core.BlobReference(condition_blob_or_net)
476  _get_next_step_name('DoUntil', name),
477  nets_or_steps,
478  should_stop_blob=stop_blob)
479 
480  nets_or_steps = _AppendNets(nets_or_steps, condition_blob_or_net)
481  stop_blob = GetConditionBlobFromNet(condition_blob_or_net)
482 
483  # If stop_blob is pre-set to True (this may happen when DoWhile() is
484  # called twice), the loop will exit after executing the first net/step
485  # in nets_or_steps. This is not what we want. So we use BootNet to
486  # set stop_blob to False.
487  bool_net = BoolNet((stop_blob, False))
488  return Do(name + '/DoUntil', bool_net, core.scoped_execution_step(
489  _get_next_step_name('DoUntil-inner', name),
490  nets_or_steps,
491  should_stop_blob=stop_blob,
492  ))
493 
494 
495 def Switch(name, *conditions):
496  """
497  Execute the steps for which the condition is true.
498  Each condition is a tuple (condition_blob_or_net, nets_or_steps).
499  Note:
500  1. Multi steps can be executed if their conditions are true.
501  2. The conditions_blob_or_net (if it is Net) of all steps will be
502  executed once.
503 
504  Examples:
505  - Switch('name', (cond_1, net_1), (cond_2, net_2), ..., (cond_n, net_n))
506  - Switch('name', [(cond_1, net1), (cond_2, net_2), ..., (cond_n, net_n)])
507  - Switch('name', (cond_1, net_1))
508  """
509  conditions = _MakeList(conditions)
511  _get_next_step_name('Switch', name),
512  [_RunOnceIf(name + '/Switch', cond, step) for cond, step in conditions])
513 
514 
515 def SwitchNot(name, *conditions):
516  """
517  Similar to Switch() but execute the steps for which the condition is False.
518  """
519  conditions = _MakeList(conditions)
521  _get_next_step_name('SwitchNot', name),
522  [_RunOnceIfNot(name + '/SwitchNot', cond, step)
523  for cond, step in conditions])
524 
525 
526 def If(name, condition_blob_or_net,
527  true_nets_or_steps, false_nets_or_steps=None):
528  """
529  condition_blob_or_net is first evaluated or executed. If the condition is
530  true, true_nets_or_steps is then executed, otherwise, false_nets_or_steps
531  is executed.
532 
533  If condition_blob_or_net is Net, the condition is its last external_output
534  that must be a single bool. And this Net will be executred before both
535  true/false_nets_or_steps so as to get the condition.
536  """
537  if not false_nets_or_steps:
538  return _RunOnceIf(name + '/If',
539  condition_blob_or_net, true_nets_or_steps)
540 
541  if isinstance(condition_blob_or_net, core.Net):
542  condition_blob = GetConditionBlobFromNet(condition_blob_or_net)
543  else:
544  condition_blob = condition_blob_or_net
545 
546  return Do(
547  name + '/If',
548  _RunOnceIf(name + '/If-true',
549  condition_blob_or_net, true_nets_or_steps),
550  _RunOnceIfNot(name + '/If-false', condition_blob, false_nets_or_steps)
551  )
552 
553 
554 def IfNot(name, condition_blob_or_net,
555  true_nets_or_steps, false_nets_or_steps=None):
556  """
557  If condition_blob_or_net returns false, executes true_nets_or_steps,
558  otherwise executes false_nets_or_steps
559  """
560  if not false_nets_or_steps:
561  return _RunOnceIfNot(name + '/IfNot',
562  condition_blob_or_net, true_nets_or_steps)
563 
564  if isinstance(condition_blob_or_net, core.Net):
565  condition_blob = GetConditionBlobFromNet(condition_blob_or_net)
566  else:
567  condition_blob = condition_blob_or_net
568 
569  return Do(
570  name + '/IfNot',
571  _RunOnceIfNot(name + '/IfNot-true',
572  condition_blob_or_net, true_nets_or_steps),
573  _RunOnceIf(name + '/IfNot-false', condition_blob, false_nets_or_steps)
574  )
def GetConditionBlobFromNet(condition_net)
Definition: control.py:83
def NotNet(condition_blob_or_net)
Definition: control.py:129
def Switch(name, conditions)
Definition: control.py:495
def If(name, condition_blob_or_net, true_nets_or_steps, false_nets_or_steps=None)
Definition: control.py:527
def Until(name, condition_blob_or_net, nets_or_steps)
Definition: control.py:412
def MergeConditionNets(name, condition_nets, relation)
Definition: control.py:170
def DoUntil(name, condition_blob_or_net, nets_or_steps)
Definition: control.py:462
def scoped_execution_step(name, args, kwargs)
Definition: core.py:2066
def DoWhile(name, condition_blob_or_net, nets_or_steps)
Definition: control.py:429
def CombineConditions(name, condition_nets, relation)
Definition: control.py:211
def DoParallel(name, nets_or_steps)
Definition: control.py:274
def Do(name, nets_or_steps)
Definition: control.py:255
def BoolNet(blobs_with_bool_value)
Definition: control.py:97
def IfNot(name, condition_blob_or_net, true_nets_or_steps, false_nets_or_steps=None)
Definition: control.py:555
def While(name, condition_blob_or_net, nets_or_steps)
Definition: control.py:372
def SwitchNot(name, conditions)
Definition: control.py:515
def For(name, nets_or_steps, iter_num)
Definition: control.py:346