Skip to content

ProxSkip algorithm - #2267

Open
epapoutsellis wants to merge 9 commits into
TomographicImaging:masterfrom
epapoutsellis:proxskip
Open

epapoutsellis wants to merge 9 commits into
TomographicImaging:masterfrom
epapoutsellis:proxskip

Conversation

@epapoutsellis

@epapoutsellis epapoutsellis commented Jan 28, 2026 •

Copy link
Copy Markdown
Contributor

Description

close #2266

Example Usage

Contribution Notes

  • The content of this Pull Request (the Contribution) is intentionally submitted for inclusion in CIL (the Work) under the terms and conditions of the Apache-2.0 License
  • I confirm that the contribution does not violate any intellectual property rights of third parties

❤️ Thanks for your contribution!

@github-project-automation github-project-automation Bot moved this to Todo in UM 2026 Jan 29, 2026
@casperdcl casperdcl removed this from UM 2026 Jan 29, 2026
@gfardell gfardell added the community-contribution Community-submitted pull requests from contributors outside the core team. label Jan 30, 2026
@gfardell

Copy link
Copy Markdown
Member

Thanks Vaggelis, this looks like a great addition. I'll add it to the work plan and make sure it gets prioritised for review.

class ProxSkip(Algorithm):


r"""Proximal Skip (ProxSkip) algorithm, see "ProxSkip: Yes! Local Gradient Steps Provably Lead to Communication Acceleration! Finally!†"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Need to add ref paper @epapoutsellis

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added

@lauramurgatroyd
lauramurgatroyd requested review from gfardell and removed request for paskino September 22, 2026 14:08

@gfardell gfardell left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @epapoutsellis, I have added some comments. Please let me know when you are ready for me to review, or if you need any support in addressing them. I haven't looked closely at the unit tests yet.

Comment on lines +87 to +92
def _update_previous_solution(self):
""" Swaps the references to current and previous solution based on the :func:`~Algorithm.update_previous_solution` of the base class :class:`Algorithm`.
"""
tmp = self.x_new
self.x = self.x_new
self.x = tmp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't switching the references as intended. Instead it's orphaning the buffer that was at self.x.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +82 to +83
self.g.proximal(self.xhat_new - (self.step_size/self.prob)*self.ht, self.step_size/self.prob, out=self.x_new)
self.ht.sapyb(1., (self.x_new - self.xhat_new), (self.prob/self.step_size), out=self.ht)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These are both allocating new ImageData's for the intermediate steps. This should use a preallocated buffer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added new buffer.

# convention: use proximal in the first iteration
if self.iteration==0:
theta = True
self.thetas.append(theta)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason to log these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is useful for the user to see where proximal is computed. I created a new function that can be called after running the algo.

if self.prob<=0:
raise ValueError("Need a positive probability")
if self.prob==1:
raise warn("If p=1, ProxSkip is equivalent to ISTA/PGD. Please use ISTA/PGD to avoid computing updates of the control variate that is not used.")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be a warnings.warn() not raise warn. It also should have a unit test check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed


logging.info("{} setting up".format(self.__class__.__name__, ))

## TODO better to use different initials for x and h.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ToDo in code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed

self.xhat_new -= self.ht
self.x.sapyb(1., self.xhat_new, -self.step_size, out=self.xhat_new)

theta = self.rng.random() < self.prob

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If prob=1, theata is true and proximal step is called. But the docstring says prob is the probability to skip the proximal step.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -0,0 +1,112 @@
from cil.optimisation.algorithms import Algorithm

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No file header. It should be the licence and you added as a contibutor. As a note we also should update your instituation in the notice.txt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.


self.configured = True

logging.info("{} configured".format(self.__class__.__name__, ))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The module log should be created at the top of the file with
log = logging.getLogger(__name__) then log.info()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

A convex function with a "simple" proximal.
prob : positive :obj:`float`
Probability to skip the proximal step. If :code:`prob=1`, proximal step is used in every iteration.
step_size : positive :obj:`float`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could this accept a StepSizeRule as Ista and Fista do?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly, can it take a preconditioner like ISTA or FISTA??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason that I do not inherit for ISTA is that at the moment there is no theory for adaptive step sizes or precond when skipping. Only heuristic choices that are not necessarily faster (in time) compared to a ISTA. So I think is dangerous for the users to allow this option.

logging.info("{} setting up".format(self.__class__.__name__, ))

## TODO better to use different initials for x and h.
self.initial = initial

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This adds a reference to initial that is never used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@MargaretDuff MargaretDuff left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few quick comments



r"""Proximal Skip (ProxSkip) algorithm, see "ProxSkip: Yes! Local Gradient Steps Provably Lead to Communication Acceleration! Finally!†"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we also add a few lines about what optimisation objective the algorithm solves and why you would want to use it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

from warnings import warn


class ProxSkip(Algorithm):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason it is not a subclass of ISTA/PGD?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason that I do not inherit for ISTA is that at the moment there is no theory for adaptive step sizes or precond when skipping. Only heuristic choices that are not necessarily faster (in time) compared to a ISTA. So I think is dangerous for the users to allow this option.

A convex function with a "simple" proximal.
prob : positive :obj:`float`
Probability to skip the proximal step. If :code:`prob=1`, proximal step is used in every iteration.
step_size : positive :obj:`float`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly, can it take a preconditioner like ISTA or FISTA??

@lauramurgatroyd lauramurgatroyd added this to the v26.1 milestone Oct 8, 2026
@epapoutsellis

Copy link
Copy Markdown
Contributor Author

Thank you @gfardell @MargaretDuff for your reviews . I've made a few changes to address your comments.

Signed-off-by: Vaggelis Papoutsellis <22398586+epapoutsellis@users.noreply.github.com>
@lauramurgatroyd lauramurgatroyd moved this from Blocked to Priority review in CIL work Oct 27, 2026
@gfardell

Copy link
Copy Markdown
Member

@epapoutsellis thanks! I will take another look in the next week.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Community-submitted pull requests from contributors outside the core team.

Projects

Status: Priority review

Development

Successfully merging this pull request may close these issues.

Implement ProxSkip algorithm

5 participants