parameters: (*, operation, condition, max_iteration=10)
return_statement: return run_loop
docstring: Generate and run a chain of operations such that condition evaluates True.
  
      Returns and operation instance that acts like a single node in the current
      work graph, but which is a proxy to the operation at the end of a dynamically generated chain
      of operations. At run time, condition is evaluated for the last element in
      the current chain. If condition evaluates False, the chain is extended and
      the next element is executed. When condition evaluates True, the object
      returned by ``while_loop`` becomes a proxy for the last element in the chain.
  
      Equivalent to calling operation.while(condition), where available.
  
      Arguments:
          operation: a callable that produces an instance of an operation when called with no arguments.
          condition: a callable that accepts an object (returned by ``operation``) that returns a boolean.
          max_iteration: execute the loop no more than this many times (default 10)
  
      Warning:
          *max_iteration* is provided in part to minimize the cost of bugs in early
          versions of this software. The default value may be changed or
          removed on short notice.
  
      Warning:
          The protocol by which ``while_loop`` interacts with ``operation`` and ``condition``
          is very unstable right now. Please refer to this documentation when installing new
          versions of the package.
  
      Protocol:
          Warning:
              This protocol will be changed before the API is finalized.
  
          When called, ``while_loop`` calls ``operation`` without arguments
          and captures the return value for inspection of outputs.
          The object produced by ``operation()`` must have a ``reset``,
          a ``run`` method, and an ``output`` attribute. From gmxapi 0.1, an additional
          ``values`` attribute is examined to advertise the output members that will
          appear on the ``while_loop`` output.
  
          This is inspected
          to determine the output data proxy for the operation produced by the call
          to ``while_loop``. When that operation is called, it does the equivalent of
  
              while(condition(self._operation)):
                  self._operation.reset()
                  self._operation.run()
  
          Then, the output data proxy of ``self`` is updated with the results from
          self._operation.output.
docstring_summary: Generate and run a chain of operations such that condition evaluates True.
docstring_tokens: Generate and run a chain of operations such that condition evaluates True .
function: def while_loop(*, operation, condition, max_iteration=10):
      """Generate and run a chain of operations such that condition evaluates True.
  
      Returns and operation instance that acts like a single node in the current
      work graph, but which is a proxy to the operation at the end of a dynamically generated chain
      of operations. At run time, condition is evaluated for the last element in
      the current chain. If condition evaluates False, the chain is extended and
      the next element is executed. When condition evaluates True, the object
      returned by ``while_loop`` becomes a proxy for the last element in the chain.
  
      Equivalent to calling operation.while(condition), where available.
  
      Arguments:
          operation: a callable that produces an instance of an operation when called with no arguments.
          condition: a callable that accepts an object (returned by ``operation``) that returns a boolean.
          max_iteration: execute the loop no more than this many times (default 10)
  
      Warning:
          *max_iteration* is provided in part to minimize the cost of bugs in early
          versions of this software. The default value may be changed or
          removed on short notice.
  
      Warning:
          The protocol by which ``while_loop`` interacts with ``operation`` and ``condition``
          is very unstable right now. Please refer to this documentation when installing new
          versions of the package.
  
      Protocol:
          Warning:
              This protocol will be changed before the API is finalized.
  
          When called, ``while_loop`` calls ``operation`` without arguments
          and captures the return value for inspection of outputs.
          The object produced by ``operation()`` must have a ``reset``,
          a ``run`` method, and an ``output`` attribute. From gmxapi 0.1, an additional
          ``values`` attribute is examined to advertise the output members that will
          appear on the ``while_loop`` output.
  
          This is inspected
          to determine the output data proxy for the operation produced by the call
          to ``while_loop``. When that operation is called, it does the equivalent of
  
              while(condition(self._operation)):
                  self._operation.reset()
                  self._operation.run()
  
          Then, the output data proxy of ``self`` is updated with the results from
          self._operation.output.
  
      """
      # In the first implementation, Subgraph is NOT and OperationHandle.
      # if not isinstance(obj, AbstractOperationHandle):
      #     raise exceptions.UsageError(
      #     '"operation" key word argument must be a callable that produces an Operation handle.')
      # outputs = {}
      # for name, descriptor in obj.output.items():
      #     outputs[name] = descriptor._dtype
  
      # 1. Get the initial inputs.
      # 2. Initialize the subgraph with the initial inputs.
      # 3. Run the subgraph.
      # 4. Get the outputs.
      # 5. Initialize the subgraph with the outputs.
      # 6. Go to 3 if condition is not met.
  
      obj = operation()
      assert hasattr(obj, 'values')
      outputs = collections.OrderedDict([(key, type(value)) for key, value in obj.values.items()])
  
      @function_wrapper(output=outputs)
      def run_loop(output: OutputCollectionDescription):
          iteration = 0
          obj = operation()
          logger.debug('Created object {}'.format(obj))
          logger.debug(', '.join(['{}: {}'.format(key, obj.values[key]) for key in obj.values]))
          logger.debug('Condition: {}'.format(condition(obj)))
          while (condition(obj)):
              logger.debug('Running iteration {}'.format(iteration))
              # WARNING: This is a dynamically generated task that can break logic based on
              # assumptions about the sequence of execution or resolution of data Futures,
              # if we aren't careful.
              obj.run()
              logger.debug(
                  ', '.join(['{}: {}'.format(key, obj.values[key]) for key in obj.values]))
              logger.debug('Condition: {}'.format(condition(obj)))
              iteration += 1
              if iteration > max_iteration:
                  logger.warning(
                      f'while_loop condition still True after {iteration} iterations. '
                      'If you are confident that the script is behaving correctly, '
                      f'but needs more than {max_iteration} iterations, '
                      'set a higher value for the *max_iteration* argument to `while_loop`'
                  )
                  break
          for name in outputs:
              setattr(output, name, obj.values[name])
  
          return obj
  
      return run_loop
function_tokens: def while_loop ( * , operation , condition , max_iteration = 10 ) : # In the first implementation, Subgraph is NOT and OperationHandle. # if not isinstance(obj, AbstractOperationHandle): #     raise exceptions.UsageError( #     '"operation" key word argument must be a callable that produces an Operation handle.') # outputs = {} # for name, descriptor in obj.output.items(): #     outputs[name] = descriptor._dtype # 1. Get the initial inputs. # 2. Initialize the subgraph with the initial inputs. # 3. Run the subgraph. # 4. Get the outputs. # 5. Initialize the subgraph with the outputs. # 6. Go to 3 if condition is not met. obj = operation ( ) assert hasattr ( obj , 'values' ) outputs = collections . OrderedDict ( [ ( key , type ( value ) ) for key , value in obj . values . items ( ) ] ) @ function_wrapper ( output = outputs ) def run_loop ( output : OutputCollectionDescription ) : iteration = 0 obj = operation ( ) logger . debug ( 'Created object {}' . format ( obj ) ) logger . debug ( ', ' . join ( [ '{}: {}' . format ( key , obj . values [ key ] ) for key in obj . values ] ) ) logger . debug ( 'Condition: {}' . format ( condition ( obj ) ) ) while ( condition ( obj ) ) : logger . debug ( 'Running iteration {}' . format ( iteration ) ) # WARNING: This is a dynamically generated task that can break logic based on # assumptions about the sequence of execution or resolution of data Futures, # if we aren't careful. obj . run ( ) logger . debug ( ', ' . join ( [ '{}: {}' . format ( key , obj . values [ key ] ) for key in obj . values ] ) ) logger . debug ( 'Condition: {}' . format ( condition ( obj ) ) ) iteration += 1 if iteration > max_iteration : logger . warning ( f'while_loop condition still True after {iteration} iterations. ' 'If you are confident that the script is behaving correctly, ' f'but needs more than {max_iteration} iterations, ' 'set a higher value for the *max_iteration* argument to `while_loop`' ) break for name in outputs : setattr ( output , name , obj . values [ name ] ) return obj return run_loop