diff --git a/data b/data index a3824606e..c2f8a4d98 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit a3824606eb3eec7c943d7effd20a48d286fbee08 +Subproject commit c2f8a4d98855d130ef983632ac40aff3f27acae9 diff --git a/docs/source/team.rst b/docs/source/team.rst index 7ae2eefc2..cdb8bda87 100644 --- a/docs/source/team.rst +++ b/docs/source/team.rst @@ -27,3 +27,5 @@ Contributors - `Saurav Maheshkar `_ (Improvements to pre-commit configuration) - `Yanpeng Yuan `_ (ASTRA interface improvements) - `Li-Ta (Ollie) Lo `_ (ASTRA interface improvements) +- `Renat Sibgatulin `_ (Docs corrections) +- `Salman Naqvi `_ (Contributions to approximate TV norm prox and proximal average implementation) diff --git a/examples/scripts/deconv_tv_padmm.py b/examples/scripts/deconv_tv_padmm.py index af7c4718b..d1df0df46 100644 --- a/examples/scripts/deconv_tv_padmm.py +++ b/examples/scripts/deconv_tv_padmm.py @@ -101,9 +101,9 @@ """ Set up a proximal ADMM solver object. """ -ρ = 1.0e-1 # ADMM penalty parameter +ρ = 5.0e-2 # ADMM penalty parameter maxiter = 50 # number of ADMM iterations -mu, nu = ProximalADMM.estimate_parameters(D) +mu, nu = ProximalADMM.estimate_parameters(A) solver = ProximalADMM( f=f, diff --git a/examples/scripts/denoise_tv_admm.py b/examples/scripts/denoise_tv_admm.py index 8e35c3e96..9ac5f885c 100644 --- a/examples/scripts/denoise_tv_admm.py +++ b/examples/scripts/denoise_tv_admm.py @@ -80,7 +80,7 @@ """ Denoise with anisotropic total variation for comparison. """ -# Tune the weight to give the same data fidelty as the isotropic case. +# Tune the weight to give the same data fidelity as the isotropic case. λ_aniso = 1.2e0 g_aniso = λ_aniso * functional.L1Norm() diff --git a/examples/scripts/denoise_tv_apgm.py b/examples/scripts/denoise_tv_apgm.py index fbdf6f017..477cacca6 100644 --- a/examples/scripts/denoise_tv_apgm.py +++ b/examples/scripts/denoise_tv_apgm.py @@ -166,7 +166,7 @@ def prox(self, v: Array, lam: float, **kwargs) -> Array: """ Use RobustLineSearchStepSize object and set up AcceleratedPGM solver -object. Weight was tuned to give the same data fidelty as the +object. Weight was tuned to give the same data fidelity as the isotropic case. Run the solver. """ diff --git a/scico/diagnostics.py b/scico/diagnostics.py index 3f202f822..b4b8e026c 100644 --- a/scico/diagnostics.py +++ b/scico/diagnostics.py @@ -26,6 +26,7 @@ def __init__( ident: Optional[dict] = None, display: bool = False, period: int = 1, + shift_cycles: bool = True, overwrite: bool = True, colsep: int = 2, ): @@ -48,14 +49,17 @@ def __init__( fields: A dictionary associating field names with format strings for displaying the corresponding values. ident: A dictionary associating field names. - with corresponding valid identifiers for use within the - namedtuple used to record results. Defaults to ``None``. + with corresponding valid identifiers for use within the + namedtuple used to record results. Defaults to ``None``. display: Flag indicating whether results should be printed to stdout. Defaults to ``False``. period: Only display one result in every cycle of length `period`. + shift_cycles: If ``True``, apply an offset to the iteration + count so that display cycles end at 0, `period` - 1, etc. + Otherwise, cycles end at `period`, 2 * `period`, etc. overwrite: If ``True``, display all results, but each one - overwrites the next, except for one result per cycle. + overwrites the next, except for one result per cycle. colsep: Number of spaces seperating fields in displayed tables. Defaults to 2. @@ -69,6 +73,8 @@ def __init__( raise TypeError("Parameter fields must be an instance of dict.") # Subsampling rate of results that are to be displayed self.period: int = period + # Offset to iteration count for determining start of period + self.period_offset = 1 if shift_cycles else 0 # Flag indicating whether to display and overwrite, or not display at all self.overwrite: bool = overwrite # Number of spaces seperating fields in displayed tables @@ -159,13 +165,13 @@ def insert(self, values: Union[List, Tuple]): print(self.disphdr) self.disphdr = None if self.overwrite: - if (len(self.iterations) - 1) % self.period == 0: + if (len(self.iterations) - self.period_offset) % self.period == 0: end = "\n" else: end = "\r" print((" " * self.colsep).join(self.fieldformat) % values, end=end) else: - if (len(self.iterations) - 1) % self.period == 0: + if (len(self.iterations) - self.period_offset) % self.period == 0: print((" " * self.colsep).join(self.fieldformat) % values) def end(self): @@ -180,7 +186,7 @@ def end(self): self.display and self.overwrite and self.period > 1 - and (len(self.iterations) - 1) % self.period + and (len(self.iterations) - self.period_offset) % self.period ): print() diff --git a/scico/linop/xray/_xray.py b/scico/linop/xray/_xray.py index c23bf420e..7c1399daf 100644 --- a/scico/linop/xray/_xray.py +++ b/scico/linop/xray/_xray.py @@ -52,6 +52,19 @@ def __init__(self, projector): class Parallel2dProjector: """Parallel ray, single axis, 2D X-ray projector. + This implementation approximates the projection of each rectangular + pixel as a boxcar function (whereas the exact projection is a + trapezoid). Detector pixels are modeled as bins (rather than points) + and this approximation allows fast calculation of the contribution + of each pixel to each bin because the integral of the boxcar is + simple. + + By requiring the side length of the pixels to be less than or equal + to the bin width (which is assumed to be 1.0), we ensure that each + pixel contributes to at most two bins, which accelerates the + accumulation of pixel values into bins (equivalently, makes the + linear operator sparse). + `x0`, `dx`, and `y0` should be expressed in units such that the detector spacing `dy` is 1.0. """ diff --git a/scico/optimize/_common.py b/scico/optimize/_common.py index 2f6f0995f..69da373c7 100644 --- a/scico/optimize/_common.py +++ b/scico/optimize/_common.py @@ -82,12 +82,17 @@ def __init__(self, **kwargs: Any): Args: **kwargs: Optional parameter dict. Valid keys are: + iter0: + Initial value of iteration counter. Default value is 0. + maxiter: - Maximum iterations on call to :meth:`solve`. + Maximum iterations on call to :meth:`solve`. Default + value is 100. nanstop: If ``True``, stop iterations if a ``NaN`` or ``Inf`` value is encountered in a solver working variable. + Default value is ``False``. itstat_options: A dict of named parameters to be passed to @@ -102,6 +107,7 @@ def __init__(self, **kwargs: Any): otherwise the default dict is updated with the dict specified by this parameter. """ + iter0 = kwargs.pop("iter0", 0) self.maxiter: int = kwargs.pop("maxiter", 100) self.nanstop: bool = kwargs.pop("nanstop", False) itstat_options = kwargs.pop("itstat_options", None) @@ -109,7 +115,7 @@ def __init__(self, **kwargs: Any): if kwargs: raise TypeError(f"Unrecognized keyword argument(s) {', '.join([k for k in kwargs])}") - self.itnum: int = 0 + self.itnum: int = iter0 self.timer: Timer = Timer() itstat_fields, itstat_attrib = self._itstat_default_fields()