Skip to content

Commit

Permalink
#195: make scaling operations in place
Browse files Browse the repository at this point in the history
  • Loading branch information
cwschilly committed May 29, 2024
1 parent fa07f8c commit b3092aa
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 75 deletions.
6 changes: 3 additions & 3 deletions romtools/vector_space/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,15 @@ def __init__(self,
shifter = create_noop_shifter(snapshots)
n_var = snapshots.shape[0]
shifter.apply_shift(snapshots)
scaled_shifted_snapshots = scaler.pre_scale(snapshots)
snapshot_matrix = _tensor_to_matrix(scaled_shifted_snapshots)
scaler.pre_scale(snapshots)
snapshot_matrix = _tensor_to_matrix(snapshots)
svd_picked = np.linalg.svd if svdFnc is None else svdFnc
lsv, svals, _ = svd_picked(snapshot_matrix, full_matrices=False,
compute_uv=True, hermitian=False)

self.__basis = truncater.truncate(lsv, svals)
self.__basis = _matrix_to_tensor(n_var, self.__basis)
self.__basis = scaler.post_scale(self.__basis)
scaler.post_scale(self.__basis)
self.__basis = _tensor_to_matrix(self.__basis)
self.__basis = orthogonalizer.orthogonalize(self.__basis)
self.__basis = _matrix_to_tensor(n_var, self.__basis)
Expand Down
51 changes: 24 additions & 27 deletions romtools/vector_space/utils/scaler.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ class Scaler(Protocol):
Interface for the Scaler class.
"""

def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def pre_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the snapshot matrix before performing SVD
Scales the snapshot matrix in place before performing SVD
"""
...

def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def post_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the left singular vectors after performing SVD
Scales the left singular vectors in place after performing SVD
"""
...

Expand All @@ -112,11 +112,11 @@ class NoOpScaler:
def __init__(self) -> None:
pass

def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
return data_tensor
def pre_scale(self, data_tensor: np.ndarray):
data_tensor = data_tensor

def post_scale(self, data_tensor) -> np.ndarray:
return data_tensor
def post_scale(self, data_tensor):
data_tensor = data_tensor


class VectorScaler:
Expand Down Expand Up @@ -148,7 +148,7 @@ def __init__(self, scaling_vector) -> None:
self.__scaling_vector_matrix = scaling_vector
self.__scaling_vector_matrix_inv = 1.0 / scaling_vector

def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def pre_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix using the inverse of the scaling vector
and returns the scaled matrix.
Expand All @@ -159,9 +159,9 @@ def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
Returns:
np.ndarray: The scaled data matrix.
"""
return self.__scaling_vector_matrix_inv[None, :, None] * data_tensor
data_tensor *= self.__scaling_vector_matrix_inv[None, :, None]

def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def post_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix using the scaling vector and returns the
scaled matrix.
Expand All @@ -172,7 +172,7 @@ def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
Returns:
np.ndarray: The scaled data matrix.
"""
return self.__scaling_vector_matrix[None, :, None] * data_tensor
data_tensor *= self.__scaling_vector_matrix[None, :, None]


class ScalarScaler:
Expand All @@ -186,10 +186,10 @@ def __init__(self, factor: float = 1.0) -> None:
self._factor = factor

def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
return data_tensor / self._factor
data_tensor /= self._factor

def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
return data_tensor * self._factor
data_tensor *= self._factor


class VariableScaler:
Expand Down Expand Up @@ -252,7 +252,7 @@ def initialize_scalings(self, data_tensor: np.ndarray) -> None:
self.have_scales_been_initialized = True

# These are all inplace operations
def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def pre_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix before processing, taking into account
the previously initialized scaling factors.
Expand All @@ -270,10 +270,9 @@ def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
self.initialize_scalings(data_tensor)
# scale each field (variable scaling)
for i in range(n_var):
data_tensor[i] = data_tensor[i] / self.var_scales_[i]
return data_tensor
data_tensor[i] /= self.var_scales_[i]

def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def post_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix using the scaling vector and returns the
scaled matrix.
Expand All @@ -288,9 +287,7 @@ def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
# scale each field
n_var = data_tensor.shape[0]
for i in range(n_var):
data_tensor[i] = data_tensor[i] * self.var_scales_[i]
return data_tensor

data_tensor[i] *= self.var_scales_[i]

class VariableAndVectorScaler:
"""
Expand Down Expand Up @@ -320,7 +317,7 @@ def __init__(self, scaling_vector, scaling_type) -> None:
self.__my_variable_scaler = VariableScaler(scaling_type)
self.__my_vector_scaler = VectorScaler(scaling_vector)

def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def pre_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix before processing, first using the
`VariableScaler` and then the `VectorScaler`.
Expand All @@ -331,10 +328,10 @@ def pre_scale(self, data_tensor: np.ndarray) -> np.ndarray:
Returns:
np.ndarray: The scaled data matrix.
"""
data_tensor = self.__my_variable_scaler.pre_scale(data_tensor)
return self.__my_vector_scaler.pre_scale(data_tensor)
self.__my_variable_scaler.pre_scale(data_tensor)
self.__my_vector_scaler.pre_scale(data_tensor)

def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
def post_scale(self, data_tensor: np.ndarray) -> None:
"""
Scales the input data matrix after processing, first using the
`VectorScaler` and then the `VariableScaler`.
Expand All @@ -345,5 +342,5 @@ def post_scale(self, data_tensor: np.ndarray) -> np.ndarray:
Returns:
np.ndarray: The scaled data matrix.
"""
data_tensor = self.__my_vector_scaler.post_scale(data_tensor)
return self.__my_variable_scaler.post_scale(data_tensor)
self.__my_vector_scaler.post_scale(data_tensor)
self.__my_variable_scaler.post_scale(data_tensor)
18 changes: 9 additions & 9 deletions tests/romtools/test_vector_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ def test_trial_space_from_scaled_pod():
snapshots = np.random.normal(size=(3, 8, 6))
my_scaler = utils.VariableScaler("max_abs")
my_vector_space = rt.VectorSpaceFromPOD(copy.deepcopy(snapshots), scaler=my_scaler)
scaled_snapshots = my_scaler.pre_scale(snapshots)
snapshotMatrix = _tensor_to_matrix(scaled_snapshots)
my_scaler.pre_scale(snapshots)
snapshotMatrix = _tensor_to_matrix(snapshots)
u, s, v = np.linalg.svd(snapshotMatrix, full_matrices=False)
basis_tensor = my_vector_space.get_basis()
u = u.reshape(basis_tensor.shape)
u = my_scaler.post_scale(u)
my_scaler.post_scale(u)
assert np.allclose(u, basis_tensor), print(u, my_vector_space.get_basis())
assert np.allclose(6, my_vector_space.extents()[-1])
assert np.allclose(0, my_vector_space.get_shift_vector())
Expand All @@ -107,12 +107,12 @@ def test_trial_space_from_scaled_pod():
my_scaler = utils.VariableScaler("max_abs")
my_vector_space = rt.VectorSpaceFromPOD(snapshots, shifter=my_shifter, scaler=my_scaler)
my_shifter.apply_shift(shifted_snapshots)
scaled_shifted_snapshots = my_scaler.pre_scale(shifted_snapshots)
snapshot_matrix = _tensor_to_matrix(scaled_shifted_snapshots)
my_scaler.pre_scale(shifted_snapshots)
snapshot_matrix = _tensor_to_matrix(shifted_snapshots)
u, s, v = np.linalg.svd(snapshot_matrix, full_matrices=False)
basis_tensor = my_vector_space.get_basis()
u = u.reshape(basis_tensor.shape)
u = my_scaler.post_scale(u)
my_scaler.post_scale(u)
assert np.allclose(basis_tensor, u) # FAILS
assert np.allclose(my_vector_space.get_shift_vector(), np.mean(original_snapshots, axis=2))
assert np.allclose(my_vector_space.extents()[-1], 6)
Expand All @@ -128,13 +128,13 @@ def test_trial_space_from_scaled_pod():
my_vector_space = rt.VectorSpaceFromPOD(snapshots, shifter=my_shifter, scaler=my_scaler, orthogonalizer=my_orthogonalizer)
my_shifter.apply_shift(shifted_snapshots)
my_scaler = utils.VariableScaler("max_abs")
scaled_shifted_snapshots = my_scaler.pre_scale(shifted_snapshots)
snapshot_matrix = _tensor_to_matrix(scaled_shifted_snapshots)
my_scaler.pre_scale(shifted_snapshots)
snapshot_matrix = _tensor_to_matrix(shifted_snapshots)
u, s, v = np.linalg.svd(snapshot_matrix, full_matrices=False)
ushp = u.shape
basis_tensor = my_vector_space.get_basis()
u = u.reshape(basis_tensor.shape)
u = my_scaler.post_scale(u)
my_scaler.post_scale(u)
u = my_orthogonalizer.orthogonalize(u.reshape(ushp))
u = u.reshape(basis_tensor.shape)
assert np.allclose(basis_tensor, u)
Expand Down
74 changes: 38 additions & 36 deletions tests/romtools/test_vector_space_utils/test_scaler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@
def test_noop_scaler():
scaler = NoOpScaler()
my_snapshots = np.random.normal(size=(3, 10, 2))
my_scaled_snapshots = scaler.pre_scale(my_snapshots)
my_unscaled_snapshots = scaler.post_scale(my_scaled_snapshots)
assert np.allclose(my_snapshots, my_scaled_snapshots)
assert np.allclose(my_scaled_snapshots, my_unscaled_snapshots)
orig_snapshots = copy.deepcopy(my_snapshots)
scaler.pre_scale(my_snapshots)
assert np.allclose(my_snapshots, orig_snapshots)
scaler.post_scale(my_snapshots)
assert np.allclose(my_snapshots, orig_snapshots)


@pytest.mark.mpi(min_size=3)
def test_noop_scaler_mpi():
comm = MPI.COMM_WORLD
scaler = NoOpScaler()
local_snapshots, _ = generate_random_local_and_global_arrays_impl((3, 10, 2), comm=comm)
scaled_local_snapshots = scaler.pre_scale(local_snapshots)
unscaled_local_snapshots = scaler.post_scale(scaled_local_snapshots)
assert np.allclose(local_snapshots, scaled_local_snapshots)
assert np.allclose(scaled_local_snapshots, unscaled_local_snapshots)
orig_snapshots = copy.deepcopy(local_snapshots)
scaler.pre_scale(local_snapshots)
assert np.allclose(local_snapshots, orig_snapshots)
scaler.post_scale(local_snapshots)
assert np.allclose(local_snapshots, orig_snapshots)


def scaling_op(scaling_type, arg):
Expand All @@ -50,11 +52,11 @@ def test_scalar_scaler():
my_initial_snapshots = copy.deepcopy(my_snapshots)
scaler = ScalarScaler(my_scaling_factor)

my_scaled_snapshots = scaler.pre_scale(my_snapshots)
scaler.pre_scale(my_snapshots)
assert np.allclose(my_snapshots, 1.0 / my_scaling_factor * my_initial_snapshots)

assert np.allclose(my_scaled_snapshots, 1.0 / my_scaling_factor * my_initial_snapshots)
my_unscaled_snapshots = scaler.post_scale(my_scaled_snapshots)
assert np.allclose(my_initial_snapshots, my_unscaled_snapshots)
scaler.post_scale(my_snapshots)
assert np.allclose(my_snapshots, my_initial_snapshots)


@pytest.mark.mpi_skip
Expand All @@ -66,11 +68,11 @@ def test_vector_scaler():
my_initial_snapshots = copy.deepcopy(my_snapshots)
scaler = VectorScaler(my_scaling_vector)

my_scaled_snapshots = scaler.pre_scale(my_snapshots)
scaler.pre_scale(my_snapshots)
assert np.allclose(my_snapshots, 1.0 / my_scaling_vector[None, :, None] * my_initial_snapshots)

assert np.allclose(my_scaled_snapshots, 1.0 / my_scaling_vector[None, :, None] * my_initial_snapshots)
my_unscaled_snapshots = scaler.post_scale(my_scaled_snapshots)
assert np.allclose(my_initial_snapshots, my_unscaled_snapshots)
scaler.post_scale(my_snapshots)
assert np.allclose(my_snapshots, my_initial_snapshots)


@pytest.mark.mpi(min_size=3)
Expand All @@ -83,11 +85,11 @@ def test_vector_scaler_mpi():
initial_local_snapshots = copy.deepcopy(local_snapshots)
scaler = VectorScaler(my_scaling_vector)

scaled_local_snapshots = scaler.pre_scale(local_snapshots)
scaler.pre_scale(local_snapshots)
assert np.allclose(local_snapshots, 1.0 / my_scaling_vector[None, :, None] * initial_local_snapshots)

assert np.allclose(scaled_local_snapshots, 1.0 / my_scaling_vector[None, :, None] * initial_local_snapshots)
unscaled_local_snapshots = scaler.post_scale(scaled_local_snapshots)
assert np.allclose(initial_local_snapshots, unscaled_local_snapshots)
scaler.post_scale(local_snapshots)
assert np.allclose(local_snapshots, initial_local_snapshots)


@pytest.mark.mpi_skip
Expand All @@ -103,13 +105,13 @@ def run_test(scaling_type):

my_initial_snapshots = copy.deepcopy(my_snapshots)
scaler = VariableScaler(scaling_type)
my_scaled_snapshots = scaler.pre_scale(my_snapshots)
scaler.pre_scale(my_snapshots)
for i in range(0, n_var):
assert np.allclose(my_scaled_snapshots[i], 1.0 / scales[i] * my_initial_snapshots[i])
assert np.allclose(my_snapshots[i], 1.0 / scales[i] * my_initial_snapshots[i])

my_unscaled_snapshots = scaler.post_scale(my_scaled_snapshots)
scaler.post_scale(my_snapshots)
assert np.allclose(scales, scaler.var_scales_)
assert np.allclose(my_initial_snapshots, my_unscaled_snapshots)
assert np.allclose(my_initial_snapshots, my_snapshots)

run_test("max_abs")
run_test("mean_abs")
Expand All @@ -130,13 +132,13 @@ def run_test(scaling_type):

initial_local_snapshots = copy.deepcopy(local_snapshots)
scaler = VariableScaler(scaling_type)
scaled_local_snapshots = scaler.pre_scale(local_snapshots)
scaler.pre_scale(local_snapshots)
for i in range(0, n_var):
assert np.allclose(scaled_local_snapshots[i], 1.0 / scales[i] * initial_local_snapshots[i])
assert np.allclose(local_snapshots[i], 1.0 / scales[i] * initial_local_snapshots[i])

unscaled_local_snapshots = scaler.post_scale(scaled_local_snapshots)
scaler.post_scale(local_snapshots)
assert np.allclose(scales, scaler.var_scales_)
assert np.allclose(initial_local_snapshots, unscaled_local_snapshots)
assert np.allclose(initial_local_snapshots, local_snapshots)

run_test("max_abs")
run_test("mean_abs")
Expand All @@ -157,12 +159,12 @@ def run_test(scaling_type):

my_initial_snapshots = copy.deepcopy(my_snapshots)
scaler = VariableAndVectorScaler(my_scaling_vector, scaling_type)
my_scaled_snapshots = scaler.pre_scale(my_snapshots)
scaler.pre_scale(my_snapshots)
for i in range(0, n_var):
assert np.allclose(my_scaled_snapshots[i], 1.0 / scales[i] * (1.0 / my_scaling_vector[None, :, None] * my_initial_snapshots)[i])
assert np.allclose(my_snapshots[i], 1.0 / scales[i] * (1.0 / my_scaling_vector[None, :, None] * my_initial_snapshots)[i])

my_unscaled_snapshots = scaler.post_scale(my_scaled_snapshots)
assert np.allclose(my_initial_snapshots, my_unscaled_snapshots)
scaler.post_scale(my_snapshots)
assert np.allclose(my_initial_snapshots, my_snapshots)

run_test("max_abs")
run_test("mean_abs")
Expand All @@ -184,12 +186,12 @@ def run_test(scaling_type):

initial_local_snapshots = copy.deepcopy(local_snapshots)
scaler = VariableAndVectorScaler(my_scaling_vector, scaling_type)
scaled_local_snapshots = scaler.pre_scale(local_snapshots)
scaler.pre_scale(local_snapshots)
for i in range(0, n_var):
assert np.allclose(scaled_local_snapshots[i], 1.0 / scales[i] * (1.0 / my_scaling_vector[None, :, None] * initial_local_snapshots)[i])
assert np.allclose(local_snapshots[i], 1.0 / scales[i] * (1.0 / my_scaling_vector[None, :, None] * initial_local_snapshots)[i])

unscaled_local_snapshots = scaler.post_scale(scaled_local_snapshots)
assert np.allclose(initial_local_snapshots, unscaled_local_snapshots)
scaler.post_scale(local_snapshots)
assert np.allclose(initial_local_snapshots, local_snapshots)

run_test("max_abs")
run_test("mean_abs")
Expand Down

0 comments on commit b3092aa

Please sign in to comment.