Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core PETSc objects/functions. #3

Merged
merged 24 commits into from
Jan 8, 2024
Merged

Core PETSc objects/functions. #3

merged 24 commits into from
Jan 8, 2024

Conversation

ZoeLeibowitz
Copy link
Owner

No description provided.

@ZoeLeibowitz
Copy link
Owner Author

I rebased on top of Fabio’s branch ‘sycl-init’ hence all of the other file changes. Please review these files:

  1. types/petsc.py
  2. tests/test_petsc.py

I also added the function ‘dtype_to_petsctype’ inside tools/dtypes_lowering.py

Also, can someone help me understand why two of the pytests are failing?


def __init_finalize__(self, *args, **kwargs):

super(PETScFunction, self).__init_finalize__(*args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can just be super().__init_finalize__(*args, **kwargs)


class PETScFunction(AbstractFunction):
"""
PETScFunctions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want a more fleshed out docstring in due course

self._is_const = kwargs.get('is_const', False)

@classmethod
def __dtype_setup__(cls, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cls is never used and can be dropped from the args

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this gets called in the super in which case it might need to stay

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it must stay

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do u need to override this method?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise the dtype for each PETScFunction is None and I use the dtype inside the new function dtype_to_petsctype. I think this works for PETScFunctions because the petsctype will actually depend on the dtype of the object (e.g Function) provided by the user.

def _C_ctype(self):
petsc_type = dtype_to_petsctype(self.dtype)
modifier = '*' * len(self.dimensions)
customtype = CustomDtype(petsc_type, modifier=modifier)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just return this line

grid = Grid((2, 2))
x, y = grid.dimensions

ptr0 = PETScFunction(name='ptr0', dtype=np.float32, dimensions=grid.dimensions,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth checking all the datatypes here, so np.float64, np.int64 etc?

self._is_const = kwargs.get('is_const', False)

@classmethod
def __dtype_setup__(cls, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it must stay

return np.float32

@classmethod
def __indices_setup__(cls, *args, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do u need to override this method?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise the object indices are set as empty tuples because I'm inheriting from AbstractFunction.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we only want part of DiscreteFunction but not all of it? WHich part of inheriting directly from DiscreteFunction creates issues?

self._is_const = kwargs.get('is_const', False)

@classmethod
def __dtype_setup__(cls, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do u need to override this method?

return tuple(dimensions), tuple(dimensions)

@property
def dimensions(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do u need to override this property?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, this one should not be here

devito/types/petsc.py Show resolved Hide resolved
np.float32: 'PetscScalar',
np.int64: 'PetscInt',
np.float64: 'PetscScalar'
}[dtype]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Petsc use the same type for single and double precision?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe PetscInt just represents an integer. Whether it is 32-bit or 64-bit depends on how you configured PETSc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant the float64 vs float32

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They use the same type for single and double precision but its representation depends on how the code was configured. https://petsc.org/release/manualpages/Sys/PetscScalar/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you can't have both single and double precision in the same code?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, as far as I'm aware PETSc doesn't support mixed precision

@ZoeLeibowitz
Copy link
Owner Author

I can achieve what I need for PETScFunctions by inheriting directly from ArrayBasic instead of Array (see types/petsc.py). However, I'm not sure if my code is a little hacky or not because it involves overriding the _C_name property. More specifically, if I do not override this property, creating this:p = PETScFunction(name='p', dimensions=.., dtype=..)generates the following TWO lines in the ccode:

PetscScalar**restrict p_vec;
float (*restrict p)[y_size] = (float (*)[y_size]) p_vec;

but if I override the _C_name property like this:

 @property
 def _C_name(self):
     return self.name

then creating the same PETScFunction as above will generate the following single line in the ccode (which is what I want):
PetscScalar**restrict p;
Does this seem ok or absolutely not? @FabioLuporini @mloubout

@FabioLuporini
Copy link
Collaborator

FabioLuporini commented Dec 21, 2023

it's hacky but because we might have a technical issue in devito, which you're relying upon. So I don't have a direct answer here...

But I do have a question 😂

If you created:

class Wrapper(LocalObject, AbstractFunction):

The former to allow custom types, the latter to allow indexing; how simpler/messy would the implementation become?

If I were to design the types hierarchy now, I would maybe devise a series of base classes, each of these base classes providing a specific set of functionalities. For example ( I would actually need different names...):

  • LocalObject enables custom dtypes + other stuff (_C_init etc etc)
  • AbstractFunction enabling array notation (via shape, dimensions, etc...)

Or, alternatively (I guess I'm just thinking out aloud here), I would have a common base class that in theory can do anything, and then it's up to the subclasses to specifiy what is actually needed

So this is not really answering your question, I know. The truth is that I don't have an answer here about how to do things properly, and this is just food for though. I would very welcome a re-design and or improvements that help achieving what you need

Sorry if not helpful this time 😄

@ZoeLeibowitz
Copy link
Owner Author

it's hacky but because we might have a technical issue in devito, which you're relying upon. So I don't have a direct answer here...

But I do have a question 😂

If you created:

class Wrapper(LocalObject, AbstractFunction):

The former to allow custom types, the latter to allow indexing; how simpler/messy would the implementation become?

If I were to design the types hierarchy now, I would maybe devise a series of base classes, each of these base classes providing a specific set of functionalities. For example ( I would actually need different names...):

  • LocalObject enables custom dtypes + other stuff (_C_init etc etc)
  • AbstractFunction enabling array notation (via shape, dimensions, etc...)

Or, alternatively (I guess I'm just thinking out aloud here), I would have a common base class that in theory can do anything, and then it's up to the subclasses to specifiy what is actually needed

So this is not really answering your question, I know. The truth is that I don't have an answer here about how to do things properly, and this is just food for though. I would very welcome a re-design and or improvements that help achieving what you need

Sorry if not helpful this time 😄

I have just spent some time trying to inherit from both classes but I was having various problems. Whilst I was debugging, I came across this: https://github.com/devitocodes/devito/blob/3126fb0e7960094360ccb1b21abe577e0804f017/devito/passes/iet/definitions.py#L392C43-L392C43
which perhaps suggests that overriding the _C_name property is ok in this case? It just means that it will not generate the cast. If this is ok, then I think my code inheriting directly from ArrayBasic works fine (in a similar way to how ArrayObject works). If I eventually want/need the _C_init and _C_free functionality, I can add them to the class and edit the place_definitions function inside iet/definitions.py, similarly to how LocalObject works (I just tried this). What do you think about this?

@FabioLuporini
Copy link
Collaborator

yes I think it makes sense

we could also think of moving all of that LocalObject logic (_C_init etc) into a mixin class, but that's a secondary thought at this point

dtype = CustomDtype('KSPConvergedReason')


class PETScFunction(ArrayBasic):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PETScArray since it's and Array now

@@ -219,6 +219,14 @@ def _alloc_object_array_on_low_lat_mem(self, site, obj, storage):

storage.update(obj, site, allocs=decl)

def _alloc_petsc_array_on_low_lat_mem(self, site, obj, storage):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks exactly the same as _alloc_object_array_on_low_lat_mem robably only needs
elif i.is_ObjectArray or i.is_PETScArray: below

@@ -219,6 +219,17 @@ def _alloc_object_array_on_low_lat_mem(self, site, obj, storage):

storage.update(obj, site, allocs=decl)

def _alloc_special_type_on_low_lat_mem(self, site, obj, storage):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's still the exact same one as _alloc_object_array_on_low_lat_mem why does it needs a separate one

np.float32: 'PetscScalar',
np.int64: 'PetscInt',
np.float64: 'PetscScalar'
}[dtype]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you can't have both single and double precision in the same code?

__rkwargs__ = AbstractFunction.__rkwargs__ + ('is_const',)

def __init_finalize__(self, *args, **kwargs):

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded blank line

def __dtype_setup__(cls, **kwargs):
return kwargs.get('dtype', np.float32)

@property
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be a cached_property


@property
def _C_ctype(self):
petsc_type = dtype_to_petsctype(self.dtype)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicking:

PETSc is so special that I would maybe put dtype_to_petsctype in here rather than in tools/

@ZoeLeibowitz ZoeLeibowitz merged commit 7ce484e into master Jan 8, 2024
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants