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

Convert dir to axis #310

Merged
merged 35 commits into from
Mar 8, 2022
Merged

Convert dir to axis #310

merged 35 commits into from
Mar 8, 2022

Conversation

cako
Copy link
Collaborator

@cako cako commented Jan 26, 2022

Motivation

As described in #123, the choice of dir for direction in many linear operators is not compatible with NumPy/Scipy notation. In addition, it encroaches on the dir built-in function. As such, it is highly desirable to move from the use of dir to axis instead. In addition, some other related keywords such as dirs and nodir can both be substituted by axes. While there are other improvements that can be made in this respect, the scope of this PR is simply to move away from dir without altering the current functionality of these modified operators.

Strategy

The main issue to keep in mind is to retain the old behavior as much as possible, so as to allow users to migrate to axis and not break any existing code. The approach taken to ensure this was as follows

  1. Change function signatures from ..., dir=0,......, axis=-1, dir=None, ... and similar for dirs/axes.
  2. Ensure that when dir is supplied, it supersedes the use of axis but also issues a deprecation warning:
if dir is not None:
    warnings.warn(
        "dir is deprecated in version 2.0.0, use axis instead.", category=DeprecationWarning, stacklevel=2,
    )
    axis = dir
else:
    axis = axis
  1. The documentation is updated to always use axis and note that dir is deprecated like so:
axis : :obj:`int`, optional
    .. versionadded:: 2.0.0
    Axis along which derivative is applied.
dir : :obj:`int`, optional
    .. deprecated:: 2.0.0
        Use ``axis`` instead. Note that the default for ``axis`` is -1
        instead of 0 which was the default for ``dir``.
  1. Changes to the linear operators were first made prior to changing tests: this ensured during development that using dir always worked. After this check, tests were updated to also call axis.

Considerations

I am creating this as a draft because there I'd like some feedback on this strategy. For example, I strongly favor setting the default as axis=-1 for v2.0.0, but this is a breaking change. I am also open to amending warnings/documentation on this change.

@cako cako added enhancement New feature or request Core Pulls for core PyLops library labels Jan 26, 2022
@mrava87
Copy link
Collaborator

mrava87 commented Jan 28, 2022

Let me get back to this once we got v1.17.0 out :)

@mrava87
Copy link
Collaborator

mrava87 commented Feb 5, 2022

I have not forgotten about this, just being thinking a little about it.

Overall thought:

  • My main fear is that we are polluting the input variables with an another parameter and this may create more confusion than benefit in the short run. Postponing this to v2.0.0 and describe this as one of the major code-breaking changes may be a better option?

Here a some more specific thoughts:

  • In general I agree the choice of dir was a bad one I regretted early. But I decided that a worst choice would have been to switch half way through... probably doing what you have done early on would have been the best choice ;)

  • Similarly I agree that in v2.0.0 we should move to default axis=-1. I can think of a few cases where having dir=0 as default makes some sense for how the operator is operating, but in most cases working on the last axis is the best idea.

  • One thing I am not sure I understand Change function signatures from ..., dir=0,... → ..., axis=-1, dir=None, ... and similar for dirs/axes.. I understand that a code currently working that has an operator where dir=0 will keep working. But what about a currently written code where an operator is made but dir is not specified, would the behavior suddenly change to axis=-1? Unfortunately if this is the case I think we need to introduce axis more softly... one way could be to have axis=None and if this is provided it will take priority... but maybe I don't understand the strategy you designed?

  • In warning messages I think it is better to say dir will be deprecated as we don't have v2.0.0 yet.

@cako
Copy link
Collaborator Author

cako commented Feb 5, 2022

Hi @mrava87 thanks for the comments. Let's go over your them:

  • I absolutely agree that this should go in v2.0.0. The question is, are you expecting to release other versions before v2.0.0? If so, we could move this to a feature branch tagged v2.0.0.
  • Yup, maintaining compatibility is important!
  • Also agree here, there may be some operators where axis=0 makes more sense as a default. I know NumPy has a few of those. If you think that any of the ones I changed should be changed, flag them and let's discuss!
  • The signature change is kind of tricky. When setting a value as a default e.g. dir=0 in the function signature, there is no way to distinguish between calling the function with myfunction() or with myfunction(dir=0). In order to distinguish between the two cases, you have two options:
    def myfunction(dir=None): # Option 1
    def myfunction2(**kwargs): # Option 2
    Calling myfunction() will create dir=None in the function body and calling myfunction(dir=0) will create dir=0 in the function body. This way we can distinguish in non-None values are passed to the function. When calling myfunction2(), dir will not exist in kwargs and calling myfunction2(dir=any_value) will create kwargs[dir] = any_value.
    So if we want to ensure that dir is respected whenever it is supplied, we need to choose one of two options. Since we don't use dir=None as a valid keyword, the first option is preferred. Using kwargs is generally the last resort since it makes code really hard to read, maintain and debug (for example, calling myfunction2(arg_that_doesnt_exist=0) does not raise an error. That's the reasoning behind the dir=None.
    To answer your question, yes, not specifying dir will use axis=-1. But that's what a default of axis=-1 is. One possibility I considered was to first introduce axis=0 (the same as dir) and only later switch the default to axis=-1. But in this case, the user will have to know exactly which version introduces the change of defaults. On the other hand, doing it all at once ensures the user that if axis is a valid keyword, then axis=-1 will always be the default. In terms of priority, dir will always precede axis until it is retired as a keyword.
  • Good point, I will change it!

@cako cako added the breaking Breaking change label Feb 20, 2022
cako added 3 commits February 21, 2022 21:05
# Conflicts:
#	examples/plot_causalintegration.py
#	examples/plot_derivative.py
#	examples/plot_flip.py
#	examples/plot_restriction.py
#	examples/plot_roll.py
#	examples/plot_stacking.py
#	examples/plot_symmetrize.py
#	examples/plot_tvreg.py
#	pylops/avo/poststack.py
#	pylops/avo/prestack.py
#	pylops/basicoperators/CausalIntegration.py
#	pylops/basicoperators/FirstDerivative.py
#	pylops/basicoperators/Flip.py
#	pylops/basicoperators/Gradient.py
#	pylops/basicoperators/Laplacian.py
#	pylops/basicoperators/Restriction.py
#	pylops/basicoperators/Roll.py
#	pylops/basicoperators/SecondDerivative.py
#	pylops/basicoperators/Smoothing1D.py
#	pylops/basicoperators/Smoothing2D.py
#	pylops/basicoperators/Symmetrize.py
#	pylops/signalprocessing/Convolve1D.py
#	pylops/signalprocessing/Convolve2D.py
#	pylops/signalprocessing/ConvolveND.py
#	pylops/signalprocessing/Interp.py
#	pylops/signalprocessing/Patch2D.py
#	pylops/signalprocessing/Sliding2D.py
#	pylops/signalprocessing/Sliding3D.py
#	pylops/waveeqprocessing/lsm.py
#	pylops/waveeqprocessing/marchenko.py
#	pylops/waveeqprocessing/seismicinterpolation.py
#	pytests/test_basicoperators.py
#	pytests/test_causalintegration.py
#	pytests/test_convolve.py
#	pytests/test_derivative.py
#	pytests/test_interpolation.py
#	pytests/test_kronecker.py
#	pytests/test_restriction.py
#	pytests/test_seismicinterpolation.py
#	tutorials/ctscan.py
#	tutorials/deblurring.py
#	tutorials/seismicinterpolation.py
@cako cako marked this pull request as ready for review February 22, 2022 02:48
@cako cako requested a review from mrava87 February 22, 2022 02:50
@cako
Copy link
Collaborator Author

cako commented Feb 22, 2022

This PR, together with #332 closes #123.

@mrava87
Copy link
Collaborator

mrava87 commented Feb 22, 2022

Great! Let me take a look in details in coming days (I just want to be sure if there is any operator where we don't want to switch convention 0->1) then I can merge it :)

Copy link
Collaborator

@mrava87 mrava87 left a comment

Choose a reason for hiding this comment

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

First half, done up to Interp

MIGRATION_V1_V2.md Outdated Show resolved Hide resolved
pylops/basicoperators/CausalIntegration.py Outdated Show resolved Hide resolved
pylops/basicoperators/FirstDerivative.py Show resolved Hide resolved
pylops/basicoperators/Restriction.py Show resolved Hide resolved
pylops/basicoperators/SecondDerivative.py Show resolved Hide resolved
pylops/basicoperators/Symmetrize.py Show resolved Hide resolved
pylops/signalprocessing/Interp.py Show resolved Hide resolved
Copy link
Collaborator

@mrava87 mrava87 left a comment

Choose a reason for hiding this comment

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

Second part

pylops/signalprocessing/Sliding1D.py Outdated Show resolved Hide resolved
pylops/signalprocessing/Sliding2D.py Show resolved Hide resolved
pylops/signalprocessing/_BaseFFTs.py Outdated Show resolved Hide resolved
@cako
Copy link
Collaborator Author

cako commented Mar 6, 2022

@mrava87, could you have a final look? By the way, we should probably squash all this into one commit when we merge :)

@cako cako mentioned this pull request Mar 7, 2022
@mrava87
Copy link
Collaborator

mrava87 commented Mar 7, 2022

Let me take a final look and I let you know if I have anything more :) Then I am happy if you want to squash all in one commit as there is quite a lot here and it all fits within the same idea of removing dir :)

@mrava87
Copy link
Collaborator

mrava87 commented Mar 7, 2022

Alright, apart from the small comment on the MIGRATION file, everything else looks good to me and ready to go :)

@cako
Copy link
Collaborator Author

cako commented Mar 7, 2022

Alright, apart from the small comment on the MIGRATION file, everything else looks good to me and ready to go :)

Have a look here. If it's ok, feel free to merge!

@mrava87
Copy link
Collaborator

mrava87 commented Mar 7, 2022

Wait, maybe my comment was confusing. In the MIGRATION file you say:

the default `dir` and `dirs` was -1 and (0, 1),

shouldn't that be:

the default `dir` and `dirs` was 0 and (0, 1),

Apart from that, you want to squash all in one commit before I merge or not? I don't really have strong feeling here ;)

@cako cako merged commit af3cf70 into PyLops:dev Mar 8, 2022
@cako cako deleted the patch-axis branch March 8, 2022 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Breaking change Core Pulls for core PyLops library enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants