From 7bb95e670f2bb4cf1509709bae48187762a9abca Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Tue, 24 Feb 2026 20:52:06 -0600 Subject: [PATCH 1/5] Created abstract Synthesizer class with generate(), noise(), and abstract model() Co-Authored-By: Claude Sonnet 4.6 --- labcore/data/synthesizer.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 labcore/data/synthesizer.py diff --git a/labcore/data/synthesizer.py b/labcore/data/synthesizer.py new file mode 100644 index 0000000..3dc8b59 --- /dev/null +++ b/labcore/data/synthesizer.py @@ -0,0 +1,16 @@ + +import numpy as np + +from abc import ABC, abstractmethod + + +class Synthesizer(ABC): + @abstractmethod + def model(self, coordinates, *args, **kwargs): + pass + + def generate(self, coordinates): + return self.model(coordinates) + self.noise() + + def noise(self, std = 1.0): + return np.random.normal(scale = std) \ No newline at end of file From 83fbf38abf4e89d2ee561ff4f548ae80b014909e Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Wed, 25 Feb 2026 19:34:56 -0600 Subject: [PATCH 2/5] fixed parameter issues/multiple datasets being passed. added exponential, sine, and gaussian class implementations --- labcore/data/synthesizer.py | 56 ++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/labcore/data/synthesizer.py b/labcore/data/synthesizer.py index 3dc8b59..46727c1 100644 --- a/labcore/data/synthesizer.py +++ b/labcore/data/synthesizer.py @@ -3,14 +3,62 @@ from abc import ABC, abstractmethod +""" + +Implementation would work like this given x (n-dimensional array): + +sine() = SineSynthesizer() +model = sine.generate(x, noise_std = 0.5, A = 2, f = 3) + +------------- +These two lines would create a synthesizer representing a sine wave and generate models +for each set of data stored in x. + +Std for Gaussian distribution passed to noise() is 0.5 --> maybe add way to have multiple + distributions for different data sets? + +A, f are passed to SineSynthesizer's model() as kwargs + +Synthesizer's generate() then applies the noise to the model and returns an array with +the same dimension that was passed + +""" + class Synthesizer(ABC): @abstractmethod def model(self, coordinates, *args, **kwargs): pass - def generate(self, coordinates): - return self.model(coordinates) + self.noise() + def generate(self, coordinates, noise_std = 1.0, **model_kwargs): + + one_d = coordinates.ndim == 1 + coordinates = np.atleast_2d(coordinates) + model_outputs = np.array([self.model(coords, **model_kwargs) + self.noise(noise_std) + for coords in coordinates]) + if (one_d): + return model_outputs.squeeze() + + return model_outputs - def noise(self, std = 1.0): - return np.random.normal(scale = std) \ No newline at end of file + def noise(self, std): + return np.random.normal(scale = std) + + +class ExponentialSynthesizer(Synthesizer): + + def model(self, coordinates, base = np.e): + return base ** coordinates + + + +class SineSynthesizer(Synthesizer): + + def model(self, coordinates, A = 1, f = 1, phi = 0, of = 0): + return A * np.sin(2 * np.pi * coordinates * f + phi) + of + + +class GaussianSynthesizer(Synthesizer): + + def model(self, coordinates, x0 = 0, sigma = 1, A = 1, of = 0): + return A * np.exp(-((coordinates - x0) ** 2) / (2 * sigma ** 2)) + of \ No newline at end of file From f4d11a059ad71ad70f0856fb56bf7392b0134c17 Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Sat, 28 Feb 2026 01:47:14 -0600 Subject: [PATCH 3/5] Refactor DataGen classes from ABC to dataclasses; model() and noise() are now static methods. Parameters can be set at instantiation or overridden in generate(). --- labcore/data/datagen.py | 103 ++++++++++++++++++++++++++++++++++++ labcore/data/synthesizer.py | 64 ---------------------- 2 files changed, 103 insertions(+), 64 deletions(-) create mode 100644 labcore/data/datagen.py delete mode 100644 labcore/data/synthesizer.py diff --git a/labcore/data/datagen.py b/labcore/data/datagen.py new file mode 100644 index 0000000..35f76a0 --- /dev/null +++ b/labcore/data/datagen.py @@ -0,0 +1,103 @@ + +import numpy as np + +import dataclasses + +""" + +Implementation would work like this given x (n-dimensional array): + +sine() = SineDataGen() +model = sine.generate(x, noise_std = 0.5, A = 2, f = 3) + +------------- +These two lines would create a synthesizer representing a sine wave and generate models +for each set of data stored in x. + +Std for Gaussian distribution passed to noise() is 0.5 --> maybe add way to have multiple + distributions for different data sets? + +A, f are passed to SineDataGen's model() as kwargs + +DataGen's generate() then applies the noise to the model and returns an array with +the same dimension that was passed + +""" + +""" +generate should now work such that the following can be done: + +x = [np array of coordinates] +sine = SineDataGen(A = 2, f = 3) + +coords = sine.generate() --> uses A = 2, f = 3 +coords = sine.generate(A = 5) --> uses A = 5, f = 2 + +""" + +@dataclasses.dataclass +class DataGen: + noise_std : float = 1.0 + + @staticmethod + def model(coordinates, *args, **kwargs): + pass + + def generate(self, coordinates, **kwargs): + + # updates previously set dataclass fields + # coords = + params = dataclasses.asdict(self) + params.update(kwargs) + noise_std = params.pop('noise_std') + + one_d = coordinates.ndim == 1 + coordinates = np.atleast_2d(coordinates) + model_outputs = np.array([self.model(coords, **params) + + self.noise(coords, noise_std) + for coords in coordinates]) + if (one_d): + return model_outputs.squeeze() + + return model_outputs + + @staticmethod + def noise(coordinates, std): + return np.random.normal(scale = std, size = len(coordinates)) + + +@dataclasses.dataclass +class ExponentialDataGen(DataGen): + + base: float = np.e + + @staticmethod + def model(coordinates, base = np.e): + return base ** coordinates + + + +@dataclasses.dataclass +class SineDataGen(DataGen): + + A : float = 1 + f : float = 1 + phi : float = 0 + of : float = 0 + + @staticmethod + def model(coordinates, A = 1, f = 1, phi = 0, of = 0): + return A * np.sin(2 * np.pi * coordinates * f + phi) + of + + +@dataclasses.dataclass +class GaussianDataGen(DataGen): + + x0 : float = 0 + sigma : float = 1 + A : float = 1 + of : float = 0 + + @staticmethod + def model(coordinates, x0 = 0, sigma = 1, A = 1, of = 0): + return A * np.exp(-((coordinates - x0) ** 2) / (2 * sigma ** 2)) + of \ No newline at end of file diff --git a/labcore/data/synthesizer.py b/labcore/data/synthesizer.py deleted file mode 100644 index 46727c1..0000000 --- a/labcore/data/synthesizer.py +++ /dev/null @@ -1,64 +0,0 @@ - -import numpy as np - -from abc import ABC, abstractmethod - -""" - -Implementation would work like this given x (n-dimensional array): - -sine() = SineSynthesizer() -model = sine.generate(x, noise_std = 0.5, A = 2, f = 3) - -------------- -These two lines would create a synthesizer representing a sine wave and generate models -for each set of data stored in x. - -Std for Gaussian distribution passed to noise() is 0.5 --> maybe add way to have multiple - distributions for different data sets? - -A, f are passed to SineSynthesizer's model() as kwargs - -Synthesizer's generate() then applies the noise to the model and returns an array with -the same dimension that was passed - -""" - - -class Synthesizer(ABC): - @abstractmethod - def model(self, coordinates, *args, **kwargs): - pass - - def generate(self, coordinates, noise_std = 1.0, **model_kwargs): - - one_d = coordinates.ndim == 1 - coordinates = np.atleast_2d(coordinates) - model_outputs = np.array([self.model(coords, **model_kwargs) + self.noise(noise_std) - for coords in coordinates]) - if (one_d): - return model_outputs.squeeze() - - return model_outputs - - def noise(self, std): - return np.random.normal(scale = std) - - -class ExponentialSynthesizer(Synthesizer): - - def model(self, coordinates, base = np.e): - return base ** coordinates - - - -class SineSynthesizer(Synthesizer): - - def model(self, coordinates, A = 1, f = 1, phi = 0, of = 0): - return A * np.sin(2 * np.pi * coordinates * f + phi) + of - - -class GaussianSynthesizer(Synthesizer): - - def model(self, coordinates, x0 = 0, sigma = 1, A = 1, of = 0): - return A * np.exp(-((coordinates - x0) ** 2) / (2 * sigma ** 2)) + of \ No newline at end of file From 73999136165c02e4322713095fb7b47f7f5eb991 Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Tue, 3 Mar 2026 23:53:39 -0600 Subject: [PATCH 4/5] switch datagen back to abstract and removed default values from child class model() signatures --- labcore/data/datagen.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/labcore/data/datagen.py b/labcore/data/datagen.py index 35f76a0..27f3717 100644 --- a/labcore/data/datagen.py +++ b/labcore/data/datagen.py @@ -2,6 +2,7 @@ import numpy as np import dataclasses +from abc import ABC, abstractmethod """ @@ -36,17 +37,16 @@ """ @dataclasses.dataclass -class DataGen: +class DataGen(ABC): noise_std : float = 1.0 - @staticmethod + @abstractmethod def model(coordinates, *args, **kwargs): pass - def generate(self, coordinates, **kwargs): - + def generate(self, coordinates, **kwargs): + # updates previously set dataclass fields - # coords = params = dataclasses.asdict(self) params.update(kwargs) noise_std = params.pop('noise_std') @@ -72,7 +72,7 @@ class ExponentialDataGen(DataGen): base: float = np.e @staticmethod - def model(coordinates, base = np.e): + def model(coordinates, base): return base ** coordinates @@ -86,7 +86,7 @@ class SineDataGen(DataGen): of : float = 0 @staticmethod - def model(coordinates, A = 1, f = 1, phi = 0, of = 0): + def model(coordinates, A, f, phi, of): return A * np.sin(2 * np.pi * coordinates * f + phi) + of @@ -99,5 +99,5 @@ class GaussianDataGen(DataGen): of : float = 0 @staticmethod - def model(coordinates, x0 = 0, sigma = 1, A = 1, of = 0): + def model(coordinates, x0, sigma, A, of): return A * np.exp(-((coordinates - x0) ** 2) / (2 * sigma ** 2)) + of \ No newline at end of file From 457df29ebb078ad8cf7b959a22b23c7dbc0088b1 Mon Sep 17 00:00:00 2001 From: olivers3uiuc Date: Thu, 5 Mar 2026 15:22:14 -0600 Subject: [PATCH 5/5] changed dataclass import :) --- labcore/data/datagen.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/labcore/data/datagen.py b/labcore/data/datagen.py index 27f3717..4e3dd79 100644 --- a/labcore/data/datagen.py +++ b/labcore/data/datagen.py @@ -1,7 +1,7 @@ import numpy as np -import dataclasses +from dataclasses import dataclass, asdict from abc import ABC, abstractmethod """ @@ -36,7 +36,7 @@ """ -@dataclasses.dataclass +@dataclass class DataGen(ABC): noise_std : float = 1.0 @@ -47,7 +47,7 @@ def model(coordinates, *args, **kwargs): def generate(self, coordinates, **kwargs): # updates previously set dataclass fields - params = dataclasses.asdict(self) + params = asdict(self) params.update(kwargs) noise_std = params.pop('noise_std') @@ -66,7 +66,7 @@ def noise(coordinates, std): return np.random.normal(scale = std, size = len(coordinates)) -@dataclasses.dataclass +@dataclass class ExponentialDataGen(DataGen): base: float = np.e @@ -77,7 +77,7 @@ def model(coordinates, base): -@dataclasses.dataclass +@dataclass class SineDataGen(DataGen): A : float = 1 @@ -90,7 +90,7 @@ def model(coordinates, A, f, phi, of): return A * np.sin(2 * np.pi * coordinates * f + phi) + of -@dataclasses.dataclass +@dataclass class GaussianDataGen(DataGen): x0 : float = 0