Skip to content

Conversation

@artofscience
Copy link
Owner

No description provided.

@artofscience artofscience self-assigned this Jan 11, 2022
@artofscience artofscience marked this pull request as draft January 11, 2022 14:14
Co-authored-by: max <MaxvdKolk@users.noreply.github.com>
@artofscience artofscience marked this pull request as ready for review January 30, 2022 17:45
@artofscience artofscience marked this pull request as draft January 30, 2022 18:09
Copy link
Collaborator

@dirkmunro89 dirkmunro89 left a comment

Choose a reason for hiding this comment

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

Had a look. Dont feel I am in a position (yet) to do anything other than approve in principle, and hope to understand better when we discuss.

@artofscience artofscience marked this pull request as ready for review February 1, 2022 22:29
Copy link
Collaborator

@MaxvdKolk MaxvdKolk left a comment

Choose a reason for hiding this comment

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

Looks like a nice way to express the classes and the resulting implementations in change_of_variable.py are quite compact now. Would Linear and DQA replace the Taylor* classes, or the other way around?

I wonder if the eval function could be done slightly differently, but lets focus on those details later :].

We might also consider to add another function to make the __setindex__ a bit more discoverable as mm[[1], [2]] = Exp(-2) can be a bit dense. Maybe a wrapper like mm.set_mapping(response=[1], variables=[2], mapping=Exp(-2)) as well? (which would just forward to __setindex__ internally)

Comment on lines 10 to 11
def __init__(self, mapping=None):
self.map = mapping if mapping is not None else Linear()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def __init__(self, mapping=None):
self.map = mapping if mapping is not None else Linear()
def __init__(self, mapping=Linear()):
self.map = mapping

Comment on lines 13 to 16
def update(self, x0, dg0, ddg0=0):
self.map.update(x0, dg0, ddg0)
dg = self.map.dg(x0)
self._update(self.map.g(x0), dg0 / dg, ddg0 / dg ** 2 - dg0 * self.map.ddg(x0) / dg ** 3)
Copy link
Collaborator

Choose a reason for hiding this comment

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

For now it might be too soon, but might be good to write a brief set of docs on the interplay between self.map.update and self._update and the contents of the arguments :].

import numpy as np


class Mapping(ABC):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is ABC here mostly used to indicate this is the base class? There are no methods that are explicitly labelled with @abstracmethod or similar?

def _ddg(self, x): return np.zeros_like(x)


class Linear(Mapping, ABC):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar as a previous remark, not sure if the ABC is necessary, but could be kept in if needed (later on?).

Comment on lines +54 to +55
def __setitem__(self, key, inter: Mapping):
self.map.append((inter, key[0], key[1]))
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 make sense to split the key argument into two arguments to make it more clear both two entries need to be passed?

Suggested change
def __setitem__(self, key, inter: Mapping):
self.map.append((inter, key[0], key[1]))
def __setitem__(self, response, variable, inter: Mapping):
self.map.append((inter, response, variable))

Comment on lines +104 to +109
class Sum(TwoMap):
def g(self, x): return self.left.g(x) + self.right.g(x)

def dg(self, x): return self.left.dg(x) + self.right.dg(x)

def ddg(self, x): return self.left.ddg(x) + self.right.ddg(x)
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 make sense to have Sum as the default behaviour of TwoMap? What happens if someone uses TwoMap.g(..)? Or is that not intended behaviour?

Comment on lines 12 to 18
def update(self, x0, dg0, ddg0=0):
if self.child is not None and self.child.updated is False: # climb down to youngest child
self.child.update(x0, dg0, ddg0)
else: # climb up to oldest broer while updating the mappings
self._update(x0, dg0, ddg0)
self.updated = True
self.parent.update(self.g(x0), self.dg(x0), self.ddg(x0))
Copy link
Collaborator

@MaxvdKolk MaxvdKolk Feb 9, 2022

Choose a reason for hiding this comment

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

I think you might get the behaviour you are looking for by placing the code like this:

def update(self, x0, dg0, ddg0=0):
    if self.child is not None:
        # Traverse until inner-most child
        x0, dg0, ddg0 = self.child.update(x0, dg0, ddg0)
    
    # Bubble up the results from inside to outside,  and use the
    # updated (x0, dg0, ddg0) tuple from its inner mapping.
    return self._update(x0, dg0, ddg0)

def _update(self, x0, dg0, ddg0):
    # Mapping-specific version of the update rule that 
    # updates the self.g0, dg0, ddg0 attributes.
    return self.g0, self.dg0, self.ddg0

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