To support map/reduce operations, I propose two operators: @ for mapping, and % for reducing.
At each @ usage, it would spawn a dimension for the subsequent workflow (assuming a flat workflow is zero-dimensional). The result of using this operator is equivalent to the outer product of the dimensions, each entry being a job parametrization to execute.
job_b.field @= job_a.list_field
it reads as "map each entry from job_a.list_field to job_b.field
The % operator collapses dimensions, transforming their entries back into lists in the original ordering. It is a binary operation, in which the first argument is the entries to transform into a list, and the second argument is the dimensions to collapse.
job_c.field %= job_c.out_field @ job_a.list_field
it reads as "reduce the entries from job_c.out_field over the dimension job_a.list_field to job_c.field
A full example:
rp = ResourcePool()
pieces = lambda path: {'pieces': path.split('/'), 'indexes': range(path.count('/'))}
uppercase = lambda text: {'text': text.upper()}
indexed_uppercase = lambda text, index: {'text': `{index}-{text.upper()}`}
join = lambda pieces: {'text': '/'.join(pieces)}
job_pieces = PythonJob(function=pieces, reference='pieces_job')
job_pieces['path'] = Resource('usr/lib/libgimp.so')
job_uppercase = PythonJob(function=uppercase, reference='uppercase_job')
job_uppercase['text'] @= job_pieces.pieces
job_join = PythonJob(function=join, reference='join_job')
job_join['pieces'] %= job_uppercase.text @ job_pieces.pieces
rp[R('text')] = job_join.text
rp = DependencySolver(rp).execute(executor=Execution())
As to easy the mnemonics, one can name its dimensions, so when using the reduce operator, one can simply use the dimension name instead of having to refer to the original job:
job_uppercase['text'] @= job_pieces.pieces, 'path_pieces'
job_join['pieces'] %= job_uppercase.text @ 'path_pieces'
There are situations in which one might want to link fields in the same dimension. By providing a tuple of several fields, it will be mapped to the list of fields in the selector:
job_uppercase = PythonJob(function=indexed_uppercase, reference='uppercase_job')
job_uppercase[['text', 'index']] @= (job_pieces.pieces, job_pieces.indexes), 'path_pieces'
# All these reducing operators execute the same operation
job_join['pieces'] %= job_uppercase.text @ 'path_pieces'
job_join['pieces'] %= job_uppercase.text @ (job_pieces.pieces, job_pieces.indexes)
job_join['pieces'] %= job_uppercase.text @ (job_pieces.pieces)
To support map/reduce operations, I propose two operators:
@for mapping, and%for reducing.At each
@usage, it would spawn a dimension for the subsequent workflow (assuming a flat workflow is zero-dimensional). The result of using this operator is equivalent to the outer product of the dimensions, each entry being a job parametrization to execute.it reads as "map each entry from
job_a.list_fieldtojob_b.fieldThe
%operator collapses dimensions, transforming their entries back into lists in the original ordering. It is a binary operation, in which the first argument is the entries to transform into a list, and the second argument is the dimensions to collapse.it reads as "reduce the entries from
job_c.out_fieldover the dimensionjob_a.list_fieldtojob_c.fieldA full example:
As to easy the mnemonics, one can name its dimensions, so when using the reduce operator, one can simply use the dimension name instead of having to refer to the original job:
There are situations in which one might want to link fields in the same dimension. By providing a tuple of several fields, it will be mapped to the list of fields in the selector: