Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linalg refactor #22

Merged
merged 20 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c9801dd
Merge in changes from sciml branch not directly associated to sciml m…
jdjakem May 21, 2024
8c50851
change environment.yml to allow only for python 3.8 and above
jdjakem May 21, 2024
fbb0773
update ci file to create docs to use setup-miniconda@v3
jdjakem May 21, 2024
fafad31
Fix error with torchklewrapper
jdjakem May 21, 2024
d896cbd
Change autogp kernels to accept linear algebra Mixin classes that all…
jdjakem May 22, 2024
a21c58f
Use LinalgMixins to create torch and numpy KLEs
jdjakem May 23, 2024
3505920
Fix a few small bugs picked up by CI that I am not sure exactly were …
jdjakem May 23, 2024
9599bc6
Merge in devel into branch before pushing to devel
jdjakem May 23, 2024
105beb9
Try to remove need for latex when running plot_bayesian_oed.py
jdjakem May 23, 2024
4632b4f
Fix issues in tutorial scripts.
jdjakem May 23, 2024
dad5dee
Try to fix tolerances to account for across platform differences
jdjakem May 23, 2024
ca487dc
Fix issues in yml files
jdjakem May 23, 2024
d039395
Merge branch 'devel' into linalg-refactor
jdjakem May 23, 2024
55af5fb
Merge remote-tracking branch 'origin/devel' into linalg-refactor
jdjakem May 23, 2024
af48732
Try to fix test tolerances to account for cross platform differences
jdjakem May 24, 2024
fdfaf12
Fix tolerance to account for cross platform differences in multioutpu…
jdjakem May 24, 2024
2a2e8d7
Again trying to fix cross platform diffs
jdjakem May 24, 2024
2e04a99
Add continuous worfklow to build docs purely with pip
jdjakem May 26, 2024
a3e263e
python3.8 is near end of life so stop running CI using 3.8.
jdjakem May 27, 2024
3516b12
Avoid broken setuptools for somer versions of python when building do…
jdjakem May 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

11 changes: 1 addition & 10 deletions .github/workflows/continuous-integration-workflow-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,9 @@ jobs:
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest]
# pin to python-3.7.16 because github actions has a bug with _bz2 on
# ubunutu for 3.7.17
# quotes needed around two-digit versions
python-version: [3.8, 3.9, '3.10', '3.11']
python-version: [3.9, '3.10', '3.11']
os: [ubuntu-latest, macos-latest]
# python-version: [3.7, 3.8] #3.8 currently fails due to numpy error
# solely experienced when using github actions ValueError:
# numpy.ndarray size changed, may indicate binary incompatibility.
# Expected 96 from C header, got 88 from PyObject

steps:
- uses: actions/checkout@v4
Expand All @@ -40,11 +33,9 @@ jobs:
with:
activate-environment: pyapprox-base
python-version: ${{ matrix.python-version }}
# channels: defaults,conda-forge
channels: defaults
environment-file: environment.yml
auto-update-conda: true
# use-only-tar-bz2: true
auto-activate-base: false
- name: Conda list
shell: bash -l {0} # - l {0} is needed to activate created env
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/continuous-integration-workflow-docs-pip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Build Docs Using Pip

on:
# push:
# branches: [master]
# branches: [master, devel]
pull_request:
branches: [devel]

workflow_dispatch:


# # schedule:
# # # * is a special character in YAML so you have to quote this string
# # - cron: '*/0 * * * *' # run once a day


jobs:
pyapprox_unit_tests:
name: Build docs with pip-build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest]
python-version: [3.9, '3.10', '3.11']
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Setup Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Setup PyApprox Documentation
shell: bash -l {0}
run: |
pip install -e .[docs]
- name: Create PyApprox Documentation
shell: bash -l {0}
run: |
cd docs
make html SPHINXOPTS=-vvv
10 changes: 1 addition & 9 deletions .github/workflows/continuous-integration-workflow-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,17 @@ jobs:
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest]
python-version: [3.8, 3.9, '3.10', '3.11']
python-version: [3.9, '3.10', '3.11']
os: [ubuntu-latest, macos-latest]
# python-version: [3.7, 3.8] #3.8 currently fails due to numpy error
# solely experienced when using github actions ValueError:
# numpy.ndarray size changed, may indicate binary incompatibility.
# Expected 96 from C header, got 88 from PyObject

steps:
- uses: actions/checkout@v4
- name: Setup Miniconda with Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: pyapprox-base
python-version: ${{ matrix.python-version }}
# channels: defaults,conda-forge
channels: defaults
environment-file: environment.yml
# use-only-tar-bz2: true
auto-update-conda: true
auto-activate-base: false
- name: Conda list
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration-workflow-pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest]
python-version: [3.8, 3.9, '3.10', '3.11']
python-version: [3.9, '3.10', '3.11']
# exclude:
# # stalls on github actions
# - os: ubuntu-latest
Expand Down
55 changes: 9 additions & 46 deletions pyapprox/benchmarks/pde_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
)
from pyapprox.variables import IndependentMarginalsVariable
from pyapprox.variables.transforms import ConfigureVariableTransformation
from pyapprox.pde.karhunen_loeve_expansion import MeshKLE, TorchKLEWrapper
from pyapprox.pde.kle.torchkle import TorchMeshKLE, TorchInterpolatedMeshKLE
from pyapprox.interface.wrappers import (
evaluate_1darray_function_on_2d_array, MultiIndexModel, ModelEnsemble)

Expand Down Expand Up @@ -96,12 +96,12 @@ def loglike_functional_dqdp(obs, obs_indices, noise_std, sol, params):
def raw_advection_diffusion_reaction_kle_dRdp(kle, residual, sol, param_vals):
mesh = residual.mesh
dmats = [residual.mesh._dmat(dd) for dd in range(mesh.nphys_vars)]
if kle.use_log:
if kle._use_log:
# compute gradient of diffusivity with respect to KLE coeff
assert param_vals.ndim == 1
kle_vals = kle(param_vals[:, None])
assert kle_vals.ndim == 2
dkdp = kle_vals*kle.eig_vecs
dkdp = kle_vals*kle._eig_vecs
else:
dkdp = kle.eig_vecs
Du = [torch.linalg.multi_dot((dmats[dd], sol))
Expand All @@ -123,9 +123,9 @@ def advection_diffusion_reaction_kle_dRdp(
elif bndry_cond[1] == "R":
mesh_pts_idx = mesh._bndry_slice(mesh.mesh_pts, idx, 1)
normal_vals = mesh._bndrys[ii].normals(mesh_pts_idx)
if kle.use_log:
if kle._use_log:
kle_vals = kle(param_vals[:, None])
dkdp = kle_vals*kle.eig_vecs
dkdp = kle_vals*kle._eig_vecs
else:
dkdp = torch.as_tensor(kle.eig_vecs)
flux_vals = [
Expand Down Expand Up @@ -206,7 +206,7 @@ def _fast_interpolate(self, values, xx):
def _set_random_sample(self, sample):
self._fwd_solver.physics._diff_fun = partial(
self._fast_interpolate,
self._kle(sample[:, None]))
self._kle(self._kle._la_atleast2d(sample[:, None])))

def _eval(self, sample, return_grad=False):
sample_copy = torch.as_tensor(sample.copy(), dtype=torch.double)
Expand Down Expand Up @@ -341,12 +341,12 @@ def _setup_advection_diffusion_benchmark(
vel_fun = partial(constant_vel_fun, vel_vec)

if kle_args is None:
npkle = MeshKLE(
kle = TorchMeshKLE(
mesh.mesh_pts, length_scale, sigma=sigma, nterms=nvars,
use_log=True, mean_field=kle_mean_field)
kle = TorchKLEWrapper(npkle)
# kle = TorchKLEWrapper(npkle)
else:
kle = InterpolatedMeshKLE(kle_args[0], kle_args[1], mesh)
kle = TorchInterpolatedMeshKLE(kle_args[0], kle_args[1], mesh)

if time_scenario is None:
forc_fun = partial(gauss_forc_fun, amp, scale, loc)
Expand All @@ -371,43 +371,6 @@ def _setup_advection_diffusion_benchmark(
return model, variable


class InterpolatedMeshKLE(MeshKLE):
def __init__(self, kle_mesh, kle, mesh):
self._kle_mesh = kle_mesh
self._kle = kle
self._mesh = mesh

self.matern_nu = self._kle._matern_nu
self.nterms = self._kle._nterms
self.lenscale = self._kle._lenscale

self._basis_mat = self._kle_mesh._get_lagrange_basis_mat(
self._kle_mesh._canonical_mesh_pts_1d,
mesh._map_samples_to_canonical_domain(self._mesh.mesh_pts))

def _fast_interpolate(self, values, xx):
assert xx.shape[1] == self._mesh.mesh_pts.shape[1]
assert np.allclose(xx, self._mesh.mesh_pts)
interp_vals = torch.linalg.multi_dot((self._basis_mat, values))
# assert np.allclose(
# interp_vals, self._kle_mesh.interpolate(values, xx))
return interp_vals

def __call__(self, coef):
assert isinstance(self._kle, TorchKLEWrapper)
# use_log = self._kle._use_log
use_log = self._kle._kle._use_log
self._kle._kle._use_log = False
vals = self._kle(coef)
interp_vals = self._fast_interpolate(vals, self._mesh.mesh_pts)
mean_field = self._fast_interpolate(
torch.as_tensor(self._kle._mean_field[:, None], dtype=torch.double),
self._mesh.mesh_pts)
if use_log:
interp_vals = torch.exp(mean_field+interp_vals)
self._kle._kle._use_log = use_log
return interp_vals


def _setup_inverse_advection_diffusion_benchmark(
amp, scale, loc, nobs, noise_std, length_scale, sigma, nvars, orders,
Expand Down
2 changes: 1 addition & 1 deletion pyapprox/benchmarks/tests/test_pde_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def test_setup_transient_multi_index_advection_diffusion_benchmark(self):
# plt.loglog(
# ndof[:-1], np.abs((qoi_means[-1]-qoi_means[:-1])/qoi_means[-1]))
# plt.show()
assert (rel_diffs.max() > 4e-2 and rel_diffs.min() < 9.5e-5)
assert (rel_diffs.max() > 4e-2 and rel_diffs.min() < 1e-4)


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion pyapprox/expdesign/tests/test_linear_oed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ def test_michaelis_menten_model_minimax_d_optimal_least_squares_design(
opt_problem = NonLinearAlphabetOptimalDesign('D', local_design_factors)
mu = opt_problem.solve_nonlinear_minimax(
parameter_samples, design_samples[np.newaxis, :],
{'iprint': 1, 'ftol': 1e-8})
{'iprint': 1, 'ftol': 1e-4, 'disp': True})
II = np.where(mu > 1e-5)[0]
# given largest theta_2=1 then optimal design will be at 1/3,1
# with masses=0.5
Expand Down
3 changes: 2 additions & 1 deletion pyapprox/expdesign/tests/test_optbayes.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def _check_classical_KL_OED_gaussian_optimization(
x0 = np.full((nobs, 1), nfinal_obs/nobs)
errors = objective.check_apply_jacobian(
x0, disp=True, fd_eps=np.logspace(-13, np.log(0.2), 13)[::-1])
assert errors.min()/errors.max() < 6e-6, errors.min()/errors.max()
assert errors.min()/errors.max() < 7e-6, errors.min()/errors.max()
# turn on hessian for testing hessian implementation, but
# apply hessian is turned off because while it reduces
# optimization iteration count but increases
Expand Down Expand Up @@ -371,6 +371,7 @@ def _check_prediction_gaussian_OED(
result = optimizer.minimize(x0)
print(result.x)

@unittest.skip("Implementation not finished")
def test_prediction_gaussian_OED(self):
test_cases = [
[3, 0, 1, 4000, 50, NoiseStatistic(SampleAverageMean())],
Expand Down
12 changes: 11 additions & 1 deletion pyapprox/interface/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ def __call__(self, samples):

class ModelFromCallable(SingleSampleModel):
def __init__(self, function, jacobian=None, apply_jacobian=None,
apply_hessian=None, hessian=None, sample_ndim=2, values_ndim=2):
apply_hessian=None, hessian=None, sample_ndim=2,
values_ndim=2):
"""
Parameters
----------
Expand Down Expand Up @@ -662,8 +663,17 @@ def __init__(self, function, nvars, inactive_var_values,
assert np.all(self._active_var_indices < self._nvars)
self._inactive_var_indices = np.delete(
np.arange(self._nvars), active_var_indices)
if base_model is None:
base_model = function
self._base_model = base_model

self._jacobian_implemented = self._base_model._jacobian_implemented
self._apply_jacobian_implemented = (
self._base_model._apply_jacobian_implemented)
self._hessian_implemented = self._base_model._hessian_implemented
self._apply_hessian_implemented = (
self._base_model._apply_hessian_implemented)

@staticmethod
def _expand_samples_from_indices(reduced_samples, active_var_indices,
inactive_var_indices,
Expand Down
21 changes: 5 additions & 16 deletions pyapprox/interface/tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def test_scalar_model_from_callable_2D_sample(self):
model = ModelFromCallable(
lambda sample: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_fun, "numpy"), sample),
lambda sample, vec: self._evaluate_sp_lambda(
apply_jacobian=lambda sample, vec: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_grad, "numpy"), sample) @ vec,
lambda sample, vec: self._evaluate_sp_lambda(
apply_hessian=lambda sample, vec: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_hessian), sample) @ vec)
sample = np.random.uniform(0, 1, (nvars, 1))
model.check_apply_jacobian(sample, disp=True)
Expand Down Expand Up @@ -72,8 +72,6 @@ def test_scalar_model_from_callable_1D_sample(self):
errors = model.check_apply_hessian(sample)
assert errors[0] < 1e-15



def test_vector_model_from_callable(self):
symbs = sp.symbols(["x", "y", "z"])
nvars = len(symbs)
Expand All @@ -83,7 +81,7 @@ def test_vector_model_from_callable(self):
model = ModelFromCallable(
lambda sample: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_fun, "numpy"), sample),
lambda sample, vec: self._evaluate_sp_lambda(
apply_jacobian=lambda sample, vec: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_grad, "numpy"), sample) @ vec)
sample = np.random.uniform(0, 1, (nvars, 1))
model.check_apply_jacobian(sample, disp=True)
Expand All @@ -102,9 +100,9 @@ def test_scipy_wrapper(self):
model = ModelFromCallable(
lambda sample: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_fun, "numpy"), sample),
lambda sample, vec: self._evaluate_sp_lambda(
apply_jacobian=lambda sample, vec: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_grad, "numpy"), sample) @ vec,
lambda sample, vec: self._evaluate_sp_lambda(
apply_hessian=lambda sample, vec: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_hessian), sample) @ vec)
scipy_model = ScipyModelWrapper(model)
# check scipy model works with 1D sample array
Expand All @@ -115,15 +113,6 @@ def test_scipy_wrapper(self):
assert np.allclose(scipy_model.hess(sample), self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_hessian, "numpy"), sample[:, None]))

# test error is thrown if scipy model does not return a scalar output
sp_fun = [sum([s*(ii+1) for ii, s in enumerate(symbs)])**4,
sum([s*(ii+1) for ii, s in enumerate(symbs)])**5]
model = ModelFromCallable(
lambda sample: self._evaluate_sp_lambda(
sp.lambdify(symbs, sp_fun, "numpy"), sample))
scipy_model = ScipyModelWrapper(model)
self.assertRaises(ValueError, scipy_model, sample)

def test_umbridge_model(self):
server_dir = os.path.dirname(__file__)
url = 'http://localhost:4242'
Expand Down
Loading
Loading