# coding: utf-8
from sympy import Symbol
from sympy.core.basic import Basic
from sympy.sets.sets import Set
from sympy.sets.sets import FiniteSet
from sympy.sets.sets import Union as sm_Union
from sympy.sets.sets import Intersection as sm_Intersection
from sympy.sets.sets import Complement
from sympy.sets.fancysets import Range as sm_Range
from sympy.sets.fancysets import Naturals
from sympy import Symbol
from sympy.sets.sets import UniversalSet
[docs]class Group(FiniteSet):
"""Represents a group of processes.
processes: Set
set of the processes within the group
rank: int
the rank of the calling process within the group
Examples
>>> from pyccel.ast.parallel.group import Group
>>> g = Group(1, 2, 3, 4)
>>> g
{1, 2, 3, 4}
>>> g.size
4
"""
_comm = None
@property
def size(self):
"""the number of processes in the group."""
return len(self)
@property
def processes(self):
"""Returns the set of the group processes."""
return self._args[0]
@property
def rank(self):
return self._args[1]
@property
def communicator(self):
return self._comm
[docs] def set_communicator(self, comm):
self._comm = comm
[docs] def translate(self, rank1, other, rank2):
"""
This routine takes an array of n ranks (ranks1) which
are ranks of processes in self. It returns in
ranks2 the corresponding ranks of the processes
as they are in other.
MPI_UNDEFINED is returned for processes not in
other
"""
pass
[docs] def compare(self, other):
"""
This routine returns the relationship between self
and other
If self and other contain the same processes,
ranked the same way, this routine returns
MPI_IDENT
If self and other contain the same processes,
but ranked differently, this routine returns
MPI_SIMILAR
Otherwise this routine returns MPI_UNEQUAL.
"""
pass
[docs] def union(self, other):
"""
Returns in newgroup a group consisting
of all processes in self followed by all
processes in other, with no duplication.
Examples
As a shortcut it is possible to use the '+' operator:
>>> from pyccel.ast.parallel.group import Group
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> g1.union(g2)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> g1 + g2
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
"""
return Union(self, other)
[docs] def intersection(self, other):
"""
Returns in newgroup all processes
that are in both groups, ordered as in self.
Examples
>>> from pyccel.ast.parallel.group import Group
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> g1.intersection(g2)
{8, 9}
"""
return Intersection(self, other)
[docs] def difference(self, other):
"""
Returns in newgroup all processes
in self that are not in other, ordered as
in self.
Examples
As a shortcut it is possible to use the '-' operator:
>>> from pyccel.ast.parallel.group import Group
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> g1.difference(g2)
{1, 3, 6, 7}
>>> g1 - g2
{1, 3, 6, 7}
"""
return Difference(self, other)
[docs]class Union(sm_Union):
"""
Represents the union of groups.
Examples
>>> from pyccel.ast.parallel.group import Group, Union
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> Union(g1, g2)
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
"""
def __new__(cls, *args, **kwargs):
u = sm_Union(*args, **kwargs)
return Group(*u.args)
[docs]class Intersection(sm_Intersection):
"""
Represents the intersection of groups.
Examples
>>> from pyccel.ast.parallel.group import Group, Intersection
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> Intersection(g1, g2)
{8, 9}
"""
def __new__(cls, *args, **kwargs):
i = sm_Intersection(*args, **kwargs)
return Group(*i.args)
[docs]class Difference(Complement):
"""
Represents the difference between two groups.
Examples
>>> from pyccel.ast.parallel.group import Group, Difference
>>> g1 = Group(1, 3, 6, 7, 8, 9)
>>> g2 = Group(0, 2, 4, 5, 8, 9)
>>> Difference(g1, g2)
{1, 3, 6, 7}
"""
def __new__(cls, *args, **kwargs):
c = Complement(*args, **kwargs)
return Group(*c.args)
[docs]class Range(sm_Range):
"""
Representes a range of processes.
Examples
>>> from pyccel.ast.parallel.group import Range
>>> from sympy import Symbol
>>> n = Symbol('n')
>>> Range(0, n)
Range(0, n)
"""
def __new__(cls, *args):
_args = []
for a in args:
if isinstance(a, Symbol):
_args.append(0)
else:
_args.append(a)
r = sm_Range.__new__(cls, *_args)
r._args = args
return r
[docs]class UniversalGroup(Naturals):
"""
Represents the group of all processes.
Since the number of processes is only known at the run-time and the universal
group is assumed to contain all processes, it is convinient to consider it
as the set of all possible processes which is nothing else than the set of
Natural numbers.
np: Symbol
a sympy symbol describing the total number of processes.
"""
np = Symbol('np')
@property
def processes(self):
return Range(0, self.np)
@property
def communicator(self):
from pyccel.ast.parallel.communicator import UniversalCommunicator
return UniversalCommunicator()
@property
def size(self):
"""the total number of processes."""
return self.np
# TODO check size between colors and group
[docs]class Split(Group):
"""
Splits the group over a given color.
Examples
>>> from pyccel.ast.parallel.group import UniversalGroup
>>> from pyccel.ast.parallel.communicator import UniversalCommunicator, split
>>> colors = [1, 1, 0, 1, 0, 0]
>>> g = Split(UniversalGroup(), colors, 0)
>>> g
{2, 4, 5}
>>> comm = split(UniversalCommunicator(), g)
Communicator({2, 4, 5}, None)
"""
def __new__(cls, group, colors, color):
args = []
i = 0
while (i < len(colors)):
if colors[i] == color:
args.append(i)
i += 1
return Group.__new__(cls, *args)